From 9efe0ee8f20d646801061a775754fea498d8db8a Mon Sep 17 00:00:00 2001 From: Vytautas Astrauskas Date: Wed, 14 Sep 2022 17:32:29 +0200 Subject: [PATCH] Ugly commit. --- .github/workflows/test.yml | 32 +- Cargo.lock | 66 +- prusti-common/src/vir/low_to_viper/ast.rs | 35 +- prusti-common/src/vir/low_to_viper/cfg.rs | 11 +- prusti-common/src/vir/low_to_viper/domain.rs | 36 +- .../vir/optimizations/folding/expressions.rs | 3 +- prusti-common/src/vir/program.rs | 4 +- prusti-common/src/vir/to_viper.rs | 2 +- .../prusti-contracts-proc-macros/src/lib.rs | 278 ++- .../prusti-contracts/src/core_spec.rs | 54 + prusti-contracts/prusti-contracts/src/lib.rs | 407 +++- prusti-contracts/prusti-specs/src/lib.rs | 349 +++- prusti-contracts/prusti-specs/src/rewriter.rs | 29 + prusti-interface/src/environment/body.rs | 94 +- .../src/environment/debug_utils/to_text.rs | 4 +- .../src/environment/mir_storage.rs | 3 +- prusti-interface/src/environment/mod.rs | 12 +- prusti-interface/src/lib.rs | 1 + prusti-interface/src/specs/mod.rs | 68 +- prusti-interface/src/specs/typed.rs | 25 +- prusti-server/src/process_verification.rs | 23 +- prusti-server/src/verification_request.rs | 20 +- .../core_proof/custom_heap_encoding/simple.rs | 17 + .../verify_overflow/fail/core_proof/forall.rs | 29 + .../fail/core_proof/framing.rs | 408 ++++ .../fail/core_proof/framing/functions.rs | 47 + .../fail/core_proof/framing/simple.rs | 244 +++ .../fail/core_proof/invariants.rs | 556 +++++ .../fail/core_proof/invariants2.rs | 38 + .../fail/core_proof/lifetimes/simple.rs | 38 + .../fail/core_proof/pointers.rs | 20 +- .../custom_heap_encoding/performance_test.rs | 972 +++++++++ .../pass/core_proof/pointers.rs | 8 +- .../verify_overflow/pass/core_proof/types.rs | 205 +- prusti-utils/src/config.rs | 105 +- prusti-viper/Cargo.toml | 1 + .../counterexample_translation.rs | 3 +- .../src/encoder/counterexamples/interface.rs | 33 +- .../src/encoder/counterexamples/mapping.rs | 3 +- prusti-viper/src/encoder/encoder.rs | 58 +- .../src/encoder/errors/error_manager.rs | 84 +- .../src/encoder/foldunfold/requirements.rs | 4 +- .../encoder/high/builtin_functions/encoder.rs | 4 +- .../src/encoder/high/lower/expression.rs | 12 + prusti-viper/src/encoder/high/lower/ty.rs | 3 + .../encoder/high/procedures/inference/mod.rs | 1 + .../high/procedures/inference/permission.rs | 10 + .../high/procedures/inference/semantics.rs | 356 +++- .../high/procedures/inference/state/places.rs | 3 +- .../state/predicate_state_on_path.rs | 30 +- .../inference/unfolding_expressions.rs | 194 ++ .../procedures/inference/visitor/context.rs | 28 +- .../high/procedures/inference/visitor/mod.rs | 147 +- .../src/encoder/high/procedures/interface.rs | 30 +- .../encoder/high/pure_functions/interface.rs | 3 +- .../src/encoder/high/to_typed/expression.rs | 14 +- .../src/encoder/high/to_typed/type_decl.rs | 9 + .../encoder/high/to_typed/types/interface.rs | 11 +- prusti-viper/src/encoder/high/types/fields.rs | 2 + .../src/encoder/high/types/interface.rs | 4 +- .../middle/core_proof/addresses/encoder.rs | 73 +- .../middle/core_proof/addresses/interface.rs | 177 +- .../middle/core_proof/addresses/mod.rs | 3 +- .../middle/core_proof/addresses/state.rs | 4 + .../middle/core_proof/adts/interface.rs | 69 +- .../core_proof/block_markers/interface.rs | 13 +- .../builtin_methods/assertion_encoder.rs | 462 +++++ .../builders/decls/change_unique_ref_place.rs | 71 +- .../builtin_methods/builders/decls/common.rs | 9 +- .../builders/decls/copy_place.rs | 48 +- .../builders/decls/duplicate_frac_ref.rs | 58 +- .../builders/decls/memory_block_into.rs | 76 +- .../builders/decls/memory_block_range_join.rs | 377 ++++ .../decls/memory_block_range_split.rs | 189 ++ .../memory_block_range_split_join_common.rs | 256 +++ .../decls/memory_block_split_join_common.rs | 5 +- .../builtin_methods/builders/decls/mod.rs | 4 + .../builders/decls/move_copy_place_common.rs | 162 +- .../builders/decls/move_place.rs | 134 +- .../builders/decls/restore_raw_borrowed.rs | 127 ++ .../builders/decls/write_place_constant.rs | 81 +- .../builtin_methods/builders/mod.rs | 3 + .../builtin_methods/calls/interface.rs | 60 +- .../core_proof/builtin_methods/interface.rs | 1402 +++++++++++-- .../middle/core_proof/builtin_methods/mod.rs | 1 + .../core_proof/compute_address/interface.rs | 374 ++-- .../middle/core_proof/footprint/interface.rs | 341 +++ .../middle/core_proof/footprint/mod.rs | 3 + .../middle/core_proof/heap/interface.rs | 164 ++ .../src/encoder/middle/core_proof/heap/mod.rs | 4 + .../encoder/middle/core_proof/heap/state.rs | 4 + .../encoder/middle/core_proof/interface.rs | 127 +- .../encoder/middle/core_proof/into_low/cfg.rs | 855 ++++++-- .../middle/core_proof/labels/interface.rs | 22 + .../encoder/middle/core_proof/labels/mod.rs | 4 + .../encoder/middle/core_proof/labels/state.rs | 14 + .../middle/core_proof/lifetimes/interface.rs | 5 +- .../core_proof/lowerer/domains/interface.rs | 96 +- .../middle/core_proof/lowerer/domains/mod.rs | 2 +- .../core_proof/lowerer/functions/interface.rs | 28 +- .../encoder/middle/core_proof/lowerer/mod.rs | 194 +- .../core_proof/lowerer/variables/interface.rs | 9 + .../src/encoder/middle/core_proof/mod.rs | 5 + .../middle/core_proof/places/encoder.rs | 23 +- .../middle/core_proof/places/interface.rs | 82 +- .../middle/core_proof/pointers/interface.rs | 199 ++ .../encoder/middle/core_proof/pointers/mod.rs | 3 + .../predicates/aliasing/interface.rs | 75 + .../core_proof/predicates/aliasing/mod.rs | 4 + .../predicates/memory_block/interface.rs | 131 +- .../middle/core_proof/predicates/mod.rs | 19 +- .../owned/builders/common/function_decl.rs | 332 +++ .../owned/builders/common/function_use.rs | 80 + .../predicates/owned/builders/common/mod.rs | 2 + .../owned/builders/common/predicate_decl.rs | 273 ++- .../owned/builders/common/predicate_use.rs | 24 +- .../owned/builders/frac_ref/function_decl.rs | 318 +++ .../owned/builders/frac_ref/function_use.rs | 65 + .../predicates/owned/builders/frac_ref/mod.rs | 2 + .../owned/builders/frac_ref/predicate_decl.rs | 231 ++- .../owned/builders/frac_ref/predicate_use.rs | 58 +- .../predicates/owned/builders/mod.rs | 32 +- .../builders/owned_aliased/function_decl.rs | 550 +++++ .../owned_aliased/function_range_decl.rs | 202 ++ .../owned_aliased/function_range_use.rs | 96 + .../builders/owned_aliased/function_use.rs | 73 + .../owned/builders/owned_aliased/mod.rs | 7 + .../builders/owned_aliased/predicate_decl.rs | 309 +++ .../owned_aliased/predicate_range_use.rs | 112 + .../builders/owned_aliased/predicate_use.rs | 72 + .../owned_non_aliased/function_decl.rs | 662 ++++++ .../owned_non_aliased/function_use.rs | 74 + .../owned/builders/owned_non_aliased/mod.rs | 2 + .../owned_non_aliased/predicate_decl.rs | 570 ++++- .../owned_non_aliased/predicate_use.rs | 58 +- .../unique_ref/function_current_decl.rs | 477 +++++ .../unique_ref/function_current_use.rs | 65 + .../unique_ref/function_final_decl.rs | 233 +++ .../builders/unique_ref/function_final_use.rs | 81 + .../owned/builders/unique_ref/mod.rs | 4 + .../builders/unique_ref/predicate_decl.rs | 380 ++-- .../builders/unique_ref/predicate_use.rs | 65 +- .../core_proof/predicates/owned/encoder.rs | 633 +++++- .../core_proof/predicates/owned/interface.rs | 851 +++++++- .../middle/core_proof/predicates/owned/mod.rs | 7 +- .../predicates/restoration/interface.rs | 70 + .../core_proof/predicates/restoration/mod.rs | 6 + .../middle/core_proof/predicates/state.rs | 7 +- .../middle/core_proof/references/interface.rs | 69 +- .../core_proof/snapshots/adts/interface.rs | 32 +- .../middle/core_proof/snapshots/adts/mod.rs | 3 +- .../middle/core_proof/snapshots/adts/state.rs | 85 + .../core_proof/snapshots/bytes/interface.rs | 64 +- .../core_proof/snapshots/domains/interface.rs | 3 + .../into_snapshot/assertions/constructor.rs | 506 +++++ .../snapshots/into_snapshot/assertions/mod.rs | 31 + .../into_snapshot/assertions/pure_heap.rs | 1 + .../into_snapshot/assertions/self_framing.rs | 821 ++++++++ .../into_snapshot/assertions/snap.rs | 1 + .../into_snapshot/assertions/validity.rs | 183 ++ .../into_snapshot/builtin_methods/mod.rs | 47 +- .../snapshots/into_snapshot/common/mod.rs | 574 +++++- .../into_snapshot/context_independent/mod.rs | 47 +- .../into_snapshot/expressions/mod.rs | 10 + .../expressions/procedure_bodies.rs | 167 ++ .../into_snapshot/expressions/pure_framed.rs | 174 ++ .../core_proof/snapshots/into_snapshot/mod.rs | 44 +- .../snapshots/into_snapshot/procedure/mod.rs | 319 ++- .../into_snapshot/procedure/traits.rs | 37 +- .../snapshots/into_snapshot/pure/mod.rs | 138 +- .../snapshots/into_snapshot/pure/traits.rs | 34 +- .../utils/bound_variable_stack.rs | 35 + .../snapshots/into_snapshot/utils/mod.rs | 1 + .../middle/core_proof/snapshots/mod.rs | 7 +- .../middle/core_proof/snapshots/state.rs | 19 +- .../snapshots/validity/interface.rs | 47 +- .../core_proof/snapshots/values/interface.rs | 47 +- .../snapshots/variables/interface.rs | 570 +++-- .../core_proof/snapshots/variables/mod.rs | 1 - .../core_proof/snapshots/variables/state.rs | 27 +- .../heap_encoder/effects/mod.rs | 538 +++++ .../heap_encoder/heap/mod.rs | 150 ++ .../custom_heap_encoding/heap_encoder/mod.rs | 137 ++ .../heap_encoder/permission_mask/mod.rs | 318 +++ .../permission_mask/operations.rs | 169 ++ .../heap_encoder/predicates.rs | 165 ++ .../heap_encoder/pure_expressions.rs | 147 ++ .../heap_encoder/statements.rs | 73 + .../custom_heap_encoding/mod.rs | 110 + .../variable_declarations.rs | 26 + .../transformations/desugar_conditionals.rs | 92 + .../transformations/desugar_fold_unfold.rs | 275 +++ .../transformations/desugar_implications.rs | 147 ++ .../transformations/desugar_method_calls.rs | 120 ++ .../transformations/expand_quantifiers.rs | 114 + .../transformations/inline_functions.rs | 106 +- .../middle/core_proof/transformations/mod.rs | 7 + .../transformations/remove_predicates.rs | 13 +- .../remove_unvisited_blocks.rs | 4 +- .../symbolic_execution/consistency_tracker.rs | 148 ++ .../symbolic_execution/egg/graphviz.rs | 94 + .../symbolic_execution/egg/language.rs | 29 + .../symbolic_execution/egg/mod.rs | 492 +++++ .../symbolic_execution/egg/rule_applier.rs | 49 + .../symbolic_execution/egg/term_interner.rs | 370 ++++ .../symbolic_execution/heap/entry.rs | 41 + .../symbolic_execution/heap/finalizer.rs | 347 ++++ .../symbolic_execution/heap/graphviz.rs | 29 + .../heap/lifetime_tokens.rs | 157 ++ .../symbolic_execution/heap/mod.rs | 197 ++ .../heap/predicate_snapshots.rs | 416 ++++ .../symbolic_execution/heap/state.rs | 370 ++++ .../transformations/symbolic_execution/mod.rs | 365 ++++ .../symbolic_execution/program_context.rs | 196 ++ .../symbolic_execution/simplifier.rs | 181 ++ .../symbolic_execution/statements.rs | 381 ++++ .../symbolic_execution/trace.rs | 45 + .../trace_builder/heap_view.rs | 53 + .../symbolic_execution/trace_builder/mod.rs | 268 +++ .../trace_builder/original_view.rs | 35 + .../symbolic_execution/utils.rs | 53 + .../core_proof/type_layouts/interface.rs | 30 +- .../middle/core_proof/types/interface.rs | 531 ++++- .../core_proof/utils/place_domain_encoder.rs | 11 + .../middle/core_proof/viewshifts/interface.rs | 246 +++ .../middle/core_proof/viewshifts/mod.rs | 4 + .../middle/core_proof/viewshifts/state.rs | 12 + .../src/encoder/mir/contracts/contracts.rs | 7 +- .../src/encoder/mir/errors/interface.rs | 10 +- .../src/encoder/mir/places/interface.rs | 4 +- .../encoder/builtin_function_encoder.rs | 170 +- .../encoder/check_mode_converters.rs | 230 +++ .../procedures/encoder/elaborate_drops/mod.rs | 26 +- .../elaborate_drops/pointer_reborrow.rs | 65 + .../mir/procedures/encoder/initialisation.rs | 34 +- .../mir/procedures/encoder/lifetimes.rs | 214 +- .../encoder/mir/procedures/encoder/loops.rs | 3 +- .../src/encoder/mir/procedures/encoder/mod.rs | 1829 +++++++++++++++-- .../encoder/specification_blocks.rs | 10 +- .../mir/procedures/encoder/specifications.rs | 126 ++ .../mir/procedures/encoder/termination.rs | 11 +- .../encoder/mir/procedures/encoder/utils.rs | 15 + .../src/encoder/mir/procedures/interface.rs | 31 +- .../mir/procedures/passes/assertions.rs | 21 +- .../mir/procedures/passes/loop_desugaring.rs | 9 +- .../mir/pure/interpreter/interpreter_high.rs | 224 +- .../mir/pure/interpreter/state_high.rs | 7 +- .../mir/pure/pure_functions/cleaner.rs | 296 +++ .../mir/pure/pure_functions/encoder_high.rs | 9 +- .../encoder/mir/pure/pure_functions/mod.rs | 1 + .../mir/pure/specifications/encoder_high.rs | 102 +- .../mir/pure/specifications/interface.rs | 40 +- .../encoder/mir/specifications/interface.rs | 18 + .../src/encoder/mir/specifications/specs.rs | 10 +- prusti-viper/src/encoder/mir/types/encoder.rs | 174 +- .../src/encoder/mir/types/interface.rs | 112 +- .../src/encoder/mirror_function_encoder.rs | 2 +- prusti-viper/src/encoder/procedure_encoder.rs | 2 +- prusti-viper/src/encoder/purifier.rs | 4 +- prusti-viper/src/encoder/snapshot/encoder.rs | 6 +- .../src/encoder/typed/to_middle/expression.rs | 11 +- .../src/encoder/typed/to_middle/statement.rs | 53 + .../src/encoder/typed/to_middle/type_decl.rs | 7 + prusti-viper/src/verifier.rs | 2 +- prusti/src/callbacks.rs | 2 +- smt-log-analyzer/src/lib.rs | 12 +- smt-log-analyzer/src/parser.rs | 34 +- smt-log-analyzer/src/state.rs | 57 +- vir-gen/src/deriver/lower.rs | 14 + vir-gen/src/helpers.rs | 2 +- vir/defs/high/ast/expression.rs | 54 + vir/defs/high/ast/function.rs | 2 +- vir/defs/high/ast/mod.rs | 6 +- vir/defs/high/ast/predicate.rs | 35 + vir/defs/high/ast/rvalue.rs | 9 +- vir/defs/high/ast/statement.rs | 190 +- vir/defs/high/ast/ty.rs | 4 + vir/defs/high/ast/type_decl.rs | 3 + vir/defs/high/cfg/procedure.rs | 5 + vir/defs/high/mod.rs | 21 +- .../const_generics/common.rs | 9 + .../high/operations_internal/expression.rs | 479 ++++- vir/defs/high/operations_internal/graphviz.rs | 9 +- vir/defs/high/operations_internal/helpers.rs | 38 +- .../identifier/predicate.rs | 21 + .../operations_internal/identifier/rvalue.rs | 11 + .../high/operations_internal/identifier/ty.rs | 2 + .../operations_internal/lifetimes/common.rs | 9 + .../position/expressions.rs | 28 + .../high/operations_internal/position/mod.rs | 1 + .../operations_internal/position/statement.rs | 106 +- .../operations_internal/position/type_decl.rs | 33 + .../high/operations_internal/predicate.rs | 18 + .../operations_internal/special_variables.rs | 49 +- vir/defs/high/operations_internal/ty.rs | 78 +- .../high/operations_internal/type_decl.rs | 6 + vir/defs/low/ast/expression.rs | 15 +- vir/defs/low/ast/function.rs | 4 +- vir/defs/low/ast/predicate.rs | 14 +- vir/defs/low/ast/statement.rs | 8 + vir/defs/low/cfg/procedure.rs | 20 +- vir/defs/low/domain/mod.rs | 30 +- vir/defs/low/mod.rs | 15 +- vir/defs/middle/ast/rvalue.rs | 9 +- vir/defs/middle/ast/statement.rs | 156 +- vir/defs/middle/mod.rs | 8 +- .../operations_internal/position/mod.rs | 1 + .../operations_internal/position/statement.rs | 78 +- vir/defs/typed/ast/ty.rs | 4 + vir/defs/typed/ast/type_decl.rs | 3 + vir/defs/typed/mod.rs | 18 +- .../operations_internal/identifier/ty.rs | 2 + vir/defs/typed/operations_internal/mod.rs | 2 +- .../typed/operations_internal/position/mod.rs | 3 + .../operations_internal/position/type_decl.rs | 30 + vir/defs/typed/operations_internal/ty.rs | 92 +- .../typed/operations_internal/type_decl.rs | 9 +- vir/src/common/builtin_constants.rs | 5 + vir/src/common/check_mode.rs | 52 +- vir/src/common/display.rs | 12 +- vir/src/common/graphviz/helpers.rs | 19 + vir/src/common/graphviz/mod.rs | 2 +- vir/src/common/graphviz/writer.rs | 8 +- vir/src/common/mod.rs | 1 + vir/src/high/builders/procedure.rs | 42 +- vir/src/low/macros.rs | 2 + vir/src/low/mod.rs | 1 + vir/src/low/operations/domain.rs | 30 + vir/src/low/operations/expression.rs | 339 ++- vir/src/low/operations/function.rs | 7 + vir/src/low/operations/graphviz.rs | 52 +- vir/src/low/operations/helpers.rs | 52 + vir/src/low/operations/mod.rs | 4 + vir/src/low/operations/position/statement.rs | 9 +- vir/src/low/operations/predicate.rs | 7 + vir/src/low/operations/procedure.rs | 77 + vir/src/low/operations/special_variables.rs | 25 +- vir/src/low/operations/successor.rs | 10 + vir/src/low/operations/ty.rs | 16 + vir/src/low/ssa/mod.rs | 205 ++ x.py | 19 + 341 files changed, 34985 insertions(+), 2803 deletions(-) create mode 100644 prusti-tests/tests/verify_overflow/fail/core_proof/custom_heap_encoding/simple.rs create mode 100644 prusti-tests/tests/verify_overflow/fail/core_proof/forall.rs create mode 100644 prusti-tests/tests/verify_overflow/fail/core_proof/framing.rs create mode 100644 prusti-tests/tests/verify_overflow/fail/core_proof/framing/functions.rs create mode 100644 prusti-tests/tests/verify_overflow/fail/core_proof/framing/simple.rs create mode 100644 prusti-tests/tests/verify_overflow/fail/core_proof/invariants.rs create mode 100644 prusti-tests/tests/verify_overflow/fail/core_proof/invariants2.rs create mode 100644 prusti-tests/tests/verify_overflow/pass/core_proof/custom_heap_encoding/performance_test.rs create mode 100644 prusti-viper/src/encoder/high/procedures/inference/unfolding_expressions.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/addresses/state.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/builtin_methods/assertion_encoder.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_join.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_split.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_split_join_common.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/restore_raw_borrowed.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/footprint/interface.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/footprint/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/heap/interface.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/heap/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/heap/state.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/labels/interface.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/labels/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/labels/state.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/pointers/interface.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/pointers/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/aliasing/interface.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/aliasing/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/function_decl.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/function_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/function_decl.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/function_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_decl.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_range_decl.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_range_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_decl.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_range_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/function_decl.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/function_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_current_decl.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_current_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_final_decl.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_final_use.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/restoration/interface.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/predicates/restoration/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/adts/state.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/constructor.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/pure_heap.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/self_framing.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/snap.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/validity.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/procedure_bodies.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/pure_framed.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/utils/bound_variable_stack.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/utils/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/effects/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/heap/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/permission_mask/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/permission_mask/operations.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/predicates.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/pure_expressions.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/statements.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/variable_declarations.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/desugar_conditionals.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/desugar_fold_unfold.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/desugar_implications.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/desugar_method_calls.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/expand_quantifiers.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/consistency_tracker.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/graphviz.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/language.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/rule_applier.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/term_interner.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/entry.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/finalizer.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/graphviz.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/lifetime_tokens.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/predicate_snapshots.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/state.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/program_context.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/simplifier.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/statements.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/heap_view.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/original_view.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/utils.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/viewshifts/interface.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/viewshifts/mod.rs create mode 100644 prusti-viper/src/encoder/middle/core_proof/viewshifts/state.rs create mode 100644 prusti-viper/src/encoder/mir/procedures/encoder/check_mode_converters.rs create mode 100644 prusti-viper/src/encoder/mir/procedures/encoder/elaborate_drops/pointer_reborrow.rs create mode 100644 prusti-viper/src/encoder/mir/procedures/encoder/specifications.rs create mode 100644 prusti-viper/src/encoder/mir/procedures/encoder/utils.rs create mode 100644 prusti-viper/src/encoder/mir/pure/pure_functions/cleaner.rs create mode 100644 vir/defs/high/operations_internal/position/type_decl.rs create mode 100644 vir/defs/typed/operations_internal/position/mod.rs create mode 100644 vir/defs/typed/operations_internal/position/type_decl.rs create mode 100644 vir/src/common/builtin_constants.rs create mode 100644 vir/src/low/operations/domain.rs create mode 100644 vir/src/low/operations/function.rs create mode 100644 vir/src/low/operations/predicate.rs create mode 100644 vir/src/low/operations/procedure.rs create mode 100644 vir/src/low/ssa/mod.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index abf3e4e87ee..c7fec0a0fda 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -110,7 +110,7 @@ jobs: # Run a subset of the tests with the purification optimization enabled # to ensure that we do not introduce regressions. purification-tests: - needs: [fmt-check, clippy-check, check-deps, smir-check, quick-tests] + #needs: [fmt-check, clippy-check, check-deps, smir-check, quick-tests] runs-on: ubuntu-latest env: PRUSTI_ENABLE_PURIFICATION_OPTIMIZATION: true @@ -162,6 +162,36 @@ jobs: # python x.py test --all pass/pure-fn/ref-mut-arg.rs # python x.py test --all pass/rosetta/Ackermann_function.rs # python x.py test --all pass/rosetta/Heapsort.rs + - name: custom_heap_encoding + env: + PRUSTI_VIPER_BACKEND: carbon + PRUSTI_CUSTOM_HEAP_ENCODING: true + PRUSTI_TRACE_WITH_SYMBOLIC_EXECUTION: false + PRUSTI_PURIFY_WITH_SYMBOLIC_EXECUTION: false + run: | + python x.py test custom_heap_encoding + - name: purify_with_symbolic_execution + env: + PRUSTI_VIPER_BACKEND: carbon + PRUSTI_CUSTOM_HEAP_ENCODING: false + PRUSTI_PURIFY_WITH_SYMBOLIC_EXECUTION: true + run: | + python x.py test custom_heap_encoding + - name: custom_heap_encoding and purify_with_symbolic_execution + env: + PRUSTI_VIPER_BACKEND: carbon + PRUSTI_CUSTOM_HEAP_ENCODING: true + PRUSTI_PURIFY_WITH_SYMBOLIC_EXECUTION: true + run: | + python x.py test custom_heap_encoding + - name: trace_with_symbolic_execution + env: + PRUSTI_VIPER_BACKEND: silicon + PRUSTI_CUSTOM_HEAP_ENCODING: false + PRUSTI_TRACE_WITH_SYMBOLIC_EXECUTION: false + PRUSTI_PURIFY_WITH_SYMBOLIC_EXECUTION: false + run: | + python x.py test custom_heap_encoding - name: Run with purification. env: PRUSTI_VIPER_BACKEND: silicon diff --git a/Cargo.lock b/Cargo.lock index a625edefcd5..07295754dcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ version = "0.1.0" dependencies = [ "compiletest_rs", "derive_more", - "env_logger", + "env_logger 0.10.0", "glob", "log", "prusti-rustc-interface", @@ -882,6 +882,24 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +[[package]] +name = "egg" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6e969a475908119d4603393dfe05a17f676d9570c493c90763321aa950de2c" +dependencies = [ + "env_logger 0.9.3", + "fxhash", + "hashbrown", + "indexmap", + "instant", + "log", + "smallvec", + "symbol_table", + "symbolic_expressions", + "thiserror", +] + [[package]] name = "either" version = "1.8.0" @@ -897,6 +915,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "log", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -2087,7 +2114,7 @@ name = "prusti" version = "0.2.1" dependencies = [ "chrono", - "env_logger", + "env_logger 0.10.0", "lazy_static", "log", "prusti-common", @@ -2161,7 +2188,7 @@ version = "0.1.0" dependencies = [ "bincode", "clap", - "env_logger", + "env_logger 0.10.0", "lazy_static", "log", "num_cpus", @@ -2205,7 +2232,7 @@ version = "0.2.0" dependencies = [ "cargo-test-support", "compiletest_rs", - "env_logger", + "env_logger 0.10.0", "log", "prusti", "prusti-launch", @@ -2236,6 +2263,7 @@ dependencies = [ "backtrace", "derive_more", "diffy", + "egg", "itertools", "lazy_static", "log", @@ -2769,6 +2797,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "smt-log-analyzer" version = "0.1.0" @@ -2829,6 +2863,22 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "symbol_table" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32bf088d1d7df2b2b6711b06da3471bc86677383c57b27251e18c56df8deac14" +dependencies = [ + "ahash", + "hashbrown", +] + +[[package]] +name = "symbolic_expressions" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c68d531d83ec6c531150584c42a4290911964d5f0d79132b193b67252a23b71" + [[package]] name = "syn" version = "1.0.107" @@ -2856,7 +2906,7 @@ dependencies = [ name = "systest" version = "0.1.0" dependencies = [ - "env_logger", + "env_logger 0.10.0", "error-chain", "jni", "jni-gen", @@ -2917,7 +2967,7 @@ dependencies = [ "clap", "color-backtrace", "csv", - "env_logger", + "env_logger 0.10.0", "failure", "glob", "log", @@ -3262,7 +3312,7 @@ version = "0.1.0" dependencies = [ "bencher", "bincode", - "env_logger", + "env_logger 0.10.0", "error-chain", "futures", "jni", @@ -3280,7 +3330,7 @@ dependencies = [ name = "viper-sys" version = "0.1.0" dependencies = [ - "env_logger", + "env_logger 0.10.0", "error-chain", "jni", "jni-gen", diff --git a/prusti-common/src/vir/low_to_viper/ast.rs b/prusti-common/src/vir/low_to_viper/ast.rs index 6e56e887b01..f3092cde41e 100644 --- a/prusti-common/src/vir/low_to_viper/ast.rs +++ b/prusti-common/src/vir/low_to_viper/ast.rs @@ -46,6 +46,7 @@ impl<'v> ToViper<'v, viper::Stmt<'v>> for Statement { fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::Stmt<'v> { match self { Statement::Comment(statement) => statement.to_viper(context, ast), + Statement::Label(statement) => statement.to_viper(context, ast), Statement::LogEvent(statement) => statement.to_viper(context, ast), Statement::Assume(statement) => statement.to_viper(context, ast), Statement::Assert(statement) => statement.to_viper(context, ast), @@ -67,6 +68,12 @@ impl<'v> ToViper<'v, viper::Stmt<'v>> for statement::Comment { } } +impl<'v> ToViper<'v, viper::Stmt<'v>> for statement::Label { + fn to_viper(&self, _context: Context, ast: &AstFactory<'v>) -> viper::Stmt<'v> { + ast.label(&self.label, &[]) + } +} + impl<'v> ToViper<'v, viper::Stmt<'v>> for statement::LogEvent { fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::Stmt<'v> { assert!( @@ -135,6 +142,11 @@ impl<'v> ToViper<'v, viper::Stmt<'v>> for statement::Fold { !self.position.is_default(), "Statement with default position: {self}" ); + assert!( + self.expression.is_predicate_access_predicate(), + "fold {}", + self.expression + ); ast.fold_with_pos( self.expression.to_viper(context, ast), self.position.to_viper(context, ast), @@ -148,6 +160,11 @@ impl<'v> ToViper<'v, viper::Stmt<'v>> for statement::Unfold { !self.position.is_default(), "Statement with default position: {self}" ); + assert!( + self.expression.is_predicate_access_predicate(), + "unfold {}", + self.expression + ); ast.unfold_with_pos( self.expression.to_viper(context, ast), self.position.to_viper(context, ast), @@ -199,6 +216,10 @@ impl<'v> ToViper<'v, viper::Stmt<'v>> for statement::MethodCall { impl<'v> ToViper<'v, viper::Stmt<'v>> for statement::Assign { fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::Stmt<'v> { + assert!( + !self.position.is_default(), + "Statement with default position: {self}" + ); let target_expression = Expression::local(self.target.clone(), self.position); ast.abstract_assign( target_expression.to_viper(context, ast), @@ -225,7 +246,7 @@ impl<'v> ToViper<'v, viper::Expr<'v>> for Expression { Expression::MagicWand(expression) => expression.to_viper(context, ast), Expression::PredicateAccessPredicate(expression) => expression.to_viper(context, ast), // Expression::FieldAccessPredicate(expression) => expression.to_viper(context, ast), - // Expression::Unfolding(expression) => expression.to_viper(context, ast), + Expression::Unfolding(expression) => expression.to_viper(context, ast), Expression::UnaryOp(expression) => expression.to_viper(context, ast), Expression::BinaryOp(expression) => expression.to_viper(context, ast), Expression::PermBinaryOp(expression) => expression.to_viper(context, ast), @@ -248,7 +269,7 @@ impl<'v> ToViper<'v, viper::Expr<'v>> for Expression { impl<'v> ToViper<'v, viper::Expr<'v>> for expression::Local { fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::Expr<'v> { - if self.variable.name == "__result" { + if self.variable.is_result_variable() { ast.result_with_pos( self.variable.ty.to_viper(context, ast), self.position.to_viper(context, ast), @@ -351,6 +372,16 @@ impl<'v> ToViper<'v, viper::Expr<'v>> for expression::PredicateAccessPredicate { } } +impl<'v> ToViper<'v, viper::Expr<'v>> for expression::Unfolding { + fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::Expr<'v> { + ast.unfolding_with_pos( + self.predicate.to_viper(context, ast), + self.base.to_viper(context, ast), + self.position.to_viper(context, ast), + ) + } +} + impl<'v> ToViper<'v, viper::Expr<'v>> for expression::UnaryOp { fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::Expr<'v> { match self.op_kind { diff --git a/prusti-common/src/vir/low_to_viper/cfg.rs b/prusti-common/src/vir/low_to_viper/cfg.rs index 95a937d337c..fa3318c5426 100644 --- a/prusti-common/src/vir/low_to_viper/cfg.rs +++ b/prusti-common/src/vir/low_to_viper/cfg.rs @@ -15,14 +15,19 @@ impl<'a, 'v> ToViper<'v, viper::Method<'v>> for &'a ProcedureDecl { for local in &self.locals { declarations.push(local.to_viper_decl(context, ast).into()); } - for block in &self.basic_blocks { - declarations.push(block.label.to_viper_decl(context, ast).into()); - statements.push(block.label.to_viper(context, ast)); + let traversal_order = self.get_topological_sort(); + for label in &traversal_order { + let block = self.basic_blocks.get(label).unwrap(); + declarations.push(label.to_viper_decl(context, ast).into()); + statements.push(label.to_viper(context, ast)); statements.extend(block.statements.to_viper(context, ast)); statements.push(block.successor.to_viper(context, ast)); } statements.push(ast.label(RETURN_LABEL, &[])); declarations.push(ast.label(RETURN_LABEL, &[]).into()); + for label in &self.custom_labels { + declarations.push(label.to_viper_decl(context, ast).into()); + } let body = Some(ast.seqn(&statements, &declarations)); ast.method(&self.name, &[], &[], &[], &[], body) } diff --git a/prusti-common/src/vir/low_to_viper/domain.rs b/prusti-common/src/vir/low_to_viper/domain.rs index 6a3e4dee445..a81e0f1834e 100644 --- a/prusti-common/src/vir/low_to_viper/domain.rs +++ b/prusti-common/src/vir/low_to_viper/domain.rs @@ -1,13 +1,15 @@ use super::{Context, ToViper, ToViperDecl}; use viper::{self, AstFactory}; -use vir::low::{DomainAxiomDecl, DomainDecl, DomainFunctionDecl}; +use vir::low::{DomainAxiomDecl, DomainDecl, DomainFunctionDecl, DomainRewriteRuleDecl}; impl<'a, 'v> ToViper<'v, viper::Domain<'v>> for &'a DomainDecl { fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::Domain<'v> { + let mut axioms = (&self.name, &self.axioms).to_viper(context, ast); + axioms.extend((&self.name, &self.rewrite_rules).to_viper(context, ast)); ast.domain( &self.name, &(&self.name, &self.functions).to_viper(context, ast), - &(&self.name, &self.axioms).to_viper(context, ast), + &axioms, &[], ) } @@ -61,3 +63,33 @@ impl<'a, 'v> ToViper<'v, viper::NamedDomainAxiom<'v>> for (&'a String, &'a Domai } } } + +impl<'a, 'v> ToViper<'v, Vec>> + for (&'a String, &'a Vec) +{ + fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> Vec> { + self.1 + .iter() + .filter(|rule| !rule.egg_only) + .map(|axiom| (self.0, axiom).to_viper(context, ast)) + .collect() + } +} + +impl<'a, 'v> ToViper<'v, viper::NamedDomainAxiom<'v>> for (&'a String, &'a DomainRewriteRuleDecl) { + fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::NamedDomainAxiom<'v> { + let (domain_name, rewrite_rule) = self; + assert!(!rewrite_rule.egg_only); + let axiom = rewrite_rule.convert_into_axiom(); + if let Some(comment) = &axiom.comment { + ast.named_domain_axiom_with_comment( + &axiom.name, + axiom.body.to_viper(context, ast), + domain_name, + comment, + ) + } else { + ast.named_domain_axiom(&axiom.name, axiom.body.to_viper(context, ast), domain_name) + } + } +} diff --git a/prusti-common/src/vir/optimizations/folding/expressions.rs b/prusti-common/src/vir/optimizations/folding/expressions.rs index e432b90d894..591447379e9 100644 --- a/prusti-common/src/vir/optimizations/folding/expressions.rs +++ b/prusti-common/src/vir/optimizations/folding/expressions.rs @@ -19,6 +19,7 @@ use crate::vir::polymorphic_vir::{ast, cfg, FallibleExprFolder}; use log::{debug, trace}; use rustc_hash::{FxHashMap, FxHashSet}; +use vir::common::builtin_constants::DISCRIMINANT_FIELD_NAME; use std::{cmp::Ordering, mem}; pub trait FoldingOptimizer { @@ -178,7 +179,7 @@ fn check_requirements_conflict( ast::PlaceComponent::Variant(..), ast::PlaceComponent::Field(ast::Field { name, .. }, _), ) => { - if name == "discriminant" { + if name == DISCRIMINANT_FIELD_NAME { debug!("guarded permission: {} {}", place1, place2); // If we are checking discriminant, this means that the // permission is guarded. diff --git a/prusti-common/src/vir/program.rs b/prusti-common/src/vir/program.rs index c7bc8765128..ba369aca7bc 100644 --- a/prusti-common/src/vir/program.rs +++ b/prusti-common/src/vir/program.rs @@ -21,12 +21,14 @@ impl Program { } } pub fn get_check_mode(&self) -> vir::common::check_mode::CheckMode { + // FIXME: Remove because this is not needed anymore. match self { - Program::Legacy(_) => vir::common::check_mode::CheckMode::Both, + Program::Legacy(_) => vir::common::check_mode::CheckMode::MemorySafetyWithFunctional, Program::Low(program) => program.check_mode, } } pub fn get_name_with_check_mode(&self) -> String { + // FIXME: Remove because this is not needed anymore. format!("{}-{}", self.get_name(), self.get_check_mode()) } } diff --git a/prusti-common/src/vir/to_viper.rs b/prusti-common/src/vir/to_viper.rs index e62a95a612c..0473d6ac235 100644 --- a/prusti-common/src/vir/to_viper.rs +++ b/prusti-common/src/vir/to_viper.rs @@ -133,7 +133,7 @@ impl<'v> ToViper<'v, viper::Type<'v>> for Type { impl<'v, 'a, 'b> ToViper<'v, viper::Expr<'v>> for (&'a LocalVar, &'b Position) { fn to_viper(&self, context: Context, ast: &AstFactory<'v>) -> viper::Expr<'v> { - if self.0.name == "__result" { + if self.0.name == vir::common::builtin_constants::RESULT_VARIABLE_NAME { ast.result_with_pos( self.0.typ.to_viper(context, ast), self.1.to_viper(context, ast), diff --git a/prusti-contracts/prusti-contracts-proc-macros/src/lib.rs b/prusti-contracts/prusti-contracts-proc-macros/src/lib.rs index a6b2b89f5f9..e7a28966e9e 100644 --- a/prusti-contracts/prusti-contracts-proc-macros/src/lib.rs +++ b/prusti-contracts/prusti-contracts-proc-macros/src/lib.rs @@ -16,6 +16,12 @@ pub fn invariant(_attr: TokenStream, tokens: TokenStream) -> TokenStream { tokens } +#[cfg(not(feature = "prusti"))] +#[proc_macro_attribute] +pub fn structural_invariant(_attr: TokenStream, tokens: TokenStream) -> TokenStream { + tokens +} + #[cfg(not(feature = "prusti"))] #[proc_macro_attribute] pub fn ensures(_attr: TokenStream, tokens: TokenStream) -> TokenStream { @@ -70,6 +76,12 @@ pub fn prusti_assume(_tokens: TokenStream) -> TokenStream { TokenStream::new() } +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn prusti_structural_assume(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + #[cfg(not(feature = "prusti"))] #[proc_macro_attribute] pub fn refine_trait_spec(_attr: TokenStream, tokens: TokenStream) -> TokenStream { @@ -124,6 +136,132 @@ pub fn body_variant(_tokens: TokenStream) -> TokenStream { TokenStream::new() } +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn manually_manage(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn pack(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn unpack(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn pack_ref(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn unpack_ref(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn pack_mut_ref(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn unpack_mut_ref(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn take_lifetime(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn join(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn join_range(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn split(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn split_range(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn stash_range(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn restore_stash_range(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn close_ref(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn open_ref(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn close_mut_ref(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn open_mut_ref(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn forget_initialization(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn restore(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[cfg(not(feature = "prusti"))] +#[proc_macro] +pub fn set_union_active_field(_tokens: TokenStream) -> TokenStream { + TokenStream::new() +} + // ---------------------- // --- PRUSTI ENABLED --- @@ -195,6 +333,12 @@ pub fn prusti_assume(tokens: TokenStream) -> TokenStream { prusti_specs::prusti_assume(tokens.into()).into() } +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn prusti_structural_assume(tokens: TokenStream) -> TokenStream { + prusti_specs::prusti_structural_assume(tokens.into()).into() +} + #[cfg(feature = "prusti")] #[proc_macro] pub fn closure(tokens: TokenStream) -> TokenStream { @@ -216,7 +360,13 @@ pub fn extern_spec(attr: TokenStream, tokens: TokenStream) -> TokenStream { #[cfg(feature = "prusti")] #[proc_macro_attribute] pub fn invariant(attr: TokenStream, tokens: TokenStream) -> TokenStream { - prusti_specs::invariant(attr.into(), tokens.into()).into() + prusti_specs::invariant(attr.into(), tokens.into(), false).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro_attribute] +pub fn structural_invariant(attr: TokenStream, tokens: TokenStream) -> TokenStream { + prusti_specs::invariant(attr.into(), tokens.into(), true).into() } #[cfg(feature = "prusti")] @@ -261,5 +411,131 @@ pub fn body_variant(tokens: TokenStream) -> TokenStream { prusti_specs::body_variant(tokens.into()).into() } +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn manually_manage(tokens: TokenStream) -> TokenStream { + prusti_specs::manually_manage(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn pack(tokens: TokenStream) -> TokenStream { + prusti_specs::pack(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn unpack(tokens: TokenStream) -> TokenStream { + prusti_specs::unpack(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn pack_ref(tokens: TokenStream) -> TokenStream { + prusti_specs::pack_ref(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn unpack_ref(tokens: TokenStream) -> TokenStream { + prusti_specs::unpack_ref(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn pack_mut_ref(tokens: TokenStream) -> TokenStream { + prusti_specs::pack_mut_ref(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn unpack_mut_ref(tokens: TokenStream) -> TokenStream { + prusti_specs::unpack_mut_ref(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn take_lifetime(tokens: TokenStream) -> TokenStream { + prusti_specs::take_lifetime(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn join(tokens: TokenStream) -> TokenStream { + prusti_specs::join(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn join_range(tokens: TokenStream) -> TokenStream { + prusti_specs::join_range(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn split(tokens: TokenStream) -> TokenStream { + prusti_specs::split(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn split_range(tokens: TokenStream) -> TokenStream { + prusti_specs::split_range(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn stash_range(tokens: TokenStream) -> TokenStream { + prusti_specs::stash_range(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn restore_stash_range(tokens: TokenStream) -> TokenStream { + prusti_specs::restore_stash_range(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn close_ref(tokens: TokenStream) -> TokenStream { + prusti_specs::close_ref(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn open_ref(tokens: TokenStream) -> TokenStream { + prusti_specs::open_ref(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn close_mut_ref(tokens: TokenStream) -> TokenStream { + prusti_specs::close_mut_ref(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn open_mut_ref(tokens: TokenStream) -> TokenStream { + prusti_specs::open_mut_ref(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn forget_initialization(tokens: TokenStream) -> TokenStream { + prusti_specs::forget_initialization(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn restore(tokens: TokenStream) -> TokenStream { + prusti_specs::restore(tokens.into()).into() +} + +#[cfg(feature = "prusti")] +#[proc_macro] +pub fn set_union_active_field(tokens: TokenStream) -> TokenStream { + prusti_specs::set_union_active_field(tokens.into()).into() +} + // Ensure that you've also crated a transparent `#[cfg(not(feature = "prusti"))]` // version of your new macro above! diff --git a/prusti-contracts/prusti-contracts/src/core_spec.rs b/prusti-contracts/prusti-contracts/src/core_spec.rs index 61fa73e42b8..c5e560b35f7 100644 --- a/prusti-contracts/prusti-contracts/src/core_spec.rs +++ b/prusti-contracts/prusti-contracts/src/core_spec.rs @@ -16,3 +16,57 @@ impl ::core::result::Result { #[requires(matches!(self, Ok(_)))] fn unwrap(self) -> T; } + +// Crashes ☹ +type Pointer = *const T; +#[extern_spec] +impl Pointer { + #[trusted] + #[terminates] + #[pure] + // FIXME: This is needed because this function is special cased only in the + // pure encoder and not in the impure one. + #[ensures(result == self.is_null())] + fn is_null(self) -> bool; +} + +type MutPointer = *mut T; +#[extern_spec] +impl MutPointer { + #[trusted] + #[terminates] + #[pure] + // FIXME: This is needed because this function is special cased only in the + // pure encoder and not in the impure one. + #[ensures(result == self.is_null())] + fn is_null(self) -> bool; + + #[trusted] + #[terminates] + #[pure] + // FIXME: Properly specify the wrapping arithmetic. + // FIXME: This is needed because this function is special cased only in the + // pure encoder and not in the impure one. + #[ensures(result == self.wrapping_offset(count))] + fn wrapping_offset(self, count: isize) -> *mut T; +} + +#[extern_spec] +mod core { + mod mem { + #[pure] + // FIXME: This is needed because this function is special cased only in the + // pure encoder and not in the impure one. + #[ensures(result == core::mem::size_of::())] + pub fn size_of() -> usize; + + #[pure] + // FIXME: What are the guarantees? + // https://doc.rust-lang.org/std/mem/fn.align_of.html says nothing… + #[ensures(result > 0)] + // FIXME: This is needed because this function is special cased only in the + // pure encoder and not in the impure one. + #[ensures(result == core::mem::align_of::())] + pub fn align_of() -> usize; + } +} diff --git a/prusti-contracts/prusti-contracts/src/lib.rs b/prusti-contracts/prusti-contracts/src/lib.rs index b7e40364743..fd592600b88 100644 --- a/prusti-contracts/prusti-contracts/src/lib.rs +++ b/prusti-contracts/prusti-contracts/src/lib.rs @@ -24,6 +24,10 @@ pub use prusti_contracts_proc_macros::verified; /// A macro for type invariants. pub use prusti_contracts_proc_macros::invariant; +/// A macro for structural type invariants. A type with a structural +/// invariant needs to be managed manually by the user. +pub use prusti_contracts_proc_macros::structural_invariant; + /// A macro for writing a loop body invariant. pub use prusti_contracts_proc_macros::body_invariant; @@ -33,6 +37,9 @@ pub use prusti_contracts_proc_macros::prusti_assert; /// A macro for writing assumptions using prusti syntax pub use prusti_contracts_proc_macros::prusti_assume; +/// A macro for writing structural assumptions using prusti syntax +pub use prusti_contracts_proc_macros::prusti_structural_assume; + /// A macro for impl blocks that refine trait specifications. pub use prusti_contracts_proc_macros::refine_trait_spec; @@ -63,6 +70,70 @@ pub use prusti_contracts_proc_macros::terminates; /// A macro to annotate body variant of a loop to prove termination pub use prusti_contracts_proc_macros::body_variant; +/// A macro to mark the place as manually managed. +pub use prusti_contracts_proc_macros::manually_manage; + +/// A macro to manually pack a place capability. +pub use prusti_contracts_proc_macros::pack; + +/// A macro to manually unpack a place capability. +pub use prusti_contracts_proc_macros::unpack; + +/// A macro to manually pack a place capability. +pub use prusti_contracts_proc_macros::pack_ref; + +/// A macro to manually unpack a place capability. +pub use prusti_contracts_proc_macros::unpack_ref; + +/// A macro to manually pack a place capability. +pub use prusti_contracts_proc_macros::pack_mut_ref; + +/// A macro to manually unpack a place capability. +pub use prusti_contracts_proc_macros::unpack_mut_ref; + +/// A macro to obtain a lifetime of a variable. +pub use prusti_contracts_proc_macros::take_lifetime; + +/// A macro to manually join a place capability. +pub use prusti_contracts_proc_macros::join; + +/// A macro to manually join a range of memory blocks into one. +pub use prusti_contracts_proc_macros::join_range; + +/// A macro to manually split a place capability. +pub use prusti_contracts_proc_macros::split; + +/// A macro to manually split a memory block into a range of memory blocks. +pub use prusti_contracts_proc_macros::split_range; + +/// A macro to stash away a range of own capabilities to get access to +/// underlying raw memory. +pub use prusti_contracts_proc_macros::stash_range; + +/// A macro to restore the stash away a range of own capabilities. +pub use prusti_contracts_proc_macros::restore_stash_range; + +/// A macro to manually close a reference. +pub use prusti_contracts_proc_macros::close_ref; + +/// A macro to manually open a reference. +pub use prusti_contracts_proc_macros::open_ref; + +/// A macro to manually close a reference. +pub use prusti_contracts_proc_macros::close_mut_ref; + +/// A macro to manually open a reference. +pub use prusti_contracts_proc_macros::open_mut_ref; + +/// A macro to forget that a place is initialized. +pub use prusti_contracts_proc_macros::forget_initialization; + +/// A macro to restore a place capability. +pub use prusti_contracts_proc_macros::restore; + +/// A macro to set a specific field of the union as active. +pub use prusti_contracts_proc_macros::set_union_active_field; + #[cfg(not(feature = "prusti"))] mod private { use core::marker::PhantomData; @@ -116,6 +187,17 @@ mod private { pub struct Ghost { _phantom: PhantomData, } + + /// A type allowing to refer to a lifetime in places where Rust syntax does + /// not allow it. It should not be possible to construct from Rust code, + /// hence the private unit inside. + pub struct Lifetime(()); + + /// A methematical type representing a machine byte. + pub struct Byte(()); + + /// A methematical type representing a sequence of machine bytes. + pub struct Bytes(()); } #[cfg(feature = "prusti")] @@ -128,15 +210,21 @@ mod private { /// A macro for defining a closure with a specification. pub use prusti_contracts_proc_macros::{closure, pure, trusted}; - pub fn prusti_set_union_active_field(_arg: T) { - unreachable!(); - } + // pub fn prusti_set_union_active_field(_arg: T) { + // unreachable!(); + // } #[pure] pub fn prusti_terminates_trusted() -> Int { Int::new(1) } + /// A type allowing to refer to a lifetime in places where Rust syntax does + /// not allow it. It should not be possible to construct from Rust code, + /// hence the private unit inside. + #[derive(Copy, Clone)] + pub struct Lifetime(()); + /// a mathematical (unbounded) integer type /// it should not be constructed from running rust code, hence the private unit inside #[derive(Copy, Clone, PartialEq, Eq)] @@ -322,6 +410,14 @@ mod private { panic!() } } + + /// A methematical type representing a machine byte. + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct Byte(()); + + /// A methematical type representing a sequence of machine bytes. + #[derive(Copy, Clone, PartialEq, Eq)] + pub struct Bytes(()); } /// This function is used to evaluate an expression in the context just @@ -365,4 +461,309 @@ pub fn snapshot_equality(_l: T, _r: T) -> bool { true } +#[doc(hidden)] +#[trusted] +pub fn prusti_manually_manage(_arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_pack_place(_arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_unpack_place(_arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_pack_ref_place(_lifetime_name: &'static str, _arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_unpack_ref_place(_lifetime_name: &'static str, _arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_pack__mut_ref_place(_lifetime_name: &'static str, _arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_unpack_mut_ref_place(_lifetime_name: &'static str, _arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_take_lifetime(_arg: T, _lifetime_name: &'static str) -> Lifetime { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_join_place(_arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_join_range(_arg: T, _start_index: usize, _end_index: usize) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_split_place(_arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_split_range(_arg: T, _start_index: usize, _end_index: usize) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_stash_range( + _arg: T, + _start_index: usize, + _end_index: usize, + _witness: &'static str, +) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_restore_stash_range(_arg: T, _new_start_index: usize, _witness: &'static str) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +/// We need to pass `_arg` to make sure the lifetime covers the closing of the +/// reference. +pub fn prusti_close_ref_place(_arg: T, _witness: &'static str) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_open_ref_place(_lifetime: &'static str, _arg: T, _witness: &'static str) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +/// We need to pass `_arg` to make sure the lifetime covers the closing of the +/// reference. +pub fn prusti_close_mut_ref_place(_arg: T, _witness: &'static str) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_open_mut_ref_place(_lifetime: &'static str, _arg: T, _witness: &'static str) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_forget_initialization(_arg: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_restore_place(_arg1: T, _arg2: T) { + unreachable!(); +} + +#[doc(hidden)] +#[trusted] +pub fn prusti_set_union_active_field(_arg: T) { + unreachable!(); +} + +/// Indicates that the expression should be evaluated assuming that the given +/// predicate is present. +#[doc(hidden)] +#[trusted] +pub fn prusti_eval_in(_predicate: bool, expression: T) -> T { + unreachable!(); +} + +#[macro_export] +macro_rules! eval_in { + ($predicate:expr, $expression:expr) => { + $crate::prusti_eval_in($predicate, $expression) + }; +} + +/// Indicates that we have the `own` capability to the specified place. +#[doc(hidden)] +#[trusted] +pub fn prusti_own(_place: T) -> bool { + unreachable!(); +} + +#[macro_export] +macro_rules! own { + ($place:expr) => { + $crate::prusti_own(unsafe { core::ptr::addr_of!($place) }) + }; +} + +/// Indicates that we have the `own` capability to the specified range. +#[doc(hidden)] +#[trusted] +pub fn prusti_own_range(_address: T, _start: usize, _end: usize) -> bool { + unreachable!(); +} + +#[macro_export] +macro_rules! own_range { + ($address:expr, $start:expr, $end:expr) => { + $crate::prusti_own_range(unsafe { core::ptr::addr_of!($address) }, $start, $end) + }; +} + +/// Indicates that we have the shared reference capability to the specified +/// place. +#[doc(hidden)] +#[trusted] +pub fn prusti_shr(_place: T) -> bool { + unreachable!(); +} + +#[macro_export] +macro_rules! shr { + ($place:expr) => { + $crate::prusti_shr(unsafe { core::ptr::addr_of!($place) }) + }; +} + +/// Indicates that we have the unique reference capability to the specified +/// place. +#[doc(hidden)] +#[trusted] +pub fn prusti_unq(_place: T) -> bool { + unreachable!(); +} + +#[macro_export] +macro_rules! unq { + ($place:expr) => { + $crate::prusti_unq(unsafe { core::ptr::addr_of!($place) }) + }; +} + +/// Deref a raw pointer with the specified offset. +#[doc(hidden)] +#[trusted] +pub unsafe fn prusti_deref_own(_address: *const T, _index: usize) -> T { + unreachable!(); +} + +#[macro_export] +macro_rules! deref_own { + ($address:expr, $index:expr) => { + unsafe { $crate::prusti_deref_own($address, $index) } + }; +} + +/// Obtain the bytes of the specified memory block. +#[doc(hidden)] +#[trusted] +pub fn prusti_bytes(_address: T, _length: usize) -> Bytes { + unreachable!(); +} + +#[macro_export] +macro_rules! bytes { + ($address:expr, $length:expr) => { + $crate::prusti_bytes(unsafe { core::ptr::addr_of!($address) }, $length) + }; +} + +/// Read the byte at the given index. +/// +/// FIXME: This function does not check bounds. Instead, it returns garbage in +/// case of out-of-bounds +pub fn read_byte(bytes: Bytes, index: Int) -> Byte { + unreachable!(); +} + +/// Indicates that we have the `raw` capability to the specified address. +#[doc(hidden)] +#[trusted] +pub fn prusti_raw(_address: T, _size: usize) -> bool { + true +} + +#[macro_export] +macro_rules! raw { + ($place:expr, $size: expr) => { + $crate::prusti_raw(unsafe { core::ptr::addr_of!($place) }, $size) + }; +} + +/// Indicates that we have the `raw` capability to the specified range. +#[doc(hidden)] +#[trusted] +pub fn prusti_raw_range(_address: T, _size: usize, _start: usize, _end: usize) -> bool { + unreachable!(); +} + +#[macro_export] +macro_rules! raw_range { + ($address:expr, $size:expr, $start:expr, $end:expr) => { + $crate::prusti_raw_range( + unsafe { core::ptr::addr_of!($address) }, + $size, + $start, + $end, + ) + }; +} + +/// Indicates that we have the capability to deallocate. +#[doc(hidden)] +#[trusted] +pub fn prusti_raw_dealloc(_address: T, _size: usize) -> bool { + true +} + +#[macro_export] +macro_rules! raw_dealloc { + ($place:expr, $size: expr, $align: expr) => { + $crate::prusti_raw_dealloc(unsafe { core::ptr::addr_of!($place) }, $size) + }; +} + +/// Temporarily unpacks the owned predicate at the given location. +#[doc(hidden)] +#[trusted] +pub fn prusti_unpacking(_place: T, _body: U) -> U { + unimplemented!() +} + +#[macro_export] +macro_rules! unpacking { + ($place:expr, $body: expr) => { + $crate::prusti_unpacking(unsafe { core::ptr::addr_of!($place) }, $body) + }; +} + pub use private::*; diff --git a/prusti-contracts/prusti-specs/src/lib.rs b/prusti-contracts/prusti-specs/src/lib.rs index 0d00f218f71..1cb17925128 100644 --- a/prusti-contracts/prusti-specs/src/lib.rs +++ b/prusti-contracts/prusti-specs/src/lib.rs @@ -431,6 +431,10 @@ pub fn prusti_assume(tokens: TokenStream) -> TokenStream { generate_expression_closure(&AstRewriter::process_prusti_assumption, tokens) } +pub fn prusti_structural_assume(tokens: TokenStream) -> TokenStream { + generate_expression_closure(&AstRewriter::process_prusti_structural_assumption, tokens) +} + /// Generates the TokenStream encoding an expression using prusti syntax /// Used for body invariants, assertions, and assumptions fn generate_expression_closure( @@ -450,6 +454,23 @@ fn generate_expression_closure( } } +fn prusti_specification_expression( + tokens: TokenStream, +) -> syn::Result<(SpecificationId, TokenStream)> { + let mut rewriter = rewriter::AstRewriter::new(); + let spec_id = rewriter.generate_spec_id(); + let closure = rewriter.process_prusti_specification_expression(spec_id, tokens)?; + let callsite_span = Span::call_site(); + let tokens = quote_spanned! {callsite_span=> + #[allow(unused_must_use, unused_variables, unused_braces, unused_parens)] + #[prusti::specs_version = #SPECS_VERSION] + if false { + #closure + } + }; + Ok((spec_id, tokens)) +} + pub fn closure(tokens: TokenStream) -> TokenStream { let cl_spec: ClosureWithSpec = handle_result!(syn::parse(tokens.into())); let callsite_span = Span::call_site(); @@ -703,7 +724,7 @@ pub fn trusted(attr: TokenStream, tokens: TokenStream) -> TokenStream { } } -pub fn invariant(attr: TokenStream, tokens: TokenStream) -> TokenStream { +pub fn invariant(attr: TokenStream, tokens: TokenStream, is_structural: bool) -> TokenStream { let mut rewriter = rewriter::AstRewriter::new(); let spec_id = rewriter.generate_spec_id(); let spec_id_str = spec_id.to_string(); @@ -711,19 +732,30 @@ pub fn invariant(attr: TokenStream, tokens: TokenStream) -> TokenStream { let item: syn::DeriveInput = handle_result!(syn::parse2(tokens)); let item_span = item.span(); let item_ident = item.ident.clone(); + let item_name_structural = if is_structural { + "structural" + } else { + "non_structural" + }; let item_name = syn::Ident::new( - &format!("prusti_invariant_item_{item_ident}_{spec_id}"), + &format!("prusti_invariant_item_{item_name_structural}_{item_ident}_{spec_id}"), item_span, ); let attr = handle_result!(parse_prusti(attr)); + let is_structural_tokens = if is_structural { + quote_spanned!(item_span => #[prusti::type_invariant_structural]) + } else { + quote_spanned!(item_span => #[prusti::type_invariant_non_structural]) + }; // TODO: move some of this to AstRewriter? // see AstRewriter::generate_spec_item_fn for explanation of syntax below let spec_item: syn::ItemFn = parse_quote_spanned! {item_span=> #[allow(unused_must_use, unused_parens, unused_variables, dead_code, non_snake_case)] #[prusti::spec_only] #[prusti::type_invariant_spec] + #is_structural_tokens #[prusti::spec_id = #spec_id_str] fn #item_name(self) -> bool { !!((#attr) : bool) @@ -1112,3 +1144,316 @@ pub fn ghost(tokens: TokenStream) -> TokenStream { syn_errors } } + +pub fn manually_manage(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_manually_manage}) +} + +pub fn pack(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_pack_place}) +} + +pub fn unpack(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_unpack_place}) +} + +pub fn pack_ref(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_pack_ref_place}) +} + +pub fn unpack_ref(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_unpack_ref_place}) +} + +pub fn pack_mut_ref(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_pack_mut_ref_place}) +} + +pub fn unpack_mut_ref(tokens: TokenStream) -> TokenStream { + // generate_place_function(tokens, quote!{prusti_unpack_mut_ref_place}) + let (lifetime_name, reference) = + handle_result!(parse_two_expressions::(tokens)); + let lifetime_name_str = handle_result!(expression_to_string(&lifetime_name)); + unsafe_spec_function_call(quote! { + prusti_unpack_mut_ref_place(#lifetime_name_str, std::ptr::addr_of!(#reference)) + }) +} + +fn parse_two_expressions( + tokens: TokenStream, +) -> syn::Result<(syn::Expr, syn::Expr)> { + let parser = syn::punctuated::Punctuated::::parse_terminated; + let mut expressions = syn::parse::Parser::parse2(parser, tokens)?; + let second = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected two expressions"))?; + let first = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected two expressions"))?; + Ok((first.into_value(), second.into_value())) +} + +fn parse_three_expressions( + tokens: TokenStream, +) -> syn::Result<(syn::Expr, syn::Expr, syn::Expr)> { + let parser = syn::punctuated::Punctuated::::parse_terminated; + let mut expressions = syn::parse::Parser::parse2(parser, tokens)?; + let third = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected three expressions"))?; + let second = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected three expressions"))?; + let first = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected three expressions"))?; + Ok((first.into_value(), second.into_value(), third.into_value())) +} + +fn parse_four_expressions( + tokens: TokenStream, +) -> syn::Result<(syn::Expr, syn::Expr, syn::Expr, syn::Expr)> { + let parser = syn::punctuated::Punctuated::::parse_terminated; + let mut expressions = syn::parse::Parser::parse2(parser, tokens)?; + let fourth = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected four expressions"))?; + let third = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected four expressions"))?; + let second = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected four expressions"))?; + let first = expressions + .pop() + .ok_or_else(|| syn::Error::new(Span::call_site(), "Expected four expressions"))?; + Ok(( + first.into_value(), + second.into_value(), + third.into_value(), + fourth.into_value(), + )) +} + +fn expression_to_string(expr: &syn::Expr) -> syn::Result { + if let syn::Expr::Path(syn::ExprPath { + qself: None, path, .. + }) = expr + { + if let Some(ident) = path.get_ident() { + return Ok(ident.to_string()); + } + } + Err(syn::Error::new(expr.span(), "needs to be an identifier")) +} + +pub fn unsafe_spec_function_call(call: TokenStream) -> TokenStream { + let callsite_span = Span::call_site(); + quote_spanned! { callsite_span => + #[allow(unused_must_use, unused_variables)] + #[prusti::specs_version = #SPECS_VERSION] + if false { + #[prusti::spec_only] + || -> bool { true }; + unsafe { #call }; + } + } +} + +pub fn take_lifetime(tokens: TokenStream) -> TokenStream { + let (reference, lifetime_name) = + handle_result!(parse_two_expressions::(tokens)); + let lifetime_name_str = handle_result!(expression_to_string(&lifetime_name)); + unsafe_spec_function_call(quote! { + prusti_take_lifetime(std::ptr::addr_of!(#reference), #lifetime_name_str) + }) + // let parser = syn::punctuated::Punctuated::]>::parse_terminated; + // let mut args = handle_result!(syn::parse::Parser::parse2(parser, tokens)); + // let lifetime = if let Some(lifetime) = args.pop() { + // lifetime.into_value() + // } else { + // return syn::Error::new( + // args.span(), + // "`take_lifetime!` needs to contain two arguments `` and ``" + // ).to_compile_error(); + // }; + // let lifetime_str = if let syn::Expr::Path(syn::ExprPath { qself: None, path, ..}) = lifetime { + // if let Some(ident) = path.get_ident() { + // ident.to_string() + // } else { + // return syn::Error::new( + // path.span(), + // "lifetime name needs to be an identifier" + // ).to_compile_error(); + // } + // } else { + // return syn::Error::new( + // lifetime.span(), + // "lifetime name needs to be an identifier" + // ).to_compile_error(); + // }; + // let reference = if let Some(reference) = args.pop() { + // reference.into_value() + // } else { + // return syn::Error::new( + // args.span(), + // "`take_lifetime!` needs to contain two arguments `` and ``" + // ).to_compile_error(); + // }; + // let callsite_span = Span::call_site(); + // quote_spanned! { callsite_span => + // #[allow(unused_must_use, unused_variables)] + // #[prusti::specs_version = #SPECS_VERSION] + // if false { + // #[prusti::spec_only] + // || -> bool { true }; + // unsafe { prusti_take_lifetime(std::ptr::addr_of!(#reference), #lifetime_str) }; + // } + // } +} + +pub fn join(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_join_place}) +} + +pub fn join_range(tokens: TokenStream) -> TokenStream { + let (pointer, start_index, end_index) = + handle_result!(parse_three_expressions::(tokens)); + unsafe_spec_function_call(quote! { + prusti_join_range(std::ptr::addr_of!(#pointer), {#start_index}, #end_index) + }) +} + +pub fn split(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_split_place}) +} + +pub fn split_range(tokens: TokenStream) -> TokenStream { + let (pointer, start_index, end_index) = + handle_result!(parse_three_expressions::(tokens)); + unsafe_spec_function_call(quote! { + prusti_split_range(std::ptr::addr_of!(#pointer), {#start_index}, #end_index) + }) +} + +/// FIXME: For `start_index` and `end_index`, we should do the same as for +/// `body_invariant!`. +pub fn stash_range(tokens: TokenStream) -> TokenStream { + let (pointer, start_index, end_index, witness) = + handle_result!(parse_four_expressions::(tokens)); + let witness_str = handle_result!(expression_to_string(&witness)); + unsafe_spec_function_call(quote! { + prusti_stash_range(std::ptr::addr_of!(#pointer), {#start_index}, {#end_index}, #witness_str) + }) +} + +/// FIXME: For `new_start_index`, we should do the same as for +/// `body_invariant!`. +pub fn restore_stash_range(tokens: TokenStream) -> TokenStream { + let (pointer, new_start_index, witness) = + handle_result!(parse_three_expressions::(tokens)); + let witness_str = handle_result!(expression_to_string(&witness)); + unsafe_spec_function_call(quote! { + prusti_restore_stash_range(std::ptr::addr_of!(#pointer), {#new_start_index}, #witness_str) + }) +} + +fn close_any_ref(tokens: TokenStream, function: TokenStream) -> TokenStream { + let (reference, witness) = handle_result!(parse_two_expressions::(tokens)); + let witness_str = handle_result!(expression_to_string(&witness)); + let (spec_id, reference_closure) = handle_result!(prusti_specification_expression( + quote! { unsafe { &#reference } } + )); + let spec_id_str = spec_id.to_string(); + let call = unsafe_spec_function_call(quote! { #function(#spec_id_str, #witness_str) }); + quote! { + #call; + #reference_closure + } +} + +pub fn close_ref(tokens: TokenStream) -> TokenStream { + close_any_ref(tokens, quote! {prusti_close_ref_place}) +} + +pub fn close_mut_ref(tokens: TokenStream) -> TokenStream { + close_any_ref(tokens, quote! {prusti_close_mut_ref_place}) +} + +fn open_any_ref(tokens: TokenStream, function: TokenStream) -> TokenStream { + let (lifetime_name, reference, witness) = + handle_result!(parse_three_expressions::(tokens)); + let lifetime_name_str = handle_result!(expression_to_string(&lifetime_name)); + let witness_str = handle_result!(expression_to_string(&witness)); + let (spec_id, reference_closure) = handle_result!(prusti_specification_expression( + quote! { unsafe { &#reference } } + )); + let spec_id_str = spec_id.to_string(); + let call = unsafe_spec_function_call(quote! { + #function(#lifetime_name_str, #spec_id_str, #witness_str) + }); + quote! { + #reference_closure; + #call + } +} + +pub fn open_ref(tokens: TokenStream) -> TokenStream { + open_any_ref(tokens, quote! {prusti_open_ref_place}) +} + +pub fn open_mut_ref(tokens: TokenStream) -> TokenStream { + open_any_ref(tokens, quote! {prusti_open_mut_ref_place}) +} + +pub fn set_union_active_field(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_set_union_active_field}) +} + +pub fn forget_initialization(tokens: TokenStream) -> TokenStream { + generate_place_function(tokens, quote! {prusti_forget_initialization}) +} + +fn generate_place_function(tokens: TokenStream, function: TokenStream) -> TokenStream { + let callsite_span = Span::call_site(); + quote_spanned! { callsite_span => + #[allow(unused_must_use, unused_variables)] + #[prusti::specs_version = #SPECS_VERSION] + if false { + #[prusti::spec_only] + || -> bool { true }; + unsafe { #function(std::ptr::addr_of!(#tokens)) }; + } + } +} + +pub fn restore(tokens: TokenStream) -> TokenStream { + let parser = syn::punctuated::Punctuated::::parse_terminated; + let mut args = handle_result!(syn::parse::Parser::parse2(parser, tokens)); + let restored_place = if let Some(restored_place) = args.pop() { + restored_place.into_value() + } else { + return syn::Error::new( + args.span(), + "`restore!` needs to contain two arguments `` and ``" + ).to_compile_error(); + }; + let borrowing_place = if let Some(borrowing_place) = args.pop() { + borrowing_place.into_value() + } else { + return syn::Error::new( + args.span(), + "`restore!` needs to contain two arguments `` and ``" + ).to_compile_error(); + }; + let callsite_span = Span::call_site(); + quote_spanned! { callsite_span => + #[allow(unused_must_use, unused_variables)] + #[prusti::specs_version = #SPECS_VERSION] + if false { + #[prusti::spec_only] + || -> bool { true }; + unsafe { prusti_restore_place(std::ptr::addr_of!(#borrowing_place), std::ptr::addr_of!(#restored_place)) }; + } + } +} diff --git a/prusti-contracts/prusti-specs/src/rewriter.rs b/prusti-contracts/prusti-specs/src/rewriter.rs index a9df02429d8..a679b4b9a56 100644 --- a/prusti-contracts/prusti-specs/src/rewriter.rs +++ b/prusti-contracts/prusti-specs/src/rewriter.rs @@ -255,6 +255,35 @@ impl AstRewriter { self.process_prusti_expression(quote! {prusti_assumption}, spec_id, tokens) } + /// Parse a prusti structural assumption into a Rust expression + pub fn process_prusti_structural_assumption( + &mut self, + spec_id: SpecificationId, + tokens: TokenStream, + ) -> syn::Result { + self.process_prusti_expression(quote! {prusti_structural_assumption}, spec_id, tokens) + } + + /// Parse a prusti expression used as an argument to some ghost operation + pub fn process_prusti_specification_expression( + &mut self, + spec_id: SpecificationId, + tokens: TokenStream, + ) -> syn::Result { + let expr = parse_prusti(tokens)?; + let spec_id_str = spec_id.to_string(); + Ok(quote_spanned! {expr.span()=> + { + #[prusti::spec_only] + #[prusti::prusti_specification_expression] + #[prusti::spec_id = #spec_id_str] + || { + #expr + }; + } + }) + } + fn process_prusti_expression( &mut self, kind: TokenStream, diff --git a/prusti-interface/src/environment/body.rs b/prusti-interface/src/environment/body.rs index 43c24eaea55..017b4f9ce40 100644 --- a/prusti-interface/src/environment/body.rs +++ b/prusti-interface/src/environment/body.rs @@ -1,3 +1,4 @@ +use crate::environment::{borrowck::facts::BorrowckFacts, mir_storage}; use log::trace; use prusti_common::config; use prusti_rustc_interface::{ @@ -11,8 +12,6 @@ use prusti_rustc_interface::{ use rustc_hash::FxHashMap; use std::{cell::RefCell, collections::hash_map::Entry, rc::Rc}; -use crate::environment::{borrowck::facts::BorrowckFacts, mir_storage}; - /// Stores any possible MIR body (from the compiler) that /// Prusti might want to work with. Cheap to clone #[derive(Clone, TyEncodable, TyDecodable)] @@ -161,6 +160,7 @@ impl<'tcx> EnvBody<'tcx> { substs: SubstsRef<'tcx>, caller_def_id: Option, body: MirBody<'tcx>, + keep_lifetimes: bool, ) -> MirBody<'tcx> { if let Entry::Vacant(v) = self.monomorphised_bodies @@ -169,8 +169,14 @@ impl<'tcx> EnvBody<'tcx> { { let monomorphised = if let Some(caller_def_id) = caller_def_id { let param_env = self.tcx.param_env(caller_def_id); - self.tcx - .subst_and_normalize_erasing_regions(substs, param_env, body.0) + if keep_lifetimes { + use prusti_rustc_interface::middle::ty::TypeVisitable; + assert!(!body.0.has_projections(), "unimplemented: projections are not supported because normalizing them erases lifetimes"); + ty::EarlyBinder(body.0).subst(self.tcx, substs) + } else { + self.tcx + .subst_and_normalize_erasing_regions(substs, param_env, body.0) + } } else { ty::EarlyBinder(body.0).subst(self.tcx, substs) }; @@ -182,12 +188,27 @@ impl<'tcx> EnvBody<'tcx> { /// Get the MIR body of a local impure function, without any substitutions. pub fn get_impure_fn_body_identity(&self, def_id: LocalDefId) -> MirBody<'tcx> { - let mut impure = self.local_impure_fns.borrow_mut(); - impure - .entry(def_id) - .or_insert_with(|| Self::load_local_mir_with_facts(self.tcx, def_id)) - .body - .clone() + // let mut impure = self.local_impure_fns.borrow_mut(); + // impure + // .entry(def_id) + // .or_insert_with(|| Self::load_local_mir_with_facts(self.tcx, def_id)) + // .body + // .clone() + self.borrow_impure_fn_body_identity(def_id).clone() + } + + /// Borrow the MIR body of a local impure function, without any substitutions. + pub fn borrow_impure_fn_body_identity( + &self, + def_id: LocalDefId, + ) -> std::cell::RefMut> { + let impure = self.local_impure_fns.borrow_mut(); + std::cell::RefMut::map(impure, |impure| { + &mut impure + .entry(def_id) + .or_insert_with(|| Self::load_local_mir_with_facts(self.tcx, def_id)) + .body + }) } /// Get the MIR body of a local impure function, monomorphised @@ -197,31 +218,61 @@ impl<'tcx> EnvBody<'tcx> { return body; } let body = self.get_impure_fn_body_identity(def_id); - self.set_monomorphised(def_id.to_def_id(), substs, None, body) + self.set_monomorphised( + def_id.to_def_id(), + substs, + None, + body, + config::unsafe_core_proof(), + ) } fn get_closure_body_identity(&self, def_id: LocalDefId) -> MirBody<'tcx> { let mut closures = self.local_closures.borrow_mut(); closures .entry(def_id) - .or_insert_with(|| Self::load_local_mir(self.tcx, def_id)) + .or_insert_with(|| { + if config::unsafe_core_proof() { + Self::load_local_mir_with_facts(self.tcx, def_id).body + } else { + Self::load_local_mir(self.tcx, def_id) + } + }) .clone() } /// Get the MIR body of a local closure (e.g. loop invariant or trigger), /// monomorphised with the given type substitutions. - pub fn get_closure_body( + pub fn get_closure_body_lifetimes_opt( &self, def_id: LocalDefId, substs: SubstsRef<'tcx>, caller_def_id: DefId, + keep_lifetimes: bool, ) -> MirBody<'tcx> { if let Some(body) = self.get_monomorphised(def_id.to_def_id(), substs, Some(caller_def_id)) { return body; } let body = self.get_closure_body_identity(def_id); - self.set_monomorphised(def_id.to_def_id(), substs, Some(caller_def_id), body) + self.set_monomorphised( + def_id.to_def_id(), + substs, + Some(caller_def_id), + body, + keep_lifetimes, + ) + } + + /// Get the MIR body of a local closure (e.g. loop invariant or trigger), + /// monomorphised with the given type substitutions. + pub fn get_closure_body( + &self, + def_id: LocalDefId, + substs: SubstsRef<'tcx>, + caller_def_id: DefId, + ) -> MirBody<'tcx> { + self.get_closure_body_lifetimes_opt(def_id, substs, caller_def_id, false) } /// Get the MIR body of a local or external pure function, @@ -236,7 +287,7 @@ impl<'tcx> EnvBody<'tcx> { return body; } let body = self.pure_fns.expect(def_id); - self.set_monomorphised(def_id, substs, Some(caller_def_id), body) + self.set_monomorphised(def_id, substs, Some(caller_def_id), body, false) } /// Get the MIR body of a local or external expression (e.g. any spec or predicate), @@ -254,7 +305,7 @@ impl<'tcx> EnvBody<'tcx> { .specs .get(def_id) .unwrap_or_else(|| self.predicates.expect(def_id)); - self.set_monomorphised(def_id, substs, Some(caller_def_id), body) + self.set_monomorphised(def_id, substs, Some(caller_def_id), body, false) } /// Get the MIR body of a local or external spec (pres/posts/pledges/type-specs), @@ -269,7 +320,7 @@ impl<'tcx> EnvBody<'tcx> { return body; } let body = self.specs.expect(def_id); - self.set_monomorphised(def_id, substs, Some(caller_def_id), body) + self.set_monomorphised(def_id, substs, Some(caller_def_id), body, false) } /// Get Polonius facts of a local procedure. @@ -296,9 +347,12 @@ impl<'tcx> EnvBody<'tcx> { if self.specs.local.contains_key(&def_id) { return; } - self.specs - .local - .insert(def_id, Self::load_local_mir(self.tcx, def_id)); + let body = if config::unsafe_core_proof() { + Self::load_local_mir_with_facts(self.tcx, def_id).body + } else { + Self::load_local_mir(self.tcx, def_id) + }; + self.specs.local.insert(def_id, body); } pub(crate) fn load_predicate_body(&mut self, def_id: LocalDefId) { assert!(!self.predicates.local.contains_key(&def_id)); diff --git a/prusti-interface/src/environment/debug_utils/to_text.rs b/prusti-interface/src/environment/debug_utils/to_text.rs index f42f02d755a..9b762c926bf 100644 --- a/prusti-interface/src/environment/debug_utils/to_text.rs +++ b/prusti-interface/src/environment/debug_utils/to_text.rs @@ -1,6 +1,6 @@ use crate::environment::mir_body::borrowck::facts::Point; use std::collections::{BTreeMap, BTreeSet}; -use vir::common::graphviz::escape_html; +use vir::common::{builtin_constants::ERASED_LIFETIME_NAME, graphviz::escape_html}; pub trait ToText { fn to_text(&self) -> String; @@ -189,7 +189,7 @@ impl<'tcx> ToText for prusti_rustc_interface::middle::ty::Region<'tcx> { prusti_rustc_interface::middle::ty::RePlaceholder(_) => { unimplemented!("RePlaceholder: {}", format!("{self}")); } - prusti_rustc_interface::middle::ty::ReErased => String::from("lft_erased"), + prusti_rustc_interface::middle::ty::ReErased => String::from(ERASED_LIFETIME_NAME), } } } diff --git a/prusti-interface/src/environment/mir_storage.rs b/prusti-interface/src/environment/mir_storage.rs index 495cde9f21d..1e765adaa45 100644 --- a/prusti-interface/src/environment/mir_storage.rs +++ b/prusti-interface/src/environment/mir_storage.rs @@ -45,7 +45,8 @@ pub(super) unsafe fn retrieve_mir_body<'tcx>( ) -> BodyWithBorrowckFacts<'tcx> { let body_with_facts: BodyWithBorrowckFacts<'static> = SHARED_STATE.with(|state| { let mut map = state.borrow_mut(); - map.remove(&def_id).unwrap() + map.remove(&def_id) + .unwrap_or_else(|| panic!("not found: {def_id:?}")) }); // SAFETY: See the module level comment. unsafe { std::mem::transmute(body_with_facts) } diff --git a/prusti-interface/src/environment/mod.rs b/prusti-interface/src/environment/mod.rs index eedb5a0130d..d6495a05efa 100644 --- a/prusti-interface/src/environment/mod.rs +++ b/prusti-interface/src/environment/mod.rs @@ -140,21 +140,19 @@ impl<'tcx> Environment<'tcx> { called_def_id: ProcedureDefId, call_substs: SubstsRef<'tcx>, ) -> bool { - if called_def_id == caller_def_id { - true - } else { + if called_def_id != caller_def_id && called_def_id.is_local() { let param_env = self.tcx().param_env(caller_def_id); if let Some(instance) = self .tcx() .resolve_instance(param_env.and((called_def_id, call_substs))) .unwrap() { - self.tcx() - .mir_callgraph_reachable((instance, caller_def_id.expect_local())) - } else { - true + return self + .tcx() + .mir_callgraph_reachable((instance, caller_def_id.expect_local())); } } + true } /// Get the current version of the `prusti` crate diff --git a/prusti-interface/src/lib.rs b/prusti-interface/src/lib.rs index 34112b46050..35c5e3ca5c4 100644 --- a/prusti-interface/src/lib.rs +++ b/prusti-interface/src/lib.rs @@ -9,6 +9,7 @@ #![deny(unused_must_use)] #![deny(unsafe_op_in_unsafe_fn)] #![warn(clippy::disallowed_types)] +#![allow(clippy::nonminimal_bool)] #![feature(rustc_private)] #![feature(box_syntax)] #![feature(box_patterns)] diff --git a/prusti-interface/src/specs/mod.rs b/prusti-interface/src/specs/mod.rs index 0950a1e73d5..01bedb41f92 100644 --- a/prusti-interface/src/specs/mod.rs +++ b/prusti-interface/src/specs/mod.rs @@ -59,6 +59,7 @@ impl From<&ProcedureSpecRefs> for ProcedureSpecificationKind { #[derive(Debug, Default)] struct TypeSpecRefs { invariants: Vec, + structural_invariants: Vec, trusted: bool, model: Option<(String, LocalDefId)>, countexample_print: Vec<(Option, LocalDefId)>, @@ -82,8 +83,10 @@ pub struct SpecCollector<'a, 'tcx> { type_specs: FxHashMap, prusti_assertions: Vec, prusti_assumptions: Vec, + prusti_structural_assumptions: Vec, ghost_begin: Vec, ghost_end: Vec, + specification_expression: Vec, } impl<'a, 'tcx> SpecCollector<'a, 'tcx> { @@ -98,8 +101,10 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { type_specs: FxHashMap::default(), prusti_assertions: vec![], prusti_assumptions: vec![], + prusti_structural_assumptions: vec![], ghost_begin: vec![], ghost_end: vec![], + specification_expression: vec![], } } @@ -112,6 +117,7 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { self.determine_prusti_assertions(&mut def_spec); self.determine_prusti_assumptions(&mut def_spec); self.determine_ghost_begin_ends(&mut def_spec); + self.determine_specification_expressions(&mut def_spec); // TODO: remove spec functions (make sure none are duplicated or left over) // Load all local spec MIR bodies, for export and later use self.ensure_local_mirs_fetched(&def_spec); @@ -157,7 +163,7 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { ))); } SpecIdRef::Terminates(spec_id) => { - spec.set_terminates(*self.spec_functions.get(spec_id).unwrap()); + spec.set_terminates(self.spec_functions.get(spec_id).unwrap().to_def_id()); } } } @@ -220,7 +226,9 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { fn determine_type_specs(&self, def_spec: &mut typed::DefSpecificationMap) { for (type_id, refs) in self.type_specs.iter() { - if !refs.invariants.is_empty() && !prusti_common::config::enable_type_invariants() { + if !(refs.invariants.is_empty() && refs.structural_invariants.is_empty()) + && !prusti_common::config::enable_type_invariants() + { let span = self.env.query.get_def_span(*type_id); PrustiError::unsupported( "Type invariants need to be enabled with the feature flag `enable_type_invariants`", @@ -240,6 +248,13 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { .map(LocalDefId::to_def_id) .collect(), ), + structural_invariant: SpecificationItem::Inherent( + refs.structural_invariants + .clone() + .into_iter() + .map(LocalDefId::to_def_id) + .collect(), + ), trusted: SpecificationItem::Inherent(refs.trusted), model: refs.model.clone(), counterexample_print: refs.countexample_print.clone(), @@ -258,11 +273,21 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { } } fn determine_prusti_assumptions(&self, def_spec: &mut typed::DefSpecificationMap) { - for local_id in self.prusti_assumptions.iter() { + for local_id in &self.prusti_assumptions { + def_spec.prusti_assumptions.insert( + local_id.to_def_id(), + typed::PrustiAssumption { + assumption: *local_id, + is_structural: false, + }, + ); + } + for local_id in &self.prusti_structural_assumptions { def_spec.prusti_assumptions.insert( local_id.to_def_id(), typed::PrustiAssumption { assumption: *local_id, + is_structural: true, }, ); } @@ -280,6 +305,16 @@ impl<'a, 'tcx> SpecCollector<'a, 'tcx> { .insert(local_id.to_def_id(), typed::GhostEnd { marker: *local_id }); } } + fn determine_specification_expressions(&self, def_spec: &mut typed::DefSpecificationMap) { + for local_id in self.specification_expression.iter() { + def_spec.specification_expression.insert( + local_id.to_def_id(), + typed::SpecificationExpression { + expression: *local_id, + }, + ); + } + } fn ensure_local_mirs_fetched(&mut self, def_spec: &typed::DefSpecificationMap) { let (specs, pure_fns, predicates) = def_spec.defid_for_export(); @@ -440,11 +475,20 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for SpecCollector<'a, 'tcx> { let hir = self.env.query.hir(); let impl_id = hir.parent_id(hir.parent_id(self_id)); let type_id = get_type_id_from_impl_node(hir.get(impl_id)).unwrap(); - self.type_specs - .entry(type_id.as_local().unwrap()) - .or_default() - .invariants - .push(local_id); + if has_prusti_attr(attrs, "type_invariant_structural") { + self.type_specs + .entry(type_id.as_local().unwrap()) + .or_default() + .structural_invariants + .push(local_id); + } else { + assert!(has_prusti_attr(attrs, "type_invariant_non_structural")); + self.type_specs + .entry(type_id.as_local().unwrap()) + .or_default() + .invariants + .push(local_id); + } } // Collect trusted type flag @@ -481,6 +525,10 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for SpecCollector<'a, 'tcx> { self.prusti_assumptions.push(local_id); } + if has_prusti_attr(attrs, "prusti_structural_assumption") { + self.prusti_structural_assumptions.push(local_id); + } + if has_prusti_attr(attrs, "ghost_begin") { self.ghost_begin.push(local_id); } @@ -488,6 +536,10 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for SpecCollector<'a, 'tcx> { if has_prusti_attr(attrs, "ghost_end") { self.ghost_end.push(local_id); } + + if has_prusti_attr(attrs, "prusti_specification_expression") { + self.specification_expression.push(local_id); + } } else { // Don't collect specs "for" spec items diff --git a/prusti-interface/src/specs/typed.rs b/prusti-interface/src/specs/typed.rs index e12290812db..cb909916e31 100644 --- a/prusti-interface/src/specs/typed.rs +++ b/prusti-interface/src/specs/typed.rs @@ -20,6 +20,7 @@ pub struct DefSpecificationMap { pub prusti_assumptions: FxHashMap, pub ghost_begin: FxHashMap, pub ghost_end: FxHashMap, + pub specification_expression: FxHashMap, } impl DefSpecificationMap { @@ -55,6 +56,10 @@ impl DefSpecificationMap { self.ghost_end.get(def_id) } + pub fn get_specification_expression(&self, def_id: &DefId) -> Option<&SpecificationExpression> { + self.specification_expression.get(def_id) + } + pub(crate) fn defid_for_export( &self, ) -> ( @@ -79,7 +84,7 @@ impl DefSpecificationMap { specs.extend(posts); } if let Some(Some(term)) = spec.terminates.extract_with_selective_replacement() { - specs.push(term.to_def_id()); + specs.push(*term); } if let Some(pledges) = spec.pledges.extract_with_selective_replacement() { specs.extend(pledges.iter().filter_map(|pledge| pledge.lhs)); @@ -102,6 +107,12 @@ impl DefSpecificationMap { if let Some(invariants) = spec.invariant.extract_with_selective_replacement() { specs.extend(invariants); } + if let Some(invariants) = spec + .structural_invariant + .extract_with_selective_replacement() + { + specs.extend(invariants); + } } (specs, pure_fns, predicates) } @@ -202,7 +213,7 @@ pub struct ProcedureSpecification { pub posts: SpecificationItem>, pub pledges: SpecificationItem>, pub trusted: SpecificationItem, - pub terminates: SpecificationItem>, + pub terminates: SpecificationItem>, pub purity: SpecificationItem>, // for type-conditional spec refinements } @@ -266,6 +277,7 @@ pub struct TypeSpecification { // `extern_spec` for type invs is supported it could differ. pub source: DefId, pub invariant: SpecificationItem>, + pub structural_invariant: SpecificationItem>, pub trusted: SpecificationItem, pub model: Option<(String, LocalDefId)>, pub counterexample_print: Vec<(Option, LocalDefId)>, @@ -276,6 +288,7 @@ impl TypeSpecification { TypeSpecification { source, invariant: SpecificationItem::Empty, + structural_invariant: SpecificationItem::Empty, trusted: SpecificationItem::Inherent(false), model: None, counterexample_print: vec![], @@ -291,6 +304,7 @@ pub struct PrustiAssertion { #[derive(Debug, Clone)] pub struct PrustiAssumption { pub assumption: LocalDefId, + pub is_structural: bool, } #[derive(Debug, Clone)] @@ -303,6 +317,11 @@ pub struct GhostEnd { pub marker: LocalDefId, } +#[derive(Debug, Clone)] +pub struct SpecificationExpression { + pub expression: LocalDefId, +} + /// The base container to store a contract of a procedure. /// A contract can be divided into multiple specifications: /// - **Base spec**: A spec without constraints. @@ -472,7 +491,7 @@ impl SpecGraph { } /// Sets the termination flag for the base spec and all constrained specs. - pub fn set_terminates(&mut self, terminates: LocalDefId) { + pub fn set_terminates(&mut self, terminates: DefId) { self.base_spec.terminates.set(Some(terminates)); self.specs_with_constraints .values_mut() diff --git a/prusti-server/src/process_verification.rs b/prusti-server/src/process_verification.rs index 32c9b0b9179..c3c98c99e14 100644 --- a/prusti-server/src/process_verification.rs +++ b/prusti-server/src/process_verification.rs @@ -105,7 +105,7 @@ pub fn process_verification_request<'v, 't: 'v>( let mut backend = match request.backend_config.backend { VerificationBackend::Carbon | VerificationBackend::Silicon => Backend::Viper( new_viper_verifier( - request.program.get_name(), + request.program.get_name_with_check_mode(), verification_context, request.backend_config, ), @@ -164,11 +164,22 @@ fn new_viper_verifier<'v, 't: 'v>( //"--printTranslatedProgram".to_string(), ]) } - VerificationBackend::Carbon => verifier_args.extend(vec![ - "--boogieOpt".to_string(), - format!("/logPrefix {log_dir_str}"), - //"--print".to_string(), "./log/boogie_program/program.bpl".to_string(), - ]), + VerificationBackend::Carbon => { + let mut found_boogie_opt = false; + for arg in &mut verifier_args { + if arg.starts_with("--boogieOpt") { + arg.push_str(&format!(" /logPrefix:{log_dir_str}")); + found_boogie_opt = true; + } + } + if !found_boogie_opt { + verifier_args.extend(vec![ + "--boogieOpt".to_string(), + format!("/logPrefix {log_dir_str}"), + //"--print".to_string(), "./log/boogie_program/program.bpl".to_string(), + ]) + } + } } } else { report_path = None; diff --git a/prusti-server/src/verification_request.rs b/prusti-server/src/verification_request.rs index 6f33840ea70..5067558a5de 100644 --- a/prusti-server/src/verification_request.rs +++ b/prusti-server/src/verification_request.rs @@ -40,6 +40,10 @@ impl ViperBackendConfig { if config::use_more_complete_exhale() { verifier_args.push("--enableMoreCompleteExhale".to_string()); } + if config::use_carbon_qps() { + verifier_args.push("--carbonQPs".to_string()); + verifier_args.push("--carbonFunctions".to_string()); + } if config::counterexample() { verifier_args.push("--counterexample".to_string()); verifier_args.push("mapped".to_string()); @@ -48,7 +52,6 @@ impl ViperBackendConfig { verifier_args.push("--numberOfParallelVerifiers".to_string()); verifier_args.push(number.to_string()); } - verifier_args.extend(vec![ "--assertTimeout".to_string(), config::assert_timeout().to_string(), @@ -56,9 +59,12 @@ impl ViperBackendConfig { // model.partial changes the default case of functions in counterexamples // to #unspecified format!( - "smt.qi.eager_threshold={} model.partial={}", + "smt.qi.eager_threshold={} model.partial={} \ + smt.arith.nl={} smt.arith.nl.gb={}", config::smt_qi_eager_threshold(), - config::counterexample() + config::counterexample(), + config::smt_use_nonlinear_arithmetic_solver(), + config::smt_use_nonlinear_arithmetic_solver(), ), "--logLevel".to_string(), "ERROR".to_string(), @@ -70,7 +76,13 @@ impl ViperBackendConfig { } } VerificationBackend::Carbon => { - verifier_args.extend(vec!["--disableAllocEncoding".to_string()]); + verifier_args.extend(vec![ + "--disableAllocEncoding".to_string(), + format!( + "--boogieOpt=/proverOpt:O:smt.qi.eager_threshold={}", + config::smt_qi_eager_threshold() + ), + ]); } } Self { diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/custom_heap_encoding/simple.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/custom_heap_encoding/simple.rs new file mode 100644 index 00000000000..0afc4a2d794 --- /dev/null +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/custom_heap_encoding/simple.rs @@ -0,0 +1,17 @@ +// compile-flags: -Punsafe_core_proof=true + +use prusti_contracts::*; + +unsafe fn test_assert1() { + let a = 5; + assert!(a == 5); +} + +unsafe fn test_assert2() { + let a = 5; + assert!(a == 6); //~ ERROR: the asserted expression might not hold +} + +#[trusted] +fn main() {} + diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/forall.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/forall.rs new file mode 100644 index 00000000000..14a44776c62 --- /dev/null +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/forall.rs @@ -0,0 +1,29 @@ +// compile-flags: -Punsafe_core_proof=true + +use prusti_contracts::*; + +fn test_forall_1() -> usize { + let res = 5; + prusti_assert!( + forall(|x: usize| true) || false + ); + res +} + +fn test_forall_2() -> usize { + let res = 5; + prusti_assert!( + forall(|x: usize| x >= 0) + ); + res +} + +fn test_forall_3() -> usize { + let res = 5; + prusti_assert!( + forall(|x: usize| x >= 1) //~ ERROR: the asserted expression might not hold + ); + res +} + +fn main() {} diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/framing.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/framing.rs new file mode 100644 index 00000000000..83c1d7dfd57 --- /dev/null +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/framing.rs @@ -0,0 +1,408 @@ +// compile-flags: -Punsafe_core_proof=true -Penable_type_invariants=true + +#![deny(unsafe_op_in_unsafe_fn)] + +use prusti_contracts::*; + +// TODO: Check only on the definition side. Add tests. + +//#[ensures(!result.is_null() ==> own!((*result).x) && unsafe { (*result).x } == 5)] +//unsafe fn test01() -> *mut Pair { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).x = 5; } + //} + //pair +//} + +//#[ensures(!result.is_null() ==> unsafe { (*result).x } == 5)] //~ ERROR: the place must be framed by permissions +//unsafe fn test01_non_framed() -> *mut Pair { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).x = 5; } + //} + //pair +//} + +//#[ensures(!result.is_null() ==> own!(*result) && unsafe { (*result).x } == 5)] +//unsafe fn test02() -> *mut Pair { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //pair +//} + +//#[ensures(!result.is_null() ==> own!(*result) && unsafe { (*result).x } == 5)] +//unsafe fn test02_missing_pack() -> *mut Pair { //~ ERROR: there might be insufficient permission to dereference a raw pointer + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //} + //pair +//} + +//#[ensures(!result.is_null() ==> unsafe { (*result).x } == 5)] //~ ERROR: there might be insufficient permission to dereference a raw pointer + ////^ ERROR: the postcondition might not be self-framing +//unsafe fn test02_non_framed() -> *mut Pair { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //pair +//} + +//#[ensures(!result.is_null() ==> own!(*result) && unsafe { (*result).x } == 5)] //~ ERROR: only unsafe functions can use permissions in their contracts +//fn test02_safe() -> *mut Pair { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //pair +//} + +//#[ensures(!result.is_null() ==> //~ ERROR: permission predicates can be only in positive positions + //own!(*result) && unsafe { !(*result).is_null() } ==> + //own!(**result) && unsafe { (**result).x } == 5)] +//unsafe fn test03() -> *mut *mut Pair { + //let pp = unsafe { + //alloc(std::mem::size_of::<*mut Pair>(), std::mem::align_of::<*mut Pair>()) + //}; + //let ppair = (pp as *mut *mut Pair); + //ppair +//} + +//#[ensures(!result.is_null() ==> + //own!(*result) && ( + //unsafe { !(*result).is_null() } ==> + //own!(**result) && unsafe { (**result).x } == 5))] +//unsafe fn test04() -> *mut *mut Pair { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pp = unsafe { + //alloc(std::mem::size_of::<*mut Pair>(), std::mem::align_of::<*mut Pair>()) + //}; + //let pair = (p as *mut Pair); + //let ppair = (pp as *mut *mut Pair); + //let mut v = 0; + //if !ppair.is_null() { + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //unsafe { *ppair = pair; } + //if !pair.is_null() { + //unpack!(**ppair); + //unsafe { v = (**ppair).x; } + //pack!(**ppair); + //} + //} + //ppair +//} + +//#[ensures(!result.is_null() ==> //~ ERROR: postcondition might not hold + //own!(*result) && ( + //unsafe { !(*result).is_null() } ==> + //own!(**result) && unsafe { (**result).x } == 6))] +//unsafe fn test04_wrong_value() -> *mut *mut Pair { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pp = unsafe { + //alloc(std::mem::size_of::<*mut Pair>(), std::mem::align_of::<*mut Pair>()) + //}; + //let pair = (p as *mut Pair); + //let ppair = (pp as *mut *mut Pair); + //let mut v = 0; + //if !ppair.is_null() { + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //unsafe { *ppair = pair; } + //if !pair.is_null() { + //unpack!(**ppair); + //unsafe { v = (**ppair).x; } + //pack!(**ppair); + //} + //} + //ppair +//} + +//#[ensures(!result.1.is_null() ==> + //own!(*result.1) && ( + //unsafe { !(*result.1).is_null() } ==> + //own!(**result.1) && unsafe { (**result.1).x } == 5 && + //result.0 == 5))] +//unsafe fn test05() -> (i32, *mut *mut Pair) { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pp = unsafe { + //alloc(std::mem::size_of::<*mut Pair>(), std::mem::align_of::<*mut Pair>()) + //}; + //let pair = (p as *mut Pair); + //let ppair = (pp as *mut *mut Pair); + //let mut v = 0; + //if !ppair.is_null() { + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //unsafe { *ppair = pair; } + //if !pair.is_null() { + //unpack!(**ppair); + //unsafe { v = (**ppair).x; } + //pack!(**ppair); + //} + //} + //(v, ppair) +//} + +//#[ensures(!result.1.is_null() ==> //~ ERROR: postcondition might not hold + //own!(*result.1) && ( + //unsafe { !(*result.1).is_null() } ==> + //own!(**result.1) && unsafe { (**result.1).x } == 6 && + //result.0 == 5))] +//unsafe fn test05_wrong_value_1() -> (i32, *mut *mut Pair) { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pp = unsafe { + //alloc(std::mem::size_of::<*mut Pair>(), std::mem::align_of::<*mut Pair>()) + //}; + //let pair = (p as *mut Pair); + //let ppair = (pp as *mut *mut Pair); + //let mut v = 0; + //if !ppair.is_null() { + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //unsafe { *ppair = pair; } + //if !pair.is_null() { + //unpack!(**ppair); + //unsafe { v = (**ppair).x; } + //pack!(**ppair); + //} + //} + //(v, ppair) +//} + +//#[ensures(!result.1.is_null() ==> //~ ERROR: postcondition might not hold + //own!(*result.1) && ( + //unsafe { !(*result.1).is_null() } ==> + //own!(**result.1) && unsafe { (**result.1).x } == 5 && + //result.0 == 6))] +//unsafe fn test05_wrong_value_2() -> (i32, *mut *mut Pair) { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pp = unsafe { + //alloc(std::mem::size_of::<*mut Pair>(), std::mem::align_of::<*mut Pair>()) + //}; + //let pair = (p as *mut Pair); + //let ppair = (pp as *mut *mut Pair); + //let mut v = 0; + //if !ppair.is_null() { + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //unsafe { *ppair = pair; } + //if !pair.is_null() { + //unpack!(**ppair); + //unsafe { v = (**ppair).x; } + //pack!(**ppair); + //} + //} + //(v, ppair) +//} + +//#[structural_invariant(!self.p.is_null() ==> own!(*self.p) && unsafe { (*self.p).x } == 5)] +//struct T6 { + //p: *mut Pair, +//} + +//fn test06(_: T6) {} + +//#[structural_invariant(!self.p.is_null() ==> unsafe { (*self.p).x } == 5)] +//struct T6MissingOwn { //~ ERROR: there might be insufficient permission to dereference a raw pointer + //p: *mut Pair, +//} + +//fn test06_missing_own(_: T6MissingOwn) {} + +//#[structural_invariant(!self.p.is_null() ==> own!(*self.p))] +#[structural_invariant(!self.p.is_null() ==> own!(*self.p) && unsafe {(*self.p).x} == 5)] +struct T4 { + p: *mut Pair, +} + +//#[ensures(!result.p.is_null() ==> unsafe { (*result.p).x } == 5)] +//unsafe fn test04() -> T4 { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //T4 { p: pair } +//} + +//#[ensures(unsafe { (*result.p).x } == 5)] +//unsafe fn test04_not_framed() -> T4 { //~ ERROR: there might be insufficient permission to dereference a raw pointer + ////^ ERROR: the postcondition might not be self-framing. + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //T4 { p: pair } +//} + +#[structural_invariant(!self.p.is_null() ==> own!((*self.p).x))] +struct T5 { + p: *mut Pair, +} + +#[ensures(!result.p.is_null() ==> unsafe { (*result.p).x } == 5)] +fn test05_safe() -> T5 { + let p = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let pair = (p as *mut Pair); + if !pair.is_null() { + split!(*pair); + //unsafe { (*pair).y = 4; } + unsafe { (*pair).x = 5; } + //pack!(*pair); + } + T5 { p: pair } +} + +//#[ensures(unsafe { (*result.p).x } == 5)] //~ ERROR: postcondition might not hold +//fn test04_safe_not_framed() -> T4 { //~ ERROR: there might be insufficient permission to dereference a raw pointer + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //T4 { p: pair } +//} + +//#[structural_invariant(!self.p.is_null() ==> own!((*self.p).x))] +//struct T2 { + //p: *mut Pair, +//} + +//#[ensures(!result.p.is_null() ==> framed!((*result.p).x, unsafe { (*result.p).x }) == 5)] +//fn test03() -> T1 { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).y = 4; } + //unsafe { (*pair).x = 5; } + //pack!(*pair); + //} + //T1 { p: pair } +//} + +//#[ensures(!result.p.is_null() ==> unsafe { (*result.p).x } == 5)] //~ ERROR: Permissions +//fn test03_non_framed() -> T1 { + //let p = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let pair = (p as *mut Pair); + //if !pair.is_null() { + //split!(*pair); + //unsafe { (*pair).x = 5; } + //} + //T1 { p: pair } +//} + +#[trusted] +#[requires(align > 0)] +#[ensures(!result.is_null() ==> ( + raw!(*result, size) && + raw_dealloc!(*result, size, align) +))] +// https://doc.rust-lang.org/alloc/alloc/fn.alloc.html +unsafe fn alloc(size: usize, align: usize) -> *mut u8 { + unimplemented!(); +} + +#[trusted] +#[requires( + raw!(*ptr, size) && + raw_dealloc!(*ptr, size, align) +)] +unsafe fn dealloc(ptr: *mut u8, size: usize, align: usize) { + unimplemented!(); +} + +struct Pair { + x: i32, + y: i32, +} + +fn main() {} diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/framing/functions.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/framing/functions.rs new file mode 100644 index 00000000000..25deada0649 --- /dev/null +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/framing/functions.rs @@ -0,0 +1,47 @@ +// compile-flags: -Punsafe_core_proof=true -Penable_type_invariants=true + +#![deny(unsafe_op_in_unsafe_fn)] + +use prusti_contracts::*; + +#[structural_invariant(!self.p.is_null() ==> own!(*self.p))] +struct T1 { + p: *mut i32, +} + +#[pure] +fn test1_get_p(x: &T1) -> i32 { + if self.p.is_null() { + 0 + } else { + unsafe { *self.p } + } +} + + +#[trusted] +#[requires(align > 0)] +#[ensures(!result.is_null() ==> ( + raw!(*result, size) && + raw_dealloc!(*result, size, align) +))] +// https://doc.rust-lang.org/alloc/alloc/fn.alloc.html +unsafe fn alloc(size: usize, align: usize) -> *mut u8 { + unimplemented!(); +} + +#[trusted] +#[requires( + raw!(*ptr, size) && + raw_dealloc!(*ptr, size, align) +)] +unsafe fn dealloc(ptr: *mut u8, size: usize, align: usize) { + unimplemented!(); +} + +struct Pair { + x: i32, + y: i32, +} + +fn main() {} diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/framing/simple.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/framing/simple.rs new file mode 100644 index 00000000000..cd8b3c7991e --- /dev/null +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/framing/simple.rs @@ -0,0 +1,244 @@ +// compile-flags: -Punsafe_core_proof=true -Penable_type_invariants=true + +#![deny(unsafe_op_in_unsafe_fn)] + +use prusti_contracts::*; + +#[ensures(!result.is_null() ==> unsafe { *result } == 5)] //~ ERROR: the place must be framed by permissions +fn test01_safe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + p +} + +#[ensures(!result.is_null() ==> own!(*result) && unsafe { *result } == 5)] //~ ERROR: only unsafe functions can use permissions in their contracts +fn test02_safe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + p +} + +#[ensures(!result.is_null() ==> own!(*result) && unsafe { *result } == 5)] +unsafe fn test03_unsafe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + p +} + +#[ensures(!result.is_null() ==> own!(*result) && unsafe { *result } == 6)] //~ ERROR: postcondition might not hold. +unsafe fn test04_unsafe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + p +} + +unsafe fn test05_unsafe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + unsafe { *p = 5; } //~ ERROR: the accessed memory location must be allocated and uninitialized + p +} + +#[ensures(own!(*result))] +unsafe fn test06_unsafe() -> *mut i32 { //~ ERROR: there might be insufficient permission to dereference a raw pointer + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + p +} + +unsafe fn test07_unsafe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + assert!(unsafe { *p } == 5); + } + p +} + +fn test07_safe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + assert!(unsafe { *p } == 5); + } + p +} + +fn callee() {} + +unsafe fn test08_unsafe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + callee(); + assert!(unsafe { *p } == 5); + } + p +} + +fn test08_safe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + callee(); + // Calling non-pure functions havoc the heap when in permissions + // are disabled: + assert!(unsafe { *p } == 5); //~ ERROR: the type invariant of the constructed object might not hold + //^ ERROR: the type invariant of the constructed object might not hold + } + p +} + +#[pure] +#[terminates] +fn pure_callee() {} + +unsafe fn test09_unsafe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + pure_callee(); + assert!(unsafe { *p } == 5); + } + p +} + +fn test09_safe() -> *mut i32 { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + pure_callee(); + assert!(unsafe { *p } == 5); + } + p +} + +#[ensures(!result.0.is_null() ==> unsafe { *result.0 } == 5)] //~ ERROR: the place must be framed by permissions +fn test11_safe() -> (*mut i32, *mut i32) { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + (p, p) +} + +#[ensures(!result.0.is_null() ==> own!(*result.0) && unsafe { *result.0 } == 5)] //~ ERROR: only unsafe functions can use permissions in their contracts +fn test12_safe() -> (*mut i32, *mut i32) { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + (p, p) +} + +#[ensures(!result.0.is_null() ==> own!(*result.0) && unsafe { *result.0 } == 5)] +unsafe fn test13_unsafe() -> (*mut i32, *mut i32) { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + (p, p) +} + +// Note: This works and `test14_unsafe_semantic_aliasing` fails because +// framing of unsafe function postconditions is done by Viper. +#[ensures(result.0 == result.1)] +#[ensures(!result.0.is_null() ==> own!(*result.0) && unsafe { *result.1 } == 5)] +unsafe fn test13_unsafe_semantic_aliasing() -> (*mut i32, *mut i32) { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + (p, p) +} + +#[ensures(!result.0.is_null() ==> own!(*result.0) && unsafe { *result.1 } == 5)] //~ ERROR: the postcondition might not be self-framing. +unsafe fn test14_unsafe_semantic_aliasing() -> (*mut i32, *mut i32) { + let p_alloc = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p_alloc as *mut i32); + if !p.is_null() { + unsafe { *p = 5; } + } + (p, p) +} + +#[trusted] +#[requires(align > 0)] +#[ensures(!result.is_null() ==> ( + raw!(*result, size) && + raw_dealloc!(*result, size, align) +))] +// https://doc.rust-lang.org/alloc/alloc/fn.alloc.html +unsafe fn alloc(size: usize, align: usize) -> *mut u8 { + unimplemented!(); +} + +#[trusted] +#[requires( + raw!(*ptr, size) && + raw_dealloc!(*ptr, size, align) +)] +unsafe fn dealloc(ptr: *mut u8, size: usize, align: usize) { + unimplemented!(); +} + +fn main() {} diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/invariants.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/invariants.rs new file mode 100644 index 00000000000..8467b7b605f --- /dev/null +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/invariants.rs @@ -0,0 +1,556 @@ +// compile-flags: -Punsafe_core_proof=true -Penable_type_invariants=true +// -Pverify_specifications_with_core_proof=true +// -Puse_snapshot_parameters_in_predicates=true + +use prusti_contracts::*; + +// struct T1 { +// f: i32, +// } + +// fn test1(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// unpack!((*b).f); +// unsafe { (*b).f = 4; } +// pack!(*b); +// restore!(*b, a); +// assert!(a.f == 4); +// a +// } + +// fn test2(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// forget_initialization!((*b).f); +// unsafe { (*b).f = 4; } +// pack!(*b); +// restore!(*b, a); +// assert!(a.f == 4); +// a +// } + +// fn test3(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// unpack!((*b).f); +// unsafe { (*b).f = 4; } +// pack!(*b); +// restore!(*b, a); +// assert!(a.f == 5); //~ ERROR: the asserted expression might not hold +// a +// } + +// fn test4(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// forget_initialization!((*b).f); +// unsafe { (*b).f = 4; } +// pack!(*b); +// restore!(*b, a); +// assert!(a.f == 5); //~ ERROR: the asserted expression might not hold +// a +// } + +// fn test5(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// forget_initialization!((*b).f); +// unsafe { (*b).f = 4; } +// assert!( unsafe { (*b).f } == 4); +// pack!(*b); +// restore!(*b, a); +// a +// } + +// fn test6(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// forget_initialization!((*b).f); +// assert!( unsafe { (*b).f } == 4); //~ ERROR: the asserted expression might not hold +// //^ ERROR: the accessed place may not be allocated or initialized +// unsafe { (*b).f = 4; } +// pack!(*b); +// restore!(*b, a); +// a +// } + +// fn test7(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// assert!( unsafe { (*b).f } == 4); //~ ERROR: the asserted expression might not hold +// forget_initialization!((*b).f); +// unsafe { (*b).f = 4; } +// pack!(*b); +// restore!(*b, a); +// a +// } + +// #[requires(b ==> own!(*p))] +// #[ensures(b ==> ((own!(*p)) && unsafe { *p } == 4))] +// unsafe fn test8(p: *mut i32, b: bool) { +// if b { +// forget_initialization!(*p); +// unsafe { *p = 4 }; +// } +// } + +// #[ensures(result.f == 4)] +// fn test9(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a.f); +// unsafe { test8(b, true); } +// restore!(*b, a.f); +// a +// } + +// #[requires(b ==> own!(*p))] +// #[ensures(b ==> ((own!(*p)) && unsafe { *p } == 5))] //~ ERROR: postcondition might not hold. +// unsafe fn test10(p: *mut i32, b: bool) { +// if b { +// forget_initialization!(*p); +// unsafe { *p = 4 }; +// } +// } + +// #[ensures(result.f == 5)] //~ ERROR: postcondition might not hold. +// fn test11(mut a: T1) -> T1 { +// let b = std::ptr::addr_of_mut!(a.f); +// unsafe { test8(b, true); } +// restore!(*b, a.f); +// a +// } + +// struct T2 { +// f: i32, +// g: i32, +// } + +// #[ensures(result.f == 4 && result.g == a.g)] +// fn test12(mut a: T2) -> T2 { +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// unpack!((*b).f); +// unsafe { (*b).f = 4; } +// pack!(*b); +// restore!(*b, a); +// assert!(a.f == 4); +// a +// } + +// #[ensures(result.f == 5 && result.g == a.g)] +// fn test13(mut a: T2) -> T2 { //~ ERROR: postcondition might not hold. +// let b = std::ptr::addr_of_mut!(a); +// unpack!(*b); +// unpack!((*b).f); +// unsafe { (*b).f = 4; } +// pack!(*b); +// restore!(*b, a); +// assert!(a.f == 4); +// a +// } + +// #[requires(b ==> (own!(*p) && unsafe { *p } < 20))] +// #[ensures(b ==> (own!(*p) && unsafe { *p } == old(unsafe { *p }) + 1))] +// unsafe fn test14(p: *mut i32, b: bool) { +// if b { +// // FIXME: unsafe { *p += 1 }; +// let tmp = unsafe { *p }; +// forget_initialization!(*p); +// unsafe { *p = tmp + 1 }; +// } +// } + +// #[ensures(result.f == 7)] +// fn test15(mut a: T1) -> T1 { +// a.f = 6; +// let b = std::ptr::addr_of_mut!(a.f); +// unsafe { test14(b, true); } +// restore!(*b, a.f); +// a +// } + +// #[ensures(result.f == 8)] +// fn test16(mut a: T1) -> T1 { +// a.f = 6; +// let b = std::ptr::addr_of_mut!(a.f); +// unsafe { test14(b, true); } +// restore!(*b, a.f); +// a +// } + +// fn alloc_client() { +// let size = std::mem::size_of::(); +// let align = std::mem::align_of::(); +// let ptr = unsafe { alloc(size, align) }; +// if !ptr.is_null() { +// unsafe { *(ptr as *mut u16) = 42; } +// assert!(unsafe { *(ptr as *mut u16) } == 42); +// let ptr_u16 = (ptr as *mut u16); +// forget_initialization!(*ptr_u16); // FIXME: We should support (ptr as *mut u16). +// unsafe { dealloc(ptr, size, align) }; +// } +// } + +// fn alloc_client2() { +// let size = std::mem::size_of::(); +// let align = std::mem::align_of::(); +// let ptr = unsafe { alloc(size, align) }; +// if !ptr.is_null() { +// unsafe { *(ptr as *mut u16) = 42; } +// assert!(unsafe { *(ptr as *mut u16) } == 43); //~ ERROR: the asserted expression might not hold +// let ptr_u16 = (ptr as *mut u16); +// forget_initialization!(*ptr_u16); // FIXME: We should support (ptr as *mut u16). +// unsafe { dealloc(ptr, size, align) }; +// } +// } + +// fn alloc_client3() { +// let size = std::mem::size_of::(); +// let align = std::mem::align_of::(); +// let ptr = unsafe { alloc(size, align) }; +// unsafe { *(ptr as *mut u16) = 42; } //~ ERROR: the accessed memory location must be allocated and uninitialized +// } + +// #[requires(x < 5)] +// unsafe fn check_x(x: u32) {} + +// #[structural_invariant(self.x < 5)] +// struct T3 { +// x: u32, +// } + +// fn test17(a: T3) { +// unpack!(a); +// unsafe { check_x(a.x) } +// pack!(a); +// forget_initialization!(a); +// } + +// #[structural_invariant( +// !self.p1.is_null() ==> ( +// raw!(*self.p1, std::mem::size_of::()) && +// raw_dealloc!(*self.p1, std::mem::size_of::(), std::mem::align_of::()) +// ) +// )] +// #[structural_invariant( +// !self.p2.is_null() ==> ( +// own!(*self.p2) && +// raw_dealloc!(*self.p2, std::mem::size_of::(), std::mem::align_of::()) +// ) +// )] +// struct T4 { +// p1: *mut i32, +// p2: *mut i32, +// } + +// impl T4 { +// fn new() -> Self { +// let p1 = unsafe { +// alloc(std::mem::size_of::(), std::mem::align_of::()) +// }; +// let p2 = unsafe { +// alloc(std::mem::size_of::(), std::mem::align_of::()) +// }; +// if !p2.is_null() { +// unsafe { *(p2 as *mut i32) = 42; } +// } +// Self { p1: (p1 as *mut i32), p2: (p2 as *mut i32) } +// } +// } + +// #[structural_invariant( +// !self.p2.is_null() ==> ( +// own!(*self.p2) && +// raw_dealloc!(*self.p2, std::mem::size_of::(), std::mem::align_of::()) && +// unsafe { *self.p2 == 42 } && +// 1 == 1 && +// 2 == 2 && +// 3 == 3 && +// 4 == 4 && +// 5 == 5 && +// 6 == 6 +// ) +// )] +// struct T5 { +// p2: *mut i32, +// } + +// impl T5 { +// fn new() -> Self { +// let p2 = unsafe { +// alloc(std::mem::size_of::(), std::mem::align_of::()) +// }; +// if !p2.is_null() { +// unsafe { *(p2 as *mut i32) = 42; } +// } +// Self { p2: (p2 as *mut i32) } +// } +// fn new_fail() -> Self { +// let p2 = unsafe { +// alloc(std::mem::size_of::(), std::mem::align_of::()) +// }; +// if !p2.is_null() { +// unsafe { *(p2 as *mut i32) = 43; } +// } +// Self { p2: (p2 as *mut i32) } //~ ERROR: The type invariant of the constructed object might not hold +// } +// } + +#[structural_invariant( + !self.p.is_null() ==> ( + raw_dealloc!(*self.p, std::mem::size_of::(), std::mem::align_of::()) && + raw!((*self.p).x, std::mem::size_of::()) && + own!((*self.p).y) && + unsafe { (*self.p).y } == self.v + ) +)] +struct T6 { + v: i32, + p: *mut Pair, +} + +impl T6 { + #[ensures(result.v == 42)] + #[ensures( + unpacking!( + result, + !result.p.is_null() ==> + (unpacking!((*result.p).y, unsafe { (*result.p).y }) == 42) + ) + )] + fn new() -> Self { + let p2 = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p2 as *mut Pair); + if !p2.is_null() { + split!(*p); + unsafe { (*p).y = 42; } + } + Self { p, v: 42 } + } +// #[ensures(result.v == 42)] +// #[ensures( +// unpacking!( //~ ERROR: postcondition might not hold. +// result, +// !result.p.is_null() ==> +// (unpacking!((*result.p).y, unsafe { (*result.p).y }) == 43) +// ) +// )] +// fn new_fail_wrong_value() -> Self { +// let p2 = unsafe { +// alloc(std::mem::size_of::(), std::mem::align_of::()) +// }; +// let p = (p2 as *mut Pair); +// if !p2.is_null() { +// split!(*p); +// unsafe { (*p).y = 42; } +// } +// Self { p, v: 42 } +// } +// #[ensures(result.v == 42)] +// #[ensures( +// !result.p.is_null() ==> +// (unpacking!((*result.p).y, unsafe { (*result.p).y }) == 42) +// )] +// fn new_fail_missing_outer_unpacking() -> Self { //~ ERROR: there might be insufficient permission to dereference a raw pointer +// let p2 = unsafe { +// alloc(std::mem::size_of::(), std::mem::align_of::()) +// }; +// let p = (p2 as *mut Pair); +// if !p2.is_null() { +// split!(*p); +// unsafe { (*p).y = 42; } +// } +// Self { p, v: 42 } +// } + #[ensures(result.v == 42)] + #[ensures( + unpacking!( + result, + !result.p.is_null() ==> + (unsafe { (*result.p).y } == 42) + ) + )] + fn new_fail_missing_inner_unpacking() -> Self { + let p2 = unsafe { + alloc(std::mem::size_of::(), std::mem::align_of::()) + }; + let p = (p2 as *mut Pair); + if !p2.is_null() { + split!(*p); + unsafe { (*p).y = 42; } + } + Self { p, v: 42 } + } +// #[ensures(result.v == 42)] +// #[ensures( +// unpacking!( +// result, +// !result.p.is_null() ==> +// (unpacking!(*result.p, unsafe { (*result.p).y }) == 42) +// ) +// )] +// fn new_fail_wrong_inner_unpacking() -> Self { //~ ERROR: there might be insufficient permission to dereference a raw pointer +// let p2 = unsafe { +// alloc(std::mem::size_of::(), std::mem::align_of::()) +// }; +// let p = (p2 as *mut Pair); +// if !p2.is_null() { +// split!(*p); +// unsafe { (*p).y = 42; } +// } +// Self { p, v: 42 } +// } + + //#[ensures(result.v == 42)] + //#[ensures(!result.p.is_null() ==> (unsafe { (*result.p).y } == 42))] //~ ERROR: there might be insufficient permission to dereference a raw pointer + //fn new_fail1() -> Self { + //let p2 = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let p = (p2 as *mut Pair); + //if !p2.is_null() { + //split!(*p); + //unsafe { (*p).y = 42; } + //} + //Self { p, v: 42 } + //} + //fn new_fail() -> Self { + //let p2 = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let p = (p2 as *mut Pair); + //if !p2.is_null() { + //split!(*p); + //unsafe { (*p).y = 43; } + //} + //Self { p, v: 42 } //~ ERROR: The type invariant of the constructed object might not hold + //} + //#[ensures(result.v == 42)] + //#[ensures((unsafe { (*result.p).y } == 42))] //~ ERROR: there might be insufficient permission to dereference a raw pointer + ////^ ERROR: postcondition might not hold + //fn new_fail2() -> Self { + //let p2 = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let p = (p2 as *mut Pair); + //if !p2.is_null() { + //split!(*p); + //unsafe { (*p).y = 42; } + //} + //Self { p, v: 42 } + //} + + //#[ensures(result.v == 42)] + //// TODO: Make sure to distinguish unpacking!((*result.p), ...) from + //// unpacking!((*result.p).y, ...) + //#[ensures((unpacking!(result.p, unsafe { (*result.p).y }) == 42))] + //fn new_fail3() -> Self { + //let p2 = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let p = (p2 as *mut Pair); + //if !p2.is_null() { + //split!(*p); + //unsafe { (*p).y = 42; } + //} + //Self { p, v: 42 } + //} + + //#[ensures(result.v == 42)] + //// TODO: Make sure to distinguish unpacking!((*result.p), ...) from + //// unpacking!((*result.p).y, ...) + //#[ensures((unpacking!((*result.p).y, unsafe { (*result.p).y }) == 42))] + //fn new_fail4() -> Self { + //let p2 = unsafe { + //alloc(std::mem::size_of::(), std::mem::align_of::()) + //}; + //let p = (p2 as *mut Pair); + //if !p2.is_null() { + //split!(*p); + //unsafe { (*p).y = 42; } + //} + //Self { p, v: 42 } + //} + // #[pure] + // fn value(&self) -> i32 { + // if self.p2.is_null() { + // 0 + // } else { + // unsafe { *self.p2 } + // } + // } +} + +// #[structural_invariant( +// !self.p2.is_null() ==> ( +// own!(*self.p2) && +// raw_dealloc!(*self.p2, std::mem::size_of::(), std::mem::align_of::()) && +// unsafe { *self.p2 } == self.v +// ) +// )] +// struct T6 { +// v: i32, +// p2: *mut i32, +// } + +// impl T6 { +// // #[ensures(result.v == 42)] +// // #[ensures(!result.p2.is_null() ==> (unsafe { *result.p2 } == 42))] +// // fn new() -> Self { +// // let p2 = unsafe { +// // alloc(std::mem::size_of::(), std::mem::align_of::()) +// // }; +// // if !p2.is_null() { +// // unsafe { *(p2 as *mut i32) = 42; } +// // } +// // Self { p2: (p2 as *mut i32), v: 42 } +// // } +// #[ensures(result.v == 42)] +// #[ensures(unsafe { *result.p2 } == 42)] //~ ERROR: Permissions +// fn new_fail() -> Self { +// let p2 = unsafe { +// alloc(std::mem::size_of::(), std::mem::align_of::()) +// }; +// if !p2.is_null() { +// unsafe { *(p2 as *mut i32) = 42; } +// } +// Self { p2: (p2 as *mut i32), v: 42 } +// } +// // #[pure] +// // fn value(&self) -> i32 { +// // if self.p2.is_null() { +// // 0 +// // } else { +// // unsafe { *self.p2 } +// // } +// // } +// } + +#[trusted] +#[requires(align > 0)] +#[ensures(!result.is_null() ==> ( + raw!(*result, size) && + raw_dealloc!(*result, size, align) +))] +// https://doc.rust-lang.org/alloc/alloc/fn.alloc.html +unsafe fn alloc(size: usize, align: usize) -> *mut u8 { + unimplemented!(); +} + +#[trusted] +#[requires( + raw!(*ptr, size) && + raw_dealloc!(*ptr, size, align) +)] +unsafe fn dealloc(ptr: *mut u8, size: usize, align: usize) { + unimplemented!(); +} + +struct Pair { + x: i32, + y: i32, +} + +fn main() {} diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/invariants2.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/invariants2.rs new file mode 100644 index 00000000000..e6f09180adf --- /dev/null +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/invariants2.rs @@ -0,0 +1,38 @@ +// compile-flags: -Punsafe_core_proof=true -Penable_type_invariants=true -Pverify_specifications_with_core_proof=true +// +// These tests need core-proof for specs. + +use prusti_contracts::*; + +struct T1 { + f: i32, +} + +fn test01(mut a: T1, mut b: T1) { + let z = b.f; + let x = std::ptr::addr_of_mut!(a); + let y = std::ptr::addr_of_mut!(b); + unpack!(*x); + unpack!((*x).f); + unsafe { (*x).f = 4; } + pack!(*x); + restore!(*x, a); + restore!(*y, b); + assert!(a.f == 4); + assert!(z == b.f); +} + +fn test02(mut a: T1, mut b: T1) { + let z = b.f; + let x = std::ptr::addr_of_mut!(a); + let y = std::ptr::addr_of_mut!(b); + unpack!(*x); + unpack!((*x).f); + unsafe { (*x).f = 4; } + pack!(*x); + restore!(*x, a); + restore!(*y, b); + assert!(a.f == 5); //~ ERROR: the asserted expression might not hold +} + +fn main() {} diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/lifetimes/simple.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/lifetimes/simple.rs index 3f3dbaf4d6a..06938634564 100644 --- a/prusti-tests/tests/verify_overflow/fail/core_proof/lifetimes/simple.rs +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/lifetimes/simple.rs @@ -9,6 +9,7 @@ pub fn mutable_borrow() { *x = 2; assert!(*x == 2); } + pub fn mutable_borrow_assert_false() { let mut a = 4; let x = &mut a; @@ -16,6 +17,21 @@ pub fn mutable_borrow_assert_false() { assert!(*x == 4); //~ ERROR: the asserted expression might not hold } +pub fn mutable_borrow_2() { + let mut a = 4; + let x = &mut a; + *x = 2; + assert!(*x == 2); + assert!(a == 2); +} + +pub fn mutable_borrow_2_assert_false() { + let mut a = 4; + let x = &mut a; + *x = 2; + assert!(a == 4); //~ ERROR: the asserted expression might not hold +} + pub fn mutable_reborrow() { let mut a = 4; let mut x = &mut a; @@ -23,6 +39,7 @@ pub fn mutable_reborrow() { *y = 3; assert!(*y == 3); } + pub fn mutable_reborrow_assert_false() { let mut a = 4; let mut x = &mut a; @@ -31,12 +48,31 @@ pub fn mutable_reborrow_assert_false() { assert!(*y == 4); //~ ERROR: the asserted expression might not hold } +pub fn mutable_reborrow_2() { + let mut a = 4; + let mut x = &mut a; + let y = &mut (*x); + *y = 3; + assert!(*y == 3); + assert!(a == 3); +} + +pub fn mutable_reborrow_2_assert_false() { + let mut a = 4; + let mut x = &mut a; + let y = &mut (*x); + *y = 3; + assert!(*y == 3); + assert!(a == 4); //~ ERROR: the asserted expression might not hold +} + pub fn shared_borrow() { let mut a = 4; let x = &a; let y = &a; assert!(*y == 4); } + pub fn shared_borrow_assert_false() { let mut a = 4; let x = &a; @@ -51,6 +87,7 @@ pub fn shared_reborrow() { let z = &(*x); assert!(*z == 4); } + pub fn shared_reborrow_assert_false() { let mut a = 4; let x = &a; @@ -65,6 +102,7 @@ pub fn simple_references() { let mut c = &mut b; let mut d = &mut c; } + pub fn simple_references_assert_false() { let mut a = 4; let mut b = &mut a; diff --git a/prusti-tests/tests/verify_overflow/fail/core_proof/pointers.rs b/prusti-tests/tests/verify_overflow/fail/core_proof/pointers.rs index adfb04fd917..014c8fae2a4 100644 --- a/prusti-tests/tests/verify_overflow/fail/core_proof/pointers.rs +++ b/prusti-tests/tests/verify_overflow/fail/core_proof/pointers.rs @@ -10,18 +10,22 @@ use prusti_contracts::*; fn test1() { let a = 4u32; - let _x = std::ptr::addr_of!(a); + let x = std::ptr::addr_of!(a); + restore!(*x, a); } fn test2() { let mut a = 4u32; - let _x = std::ptr::addr_of_mut!(a); + let x = std::ptr::addr_of_mut!(a); + restore!(*x, a); } fn test3() { let a = 4u32; let x = std::ptr::addr_of!(a); + restore!(*x, a); let y = std::ptr::addr_of!(a); + restore!(*y, a); assert!(x == y); } @@ -29,7 +33,9 @@ fn test4() { let a = 4u32; let b = 4u32; let x = std::ptr::addr_of!(a); + restore!(*x, a); let y = std::ptr::addr_of!(b); + restore!(*y, b); assert!(x == y); //~ ERROR } @@ -37,7 +43,9 @@ fn test5() { let a = 4u32; let b = 4u32; let x = std::ptr::addr_of!(a); + restore!(*x, a); let y = std::ptr::addr_of!(b); + restore!(*y, b); assert!(x != y); //~ ERROR } @@ -45,7 +53,9 @@ fn test6() { let a = 4u32; let b = 4u32; let x = std::ptr::addr_of!(a); + restore!(*x, a); let y = std::ptr::addr_of!(b); + restore!(*y, b); assert!(!(x == y)); //~ ERROR } @@ -64,6 +74,8 @@ fn test7() { let x = std::ptr::addr_of!(a); let y = std::ptr::addr_of!(c.f.g); assert!(x != y); //~ ERROR + restore!(*x, a); + restore!(*y, c.f.g); } fn test8() { @@ -73,6 +85,8 @@ fn test8() { let x = std::ptr::addr_of!(a); let y = std::ptr::addr_of!(c.f.g); assert!(!(x == y)); //~ ERROR + restore!(*x, a); + restore!(*y, c.f.g); } fn test9() { @@ -82,6 +96,8 @@ fn test9() { let x = std::ptr::addr_of!(a); let y = std::ptr::addr_of!(c.f.g); assert!(x == y); //~ ERROR + restore!(*x, a); + restore!(*y, c.f.g); } fn main() {} diff --git a/prusti-tests/tests/verify_overflow/pass/core_proof/custom_heap_encoding/performance_test.rs b/prusti-tests/tests/verify_overflow/pass/core_proof/custom_heap_encoding/performance_test.rs new file mode 100644 index 00000000000..30184e1bfbd --- /dev/null +++ b/prusti-tests/tests/verify_overflow/pass/core_proof/custom_heap_encoding/performance_test.rs @@ -0,0 +1,972 @@ +// compile-flags: -Punsafe_core_proof=true -Pverification_deadline=120 + +use prusti_contracts::*; + +struct T {} + +//fn test001() { + //let a = T{}; + //let a = a; +//} + +//fn test002() { + //let a = T{}; + //let a = a; + //let a = a; +//} + +//fn test003() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test004() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test005() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test006() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test007() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test008() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test009() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test010() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test011() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test012() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test013() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test014() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test015() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test016() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test017() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test018() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test019() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test020() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test021() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test022() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test023() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test024() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test025() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} +//fn test030() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test040() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test050() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test060() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test070() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test080() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +//fn test090() { + //let a = T{}; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; + //let a = a; +//} + +fn test100() { + let a = T{}; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; + let a = a; +} + +#[trusted] +fn main() {} + diff --git a/prusti-tests/tests/verify_overflow/pass/core_proof/pointers.rs b/prusti-tests/tests/verify_overflow/pass/core_proof/pointers.rs index 842b551257f..bfcbc5b01e3 100644 --- a/prusti-tests/tests/verify_overflow/pass/core_proof/pointers.rs +++ b/prusti-tests/tests/verify_overflow/pass/core_proof/pointers.rs @@ -4,18 +4,22 @@ use prusti_contracts::*; fn test1() { let a = 4u32; - let _x = std::ptr::addr_of!(a); + let x = std::ptr::addr_of!(a); + restore!(*x, a); } fn test2() { let mut a = 4u32; - let _x = std::ptr::addr_of_mut!(a); + let x = std::ptr::addr_of_mut!(a); + restore!(*x, a); } fn test3() { let a = 4u32; let x = std::ptr::addr_of!(a); + restore!(*x, a); let y = std::ptr::addr_of!(a); + restore!(*y, a); assert!(x == y); } diff --git a/prusti-tests/tests/verify_overflow/pass/core_proof/types.rs b/prusti-tests/tests/verify_overflow/pass/core_proof/types.rs index 180a246a332..2b53cb97d63 100644 --- a/prusti-tests/tests/verify_overflow/pass/core_proof/types.rs +++ b/prusti-tests/tests/verify_overflow/pass/core_proof/types.rs @@ -1,4 +1,5 @@ // compile-flags: -Punsafe_core_proof=true -Pverify_types=true +// -Puse_snapshot_parameters_in_predicates=true use prusti_contracts::*; @@ -16,117 +17,117 @@ struct T3 { struct T4<'a> { f: &'a mut T1, - g: &'a T1, + //g: &'a T1, } struct T5<'a, 'b, 'c> { f: &'a mut T1, g: &'c mut T4<'b>, - h: &'a T1, - i: &'c T4<'b>, -} - -struct T6<'a, 'b, 'c> { - f: &'a mut T1, - g: &'a mut &'b mut T1, - h: &'a mut &'b mut &'c mut T1, - i: &'a T1, - j: &'a &'b T1, - k: &'a &'b &'c T1, -} - -struct T7<'a> { - f: &'a mut T1, - g: &'a mut &'a mut T1, - h: &'a mut &'a mut &'a mut T1, - i: &'a T1, - j: &'a &'a T1, - k: &'a &'a &'a T1, -} - -struct T8 { - f: [i32; 10], - g: [[T2; 10]; 10], - h: [[[T2; 10]; 10]; 10], -} - -enum T9 { - F([i32; 10]), - G([[T2; 10]; 10]), - H([[[T2; 10]; 10]; 10]), -} - -struct T10 { - f: [T8; 10], - g: [[T8; 10]; 10], - h: [[[T9; 10]; 10]; 10], -} - -struct T11<'a, 'b, 'c, 'd> { - f: &'a mut [&'b mut T8; 10], - g: &'a mut [&'b mut [&'c mut T9; 10]; 10], - h: &'a mut [&'b mut [&'c mut [&'d mut T9; 10]; 10]; 10], - i: &'a [&'b T8; 10], - j: &'a [&'b [&'c T9; 10]; 10], - k: &'a [&'b [&'c [&'d T9; 10]; 10]; 10], -} - -struct T12<'a> { - f: &'a mut [&'a mut T9; 10], - g: &'a mut [&'a mut [&'a mut T9; 10]; 10], - h: &'a mut [&'a mut [&'a mut [&'a mut T9; 10]; 10]; 10], - i: &'a [&'a T9; 10], - j: &'a [&'a [&'a T9; 10]; 10], - k: &'a [&'a [&'a [&'a T9; 10]; 10]; 10], -} - -struct T13 { - f: (), - g: (T1, T2, T3<(T2, T1)>), -} - -struct T14<'a, 'b, 'c> { - f: &'a mut (), - g: &'a mut (&'b mut T1, &'c mut T2), - i: &'a (), - j: &'a (&'b T1, &'c T2), -} + //h: &'a T1, + //i: &'c T4<'b>, +} + +//struct T6<'a, 'b, 'c> { + //f: &'a mut T1, + //g: &'a mut &'b mut T1, + //h: &'a mut &'b mut &'c mut T1, + //i: &'a T1, + //j: &'a &'b T1, + //k: &'a &'b &'c T1, +//} + +//struct T7<'a> { + //f: &'a mut T1, + //g: &'a mut &'a mut T1, + //h: &'a mut &'a mut &'a mut T1, + //i: &'a T1, + //j: &'a &'a T1, + //k: &'a &'a &'a T1, +//} + +//struct T8 { + //f: [i32; 10], + //g: [[T2; 10]; 10], + //h: [[[T2; 10]; 10]; 10], +//} + +//enum T9 { + //F([i32; 10]), + //G([[T2; 10]; 10]), + //H([[[T2; 10]; 10]; 10]), +//} + +//struct T10 { + //f: [T8; 10], + //g: [[T8; 10]; 10], + //h: [[[T9; 10]; 10]; 10], +//} + +//struct T11<'a, 'b, 'c, 'd> { + //f: &'a mut [&'b mut T8; 10], + //g: &'a mut [&'b mut [&'c mut T9; 10]; 10], + //h: &'a mut [&'b mut [&'c mut [&'d mut T9; 10]; 10]; 10], + //i: &'a [&'b T8; 10], + //j: &'a [&'b [&'c T9; 10]; 10], + //k: &'a [&'b [&'c [&'d T9; 10]; 10]; 10], +//} + +//struct T12<'a> { + //f: &'a mut [&'a mut T9; 10], + //g: &'a mut [&'a mut [&'a mut T9; 10]; 10], + //h: &'a mut [&'a mut [&'a mut [&'a mut T9; 10]; 10]; 10], + //i: &'a [&'a T9; 10], + //j: &'a [&'a [&'a T9; 10]; 10], + //k: &'a [&'a [&'a [&'a T9; 10]; 10]; 10], +//} + +//struct T13 { + //f: (), + //g: (T1, T2, T3<(T2, T1)>), +//} + +//struct T14<'a, 'b, 'c> { + //f: &'a mut (), + //g: &'a mut (&'b mut T1, &'c mut T2), + //i: &'a (), + //j: &'a (&'b T1, &'c T2), +//} struct T15<'a> { f: &'a mut [T1], g: &'a mut [T1; 10], - i: &'a [T1], - j: &'a [T1; 10], -} - -struct T16<'a, 'b> { - f: &'a mut [&'b mut T1], - g: &'a mut [&'b mut T1; 10], - i: &'a [&'b T1], - j: &'a [&'b T1; 10], -} - -enum T17<'a, 'b> { - Left (&'a mut [T1; 10]), - Right (&'b mut [T2; 10]), - SharedLeft (&'a [T1; 10]), - SharedRight (&'b [T2; 10]), -} - -enum T18<'a> { - Left(&'a mut [T1]), - Right([T2; 10]), -} - -union T19<'a, 'b> { - f: &'a mut [&'b mut T1], - g: &'a mut [&'b mut T1; 10], -} - -struct T20 { - f: *mut u8, - g: *mut [u8], -} + //i: &'a [T1], + //j: &'a [T1; 10], +} + +//struct T16<'a, 'b> { + //f: &'a mut [&'b mut T1], + //g: &'a mut [&'b mut T1; 10], + //i: &'a [&'b T1], + //j: &'a [&'b T1; 10], +//} + +//enum T17<'a, 'b> { + //Left (&'a mut [T1; 10]), + //Right (&'b mut [T2; 10]), + //SharedLeft (&'a [T1; 10]), + //SharedRight (&'b [T2; 10]), +//} + +//enum T18<'a> { + //Left(&'a mut [T1]), + //Right([T2; 10]), +//} + +//union T19<'a, 'b> { + //f: &'a mut [&'b mut T1], + //g: &'a mut [&'b mut T1; 10], +//} + +//struct T20 { + //f: *mut u8, + //g: *mut [u8], +//} #[trusted] fn main() {} diff --git a/prusti-utils/src/config.rs b/prusti-utils/src/config.rs index c2f11817169..e56578b93ef 100644 --- a/prusti-utils/src/config.rs +++ b/prusti-utils/src/config.rs @@ -102,7 +102,9 @@ lazy_static::lazy_static! { settings.set_default("quiet", false).unwrap(); settings.set_default("assert_timeout", 10_000).unwrap(); settings.set_default("smt_qi_eager_threshold", 1000).unwrap(); - settings.set_default("use_more_complete_exhale", true).unwrap(); + settings.set_default("smt_use_nonlinear_arithmetic_solver", false).unwrap(); + settings.set_default("use_more_complete_exhale", false).unwrap(); + settings.set_default("use_carbon_qps", true).unwrap(); settings.set_default("skip_unsupported_features", false).unwrap(); settings.set_default("internal_errors_as_warnings", false).unwrap(); settings.set_default("allow_unreachable_unsupported_code", false).unwrap(); @@ -116,13 +118,20 @@ lazy_static::lazy_static! { settings.set_default("enable_purification_optimization", false).unwrap(); // settings.set_default("enable_manual_axiomatization", false).unwrap(); settings.set_default("unsafe_core_proof", false).unwrap(); + settings.set_default("custom_heap_encoding", false).unwrap(); + settings.set_default("trace_with_symbolic_execution", false).unwrap(); + settings.set_default("purify_with_symbolic_execution", false).unwrap(); + settings.set_default("expand_quantifiers", false).unwrap(); + settings.set_default("report_symbolic_execution_failures", false).unwrap(); + settings.set_default("report_symbolic_execution_purification", false).unwrap(); settings.set_default("verify_core_proof", true).unwrap(); settings.set_default("verify_specifications", true).unwrap(); settings.set_default("verify_types", false).unwrap(); - settings.set_default("verify_specifications_with_core_proof", false).unwrap(); + settings.set_default("verify_specifications_with_core_proof", true).unwrap(); settings.set_default("verify_specifications_backend", "Silicon").unwrap(); settings.set_default("use_eval_axioms", true).unwrap(); settings.set_default("inline_caller_for", false).unwrap(); + settings.set_default("use_snapshot_parameters_in_predicates", false).unwrap(); settings.set_default("check_no_drops", false).unwrap(); settings.set_default("enable_type_invariants", false).unwrap(); settings.set_default("use_new_encoder", true).unwrap(); @@ -494,6 +503,12 @@ pub fn smt_qi_eager_threshold() -> u64 { read_setting("smt_qi_eager_threshold") } +/// Disable or enable the non-linear arithmetic solver by setting Z3 +/// `smt.arith.nl` and `smt.arith.nl.gb` values to the given one. +pub fn smt_use_nonlinear_arithmetic_solver() -> bool { + read_setting("smt_use_nonlinear_arithmetic_solver") +} + /// Maximum time (in milliseconds) for the verifier to spend on checks. /// Set to None uses the verifier's default value. Maps to the verifier command-line /// argument `--checkTimeout`. @@ -507,8 +522,28 @@ pub fn check_timeout() -> Option { /// See [`consolidate`](https://github.com/viperproject/silicon/blob/f48de7f6e2d90d9020812869c713a5d3e2035995/src/main/scala/rules/StateConsolidator.scala#L29-L46). /// Equivalent to the verifier command-line argument /// `--enableMoreCompleteExhale`. +/// +/// Note: This option conflicts with `use_carbon_qps`. pub fn use_more_complete_exhale() -> bool { - read_setting("use_more_complete_exhale") + let result = read_setting("use_more_complete_exhale"); + assert!( + !(result && read_setting::("use_carbon_qps")), + "use_more_complete_exhale and use_carbon_qps are mutually exclusive" + ); + result +} + +/// When enabled, a Carbon QPs version of Silicon is used. Equivalent to the +/// Silicon command-line argument `--carbonQPs`. +/// +/// Note: This option conflicts with `use_more_complete_exhale`. +pub fn use_carbon_qps() -> bool { + let result = read_setting("use_carbon_qps"); + assert!( + !(result && read_setting::("use_more_complete_exhale")), + "use_more_complete_exhale and use_carbon_qps are mutually exclusive" + ); + result } /// When enabled, prints the items collected for verification. @@ -864,6 +899,62 @@ pub fn unsafe_core_proof() -> bool { read_setting("unsafe_core_proof") } +/// Use symbolic execution to split the procedure into traces that are verified +/// separately. +/// +/// **Note:** This option is taken into account only when `unsafe_core_proof` is +/// true. +pub fn trace_with_symbolic_execution() -> bool { + read_setting("trace_with_symbolic_execution") || purify_with_symbolic_execution() +} + +/// Use symbolic execution based purification. +/// +/// **Note:** This option is taken into account only when `unsafe_core_proof` is +/// true. +/// +/// **Note:** This option automatically enables +/// `trace_with_symbolic_execution`. +pub fn purify_with_symbolic_execution() -> bool { + read_setting("purify_with_symbolic_execution") +} + +/// Whether to expand the asserted quantifiers (skolemize them out). +/// +/// **Note:** This option is taken into account only when `unsafe_core_proof` +/// is true. +pub fn expand_quantifiers() -> bool { + read_setting("expand_quantifiers") +} + +/// Report an error when failing to purify a predicate in symbolic execution. +/// +/// **Note:** This option requires `purify_with_symbolic_execution` to be +/// enabled. +pub fn report_symbolic_execution_failures() -> bool { + let result: bool = read_setting("report_symbolic_execution_failures"); + assert!(!result || purify_with_symbolic_execution()); + result +} + +/// Add comments at the places where predicates were purified by the symbolic +/// execution. +/// +/// **Note:** This option requires `purify_with_symbolic_execution` to be +/// enabled. +pub fn report_symbolic_execution_purification() -> bool { + assert!(purify_with_symbolic_execution() || trace_with_symbolic_execution()); + read_setting("report_symbolic_execution_purification") +} + +/// Use custom heap encoding. +/// +/// **Note:** This option is taken into account only when `unsafe_core_proof` is +/// true. +pub fn custom_heap_encoding() -> bool { + read_setting("custom_heap_encoding") +} + /// Whether the core proof (memory safety) should be verified. /// /// **Note:** This option is taken into account only when `unsafe_core_proof` is @@ -918,6 +1009,14 @@ pub fn inline_caller_for() -> bool { read_setting("inline_caller_for") } +/// Whether to make the snapshot, an explicit parameter of the predicate. +/// +/// **Note:** This option is taken into account only when `unsafe_core_proof` is +/// true. +pub fn use_snapshot_parameters_in_predicates() -> bool { + read_setting("use_snapshot_parameters_in_predicates") +} + /// When enabled, replaces calls to the drop function with `assert false`. /// /// **Note:** This option is used only for testing. diff --git a/prusti-viper/Cargo.toml b/prusti-viper/Cargo.toml index d21e4c5d9ab..1a37adabfdb 100644 --- a/prusti-viper/Cargo.toml +++ b/prusti-viper/Cargo.toml @@ -27,6 +27,7 @@ rustc-hash = "1.1.0" derive_more = "0.99.16" itertools = "0.10.3" once_cell = "1.17.1" +egg = "0.9.2" [dev-dependencies] lazy_static = "1.4" diff --git a/prusti-viper/src/encoder/counterexamples/counterexample_translation.rs b/prusti-viper/src/encoder/counterexamples/counterexample_translation.rs index aff35fdc22f..9b2e7515ecb 100644 --- a/prusti-viper/src/encoder/counterexamples/counterexample_translation.rs +++ b/prusti-viper/src/encoder/counterexamples/counterexample_translation.rs @@ -16,6 +16,7 @@ use prusti_rustc_interface::{ span::Span, }; use rustc_hash::FxHashMap; +use vir_crate::common::builtin_constants::DISCRIMINANT_FIELD_NAME; use std::iter; use viper::silicon_counterexample::*; use DiscriminantsStateInterface; @@ -357,7 +358,7 @@ impl<'ce, 'tcx> CounterexampleTranslator<'ce, 'tcx> { let mut field_entries = vec![]; let mut variant = None; - let mut opt_discriminant = self.translate_int(map.get("discriminant")); + let mut opt_discriminant = self.translate_int(map.get(DISCRIMINANT_FIELD_NAME)); //need to find a discriminant to do something if opt_discriminant.is_none() { //try to find disc in the associated local variable diff --git a/prusti-viper/src/encoder/counterexamples/interface.rs b/prusti-viper/src/encoder/counterexamples/interface.rs index 183cd20cc55..a0958fc3d0c 100644 --- a/prusti-viper/src/encoder/counterexamples/interface.rs +++ b/prusti-viper/src/encoder/counterexamples/interface.rs @@ -1,13 +1,11 @@ +use prusti_interface::data::ProcedureDefId; use rustc_hash::FxHashMap; -use vir_crate::{ - common::check_mode::CheckMode, - low::{self as vir_low}, -}; +use vir_crate::low::{self as vir_low}; #[derive(Default)] pub(crate) struct MirProcedureMapping { //Map of all variables assigned in this basic block - mapping: FxHashMap>, + mapping: FxHashMap>, } #[derive(Debug)] @@ -24,7 +22,7 @@ impl MirProcedureMapping { procedure .basic_blocks .iter() - .map(|basic_block| { + .map(|(label, basic_block)| { let mut stmts = Vec::new(); for statement in &basic_block.statements { @@ -51,7 +49,7 @@ impl MirProcedureMapping { } }; BasicBlock { - label: basic_block.label.name.clone(), + label: label.name.clone(), successor, stmts, } @@ -84,27 +82,32 @@ impl MirProcedureMapping { } pub(crate) trait MirProcedureMappingInterface { - fn add_mapping(&mut self, program: &vir_low::Program); - fn get_mapping(&self, proc_name: String) -> Option<&Vec>; + fn add_mapping(&mut self, proc_def_id: ProcedureDefId, program: &vir_low::Program); + fn get_mapping(&self, def_id: ProcedureDefId) -> Option<&Vec>; } impl<'v, 'tcx: 'v> MirProcedureMappingInterface for super::super::Encoder<'v, 'tcx> { - fn add_mapping(&mut self, program: &vir_low::Program) { + fn add_mapping(&mut self, proc_def_id: ProcedureDefId, program: &vir_low::Program) { if let Some(vir_low_procedure) = program.procedures.first() { //at the moment a counterexample is only produced for the specifications-poof - if matches!(program.check_mode, CheckMode::Specifications) - || matches!(program.check_mode, CheckMode::Both) + if program.check_mode.check_specifications() + // matches!(program.check_mode, CheckMode::Specifications) + // || matches!(program.check_mode, CheckMode::Both) { let procedure_new = self .mir_procedure_mapping .translate_procedure_decl(vir_low_procedure); + // FIXME: `proc_def_id` is not unique. We should use program + // + procedure name here instead. However, this requires + // refactoring all code to include this information for all + // positions. self.mir_procedure_mapping .mapping - .insert(program.name.clone(), procedure_new); + .insert(proc_def_id, procedure_new); } } } - fn get_mapping(&self, proc_name: String) -> Option<&Vec> { - self.mir_procedure_mapping.mapping.get(&proc_name) + fn get_mapping(&self, def_id: ProcedureDefId) -> Option<&Vec> { + self.mir_procedure_mapping.mapping.get(&def_id) } } diff --git a/prusti-viper/src/encoder/counterexamples/mapping.rs b/prusti-viper/src/encoder/counterexamples/mapping.rs index eb3df45a7be..7be05f0bc97 100644 --- a/prusti-viper/src/encoder/counterexamples/mapping.rs +++ b/prusti-viper/src/encoder/counterexamples/mapping.rs @@ -52,8 +52,7 @@ impl<'ce, 'tcx, 'v> VarMappingInterface for super::counterexample_translation_refactored::CounterexampleTranslator<'ce, 'tcx, 'v> { fn create_mapping(&mut self, proc_def_id: ProcedureDefId, encoder: &Encoder) { - let name = encoder.env().name.get_absolute_item_name(proc_def_id); - if let Some(mir_procedure_mapping) = encoder.get_mapping(name) { + if let Some(mir_procedure_mapping) = encoder.get_mapping(proc_def_id) { for basic_block in mir_procedure_mapping { let label = &basic_block.label; diff --git a/prusti-viper/src/encoder/encoder.rs b/prusti-viper/src/encoder/encoder.rs index c1f0c4fcbed..481bd98731c 100644 --- a/prusti-viper/src/encoder/encoder.rs +++ b/prusti-viper/src/encoder/encoder.rs @@ -242,12 +242,17 @@ impl<'v, 'tcx> Encoder<'v, 'tcx> { pub fn get_core_proof_programs(&mut self) -> Vec { if config::counterexample() && config::unsafe_core_proof(){ self.take_core_proof_programs().into_iter().map( - | program | { - self.add_mapping(&program); + |( def_id, program )| { + if let Some(def_id) = def_id { + self.add_mapping(def_id, &program); + } prusti_common::vir::program::Program::Low(program) }).collect() } else { - self.take_core_proof_programs().into_iter().map(prusti_common::vir::program::Program::Low).collect() + self.take_core_proof_programs().into_iter().map( + |(_, program)| { + prusti_common::vir::program::Program::Low(program) + }).collect() } } @@ -698,6 +703,19 @@ impl<'v, 'tcx> Encoder<'v, 'tcx> { } } + /// Returns true iff `def_id` is a function that uses raw pointers. + fn is_internally_unsafe_function(&self, def_id: ProcedureDefId) -> bool { + let mir = self.env.body.borrow_impure_fn_body_identity(def_id.expect_local()); + for local_decl in &mir.local_decls { + if let prusti_rustc_interface::middle::ty::TyKind::RawPtr(_) = + local_decl.ty.kind() + { + return true; + } + } + false + } + pub fn process_encoding_queue(&mut self) { if let Err(error) = self.initialize() { panic!("The initialization of the encoder failed with the error: {error:?}"); @@ -715,26 +733,34 @@ impl<'v, 'tcx> Encoder<'v, 'tcx> { if config::unsafe_core_proof() { if self.env.query.is_unsafe_function(proc_def_id) { - if let Err(error) = self.encode_lifetimes_core_proof(proc_def_id, CheckMode::Both) { + if let Err(error) = self.encode_lifetimes_core_proof(proc_def_id, CheckMode::UnsafeSafety) { self.register_encoding_error(error); - debug!("Error encoding function: {:?} {}", proc_def_id, CheckMode::Both); + debug!("Error encoding function: {:?} {}", proc_def_id, CheckMode::UnsafeSafety); + } + } else if config::verify_specifications_with_core_proof() || self.is_internally_unsafe_function(proc_def_id) { + if config::verify_core_proof() { + if let Err(error) = self.encode_lifetimes_core_proof(proc_def_id, CheckMode::MemorySafety) { + self.register_encoding_error(error); + debug!("Error encoding function: {:?} {}", proc_def_id, CheckMode::MemorySafety); + } + } + if config::verify_specifications() { + if let Err(error) = self.encode_lifetimes_core_proof(proc_def_id, CheckMode::MemorySafetyWithFunctional) { + self.register_encoding_error(error); + debug!("Error encoding function: {:?} {}", proc_def_id, CheckMode::MemorySafetyWithFunctional); + } } } else { if config::verify_core_proof() { - if let Err(error) = self.encode_lifetimes_core_proof(proc_def_id, CheckMode::CoreProof) { + if let Err(error) = self.encode_lifetimes_core_proof(proc_def_id, CheckMode::PurificationSoudness) { self.register_encoding_error(error); - debug!("Error encoding function: {:?} {}", proc_def_id, CheckMode::CoreProof); + debug!("Error encoding function: {:?} {}", proc_def_id, CheckMode::PurificationSoudness); } } if config::verify_specifications() { - let check_mode = if config::verify_specifications_with_core_proof() { - CheckMode::Both - } else { - CheckMode::Specifications - }; - if let Err(error) = self.encode_lifetimes_core_proof(proc_def_id, check_mode) { + if let Err(error) = self.encode_lifetimes_core_proof(proc_def_id, CheckMode::PurificationFunctional) { self.register_encoding_error(error); - debug!("Error encoding function: {:?} {}", proc_def_id, check_mode); + debug!("Error encoding function: {:?} {}", proc_def_id, CheckMode::PurificationFunctional); } } } @@ -792,9 +818,9 @@ impl<'v, 'tcx> Encoder<'v, 'tcx> { } EncodingTask::Type { ty } => { if config::unsafe_core_proof() && config::verify_core_proof() && config::verify_types() { - if let Err(error) = self.encode_core_proof_for_type(ty, CheckMode::CoreProof) { + if let Err(error) = self.encode_core_proof_for_type(ty, CheckMode::MemorySafety) { self.register_encoding_error(error); - debug!("Error encoding type: {:?} {}", ty, CheckMode::CoreProof); + debug!("Error encoding type: {:?} {}", ty, CheckMode::MemorySafety); } } } diff --git a/prusti-viper/src/encoder/errors/error_manager.rs b/prusti-viper/src/encoder/errors/error_manager.rs index b3cdd293bb2..139a1b68663 100644 --- a/prusti-viper/src/encoder/errors/error_manager.rs +++ b/prusti-viper/src/encoder/errors/error_manager.rs @@ -41,6 +41,7 @@ pub enum PanicCause { pub enum BuiltinMethodKind { WriteConstant, MovePlace, + CopyPlace, IntoMemoryBlock, SplitMemoryBlock, JoinMemoryBlock, @@ -48,6 +49,7 @@ pub enum BuiltinMethodKind { ChangeUniqueRefPlace, DuplicateFracRef, Assign, + RestoreRawBorrowed, } /// In case of verification error, this enum will contain additional information @@ -177,6 +179,10 @@ pub enum ErrorCtxt { CloseMutRef, /// Failed to encode CloseFracRef CloseFracRef, + /// Closing a reference failed. + CloseRef, + /// Opening a reference failed. + OpenRef, /// Failed to set an active variant of an union. SetEnumVariant, /// A user assumption raised an error @@ -184,6 +190,31 @@ pub enum ErrorCtxt { /// The state that fold-unfold algorithm deduced as unreachable, is actually /// reachable. UnreachableFoldingState, + /// A user-specified pack operation failed. + Pack, + /// A user-specified unpack operation failed. + Unpack, + /// A user-specified forget-initialization operation failed. + ForgetInitialization, + /// Restore a place borrowed via raw pointer. + RestoreRawBorrowed, + /// An error in the definition of the type invariant. + TypeInvariantDefinition, + /// Pointer dereference in the postcondition is not framed by permissions. + /// + /// Note: This can also be reported when the underlying solver failing to + /// prove that the postcondition implies itself. + MethodPostconditionFraming, + /// An unexpected error when assuming false to end method postcondition + /// framing check. + UnexpectedAssumeEndMethodPostconditionFraming, + StashRange, + RestoreStashRange, + JoinRange, + SplitRange, + UnexpectedSpecificationExpression, + // /// Permission error when dereferencing a raw pointer. + // EnsureOwnedPredicate, } /// The error manager @@ -398,6 +429,7 @@ impl<'tcx> ErrorManager<'tcx> { .set_help("This might be a bug in the Rust compiler.") } + ("exhale.failed:assertion.false", ErrorCtxt::ExhaleMethodPrecondition) | ("assert.failed:assertion.false", ErrorCtxt::ExhaleMethodPrecondition) => { PrustiError::verification("precondition might not hold.", error_span) .set_failing_assertion(opt_cause_span) @@ -553,11 +585,19 @@ impl<'tcx> ErrorManager<'tcx> { .set_failing_assertion(opt_cause_span) } - ("assert.failed:assertion.false", ErrorCtxt::AssertMethodPostcondition) => { + ("assert.failed:assertion.false", ErrorCtxt::AssertMethodPostcondition) + |("exhale.failed:assertion.false", ErrorCtxt::AssertMethodPostcondition)=> { PrustiError::verification("postcondition might not hold.".to_string(), error_span) .push_primary_span(opt_cause_span) } + ("inhale.failed:insufficient.permission", ErrorCtxt::MethodPostconditionFraming) + | ("application.precondition:insufficient.permission", ErrorCtxt::MethodPostconditionFraming) => { + PrustiError::verification("the postcondition might not be self-framing.".to_string(), error_span) + .push_primary_span(opt_cause_span) + .set_help("This error might be also caused by prover failing to prove that the postcondition implies itself") + } + ( "assert.failed:assertion.false", ErrorCtxt::AssertMethodPostconditionTypeInvariants, @@ -693,6 +733,48 @@ impl<'tcx> ErrorManager<'tcx> { ) } + ("exhale.failed:insufficient.permission", ErrorCtxt::CopyPlace) | + ("exhale.failed:insufficient.permission", ErrorCtxt::MovePlace) | + ("call.precondition:insufficient.permission", ErrorCtxt::CopyPlace) | + ("call.precondition:insufficient.permission", ErrorCtxt::MovePlace) => { + PrustiError::verification( + "the accessed place may not be allocated or initialized".to_string(), + error_span + ).set_failing_assertion(opt_cause_span) + } + + ("exhale.failed:insufficient.permission", ErrorCtxt::WritePlace) | + ("call.precondition:insufficient.permission", ErrorCtxt::WritePlace) => { + PrustiError::verification( + "the accessed memory location must be allocated and uninitialized".to_string(), + error_span + ).set_failing_assertion(opt_cause_span) + } + + ("exhale.failed:insufficient.permission", ErrorCtxt::AssertMethodPostcondition) | + ("application.precondition:insufficient.permission", ErrorCtxt::AssertMethodPostcondition) | + ("application.precondition:insufficient.permission", ErrorCtxt::TypeInvariantDefinition) => { + PrustiError::verification( + "there might be insufficient permission to dereference a raw pointer".to_string(), + error_span + ).set_failing_assertion(opt_cause_span) + } + + ("call.precondition:assertion.false", ErrorCtxt::Assign | ErrorCtxt::CopyPlace) => { + PrustiError::verification( + "the type invariant of the constructed object might not hold".to_string(), + error_span + ).set_failing_assertion(opt_cause_span) + } + + ("call.precondition:insufficient.permission", ErrorCtxt::LifetimeEncoding) => { + PrustiError::verification( + "there might be insufficient permission to a lifetime token".to_string(), + error_span + ).set_failing_assertion(opt_cause_span) + .set_help("This could be caused by an unclosed reference.") + } + (full_err_id, ErrorCtxt::Unexpected) => { PrustiError::internal( format!( diff --git a/prusti-viper/src/encoder/foldunfold/requirements.rs b/prusti-viper/src/encoder/foldunfold/requirements.rs index bbb5c96d099..678fb2f3c69 100644 --- a/prusti-viper/src/encoder/foldunfold/requirements.rs +++ b/prusti-viper/src/encoder/foldunfold/requirements.rs @@ -12,7 +12,7 @@ use crate::encoder::foldunfold::{ use log::{debug, trace}; use rustc_hash::FxHashSet; use std::iter::FromIterator; -use vir_crate::polymorphic::{self as vir, PermAmount}; +use vir_crate::{polymorphic::{self as vir, PermAmount}, common::builtin_constants::DISCRIMINANT_FIELD_NAME}; pub trait RequiredStmtPermissionsGetter { /// Returns the permissions required for the statement to be well-defined. @@ -257,7 +257,7 @@ fn get_all_required_expr_permissions( let (base_reqs, base_discr) = get_all_required_expr_permissions(base, preds); reqs.extend(base_reqs); discr.extend(base_discr); - if field.name == "discriminant" { + if field.name == DISCRIMINANT_FIELD_NAME { debug_assert!(base.is_place()); discr.insert(base.clone()); } diff --git a/prusti-viper/src/encoder/high/builtin_functions/encoder.rs b/prusti-viper/src/encoder/high/builtin_functions/encoder.rs index 3bdd18aa1a5..440a61827a8 100644 --- a/prusti-viper/src/encoder/high/builtin_functions/encoder.rs +++ b/prusti-viper/src/encoder/high/builtin_functions/encoder.rs @@ -93,8 +93,8 @@ pub(super) fn encode_builtin_function_def(kind: BuiltinFunctionHighKind) -> vir_ BuiltinFunctionHighKind::SliceLen { slice_pred_type, .. } => { - let self_var = vir_high::VariableDecl::new("self", slice_pred_type); - let result_var = vir_high::VariableDecl::new("__result", vir_high::Type::MInt); + let self_var = vir_high::VariableDecl::self_variable(slice_pred_type); + let result_var = vir_high::VariableDecl::result_variable(vir_high::Type::MInt); vir_high::FunctionDecl { name: fn_name, diff --git a/prusti-viper/src/encoder/high/lower/expression.rs b/prusti-viper/src/encoder/high/lower/expression.rs index 3f53a8d5354..00ac3c1aae1 100644 --- a/prusti-viper/src/encoder/high/lower/expression.rs +++ b/prusti-viper/src/encoder/high/lower/expression.rs @@ -33,6 +33,9 @@ impl IntoPolymorphic for vir_high::Expression { vir_high::Expression::Deref(expression) => { vir_poly::Expr::Field(expression.lower(encoder)) } + vir_high::Expression::Final(expression) => { + unimplemented!("not supported lowering: {}", expression); + } vir_high::Expression::AddrOf(expression) => { vir_poly::Expr::AddrOf(expression.lower(encoder)) } @@ -75,6 +78,15 @@ impl IntoPolymorphic for vir_high::Expression { vir_high::Expression::Downcast(expression) => { vir_poly::Expr::Downcast(expression.lower(encoder)) } + vir_high::Expression::AccPredicate(_expression) => { + todo!() + } + vir_high::Expression::Unfolding(_expression) => { + todo!() + } + vir_high::Expression::EvalIn(_expression) => { + todo!() + } } } } diff --git a/prusti-viper/src/encoder/high/lower/ty.rs b/prusti-viper/src/encoder/high/lower/ty.rs index 17a72a0a56d..4478d6c2f12 100644 --- a/prusti-viper/src/encoder/high/lower/ty.rs +++ b/prusti-viper/src/encoder/high/lower/ty.rs @@ -15,6 +15,9 @@ impl IntoPolymorphic for vir_high::Type { vir_high::Type::MPerm => { unreachable!("Permissions are used only in the unsafe core proof") } + vir_high::Type::MByte | vir_high::Type::MBytes => { + unreachable!("Bytes are used only in the unsafe core proof") + } vir_high::Type::Bool => vir_poly::Type::typed_ref("bool"), vir_high::Type::Int(int) => vir_poly::Type::typed_ref(int.to_string().to_lowercase()), vir_high::Type::Sequence(ty) => vir_poly::Type::Seq(vir_poly::SeqType { diff --git a/prusti-viper/src/encoder/high/procedures/inference/mod.rs b/prusti-viper/src/encoder/high/procedures/inference/mod.rs index c624ab4e426..867a5fac10a 100644 --- a/prusti-viper/src/encoder/high/procedures/inference/mod.rs +++ b/prusti-viper/src/encoder/high/procedures/inference/mod.rs @@ -25,6 +25,7 @@ mod permission; mod semantics; mod state; mod visitor; +mod unfolding_expressions; pub(super) fn infer_shape_operations<'v, 'tcx: 'v>( encoder: &mut Encoder<'v, 'tcx>, diff --git a/prusti-viper/src/encoder/high/procedures/inference/permission.rs b/prusti-viper/src/encoder/high/procedures/inference/permission.rs index 31c88f8379f..b499a26e2f7 100644 --- a/prusti-viper/src/encoder/high/procedures/inference/permission.rs +++ b/prusti-viper/src/encoder/high/procedures/inference/permission.rs @@ -44,3 +44,13 @@ pub(in super::super) enum PermissionKind { MemoryBlock, Owned, } + +impl Permission { + pub(in super::super) fn place(&self) -> &vir_typed::Expression { + match self { + Permission::MemoryBlock(place) => place, + Permission::Owned(place) => place, + Permission::MutBorrowed(MutBorrowed { place, .. }) => place, + } + } +} diff --git a/prusti-viper/src/encoder/high/procedures/inference/semantics.rs b/prusti-viper/src/encoder/high/procedures/inference/semantics.rs index 2f800993b0e..6f31c1f078c 100644 --- a/prusti-viper/src/encoder/high/procedures/inference/semantics.rs +++ b/prusti-viper/src/encoder/high/procedures/inference/semantics.rs @@ -18,9 +18,30 @@ pub(in super::super) fn collect_permission_changes<'v, 'tcx>( &mut consumed_permissions, &mut produced_permissions, )?; + consumed_permissions.retain(|permission| !permission.place().is_behind_pointer_dereference()); + produced_permissions.retain(|permission| !permission.place().is_behind_pointer_dereference()); + // remove_after_pointer_deref(&mut consumed_permissions); + // remove_after_pointer_deref(&mut produced_permissions); Ok((consumed_permissions, produced_permissions)) } +// fn remove_after_pointer_deref(permissions: &mut Vec) { +// permissions.retain_mut(|permission| { +// match permission { +// Permission::MemoryBlock(place) => { +// !place.is_behind_pointer_dereference() +// } +// Permission::Owned(place) => { +// if let Some(pointer_place) = place.get_first_dereferenced_pointer() { +// *place = pointer_place.clone(); +// } +// true +// } +// Permission::MutBorrowed(_) => unreachable!(), +// } +// }); +// } + trait CollectPermissionChanges { #[allow(clippy::ptr_arg)] // Clippy false positive. fn collect<'v, 'tcx>( @@ -45,10 +66,16 @@ impl CollectPermissionChanges for vir_typed::Statement { vir_typed::Statement::OldLabel(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } - vir_typed::Statement::Inhale(statement) => { + vir_typed::Statement::InhalePredicate(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::ExhalePredicate(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } - vir_typed::Statement::Exhale(statement) => { + vir_typed::Statement::InhaleExpression(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::ExhaleExpression(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } vir_typed::Statement::Consume(statement) => { @@ -60,6 +87,9 @@ impl CollectPermissionChanges for vir_typed::Statement { vir_typed::Statement::GhostHavoc(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } + vir_typed::Statement::HeapHavoc(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } vir_typed::Statement::GhostAssign(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } @@ -93,12 +123,45 @@ impl CollectPermissionChanges for vir_typed::Statement { vir_typed::Statement::SetUnionVariant(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } + vir_typed::Statement::Pack(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::Unpack(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::Join(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::JoinRange(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::Split(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::SplitRange(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::StashRange(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::StashRangeRestore(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::ForgetInitialization(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } + vir_typed::Statement::RestoreRawBorrowed(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } vir_typed::Statement::NewLft(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } vir_typed::Statement::EndLft(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } + vir_typed::Statement::DeadReference(statement) => { + statement.collect(encoder, consumed_permissions, produced_permissions) + } vir_typed::Statement::DeadLifetime(statement) => { statement.collect(encoder, consumed_permissions, produced_permissions) } @@ -133,6 +196,17 @@ impl CollectPermissionChanges for vir_typed::Statement { } } +impl CollectPermissionChanges for vir_typed::HeapHavoc { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + Ok(()) + } +} + impl CollectPermissionChanges for vir_typed::GhostHavoc { fn collect<'v, 'tcx>( &self, @@ -193,14 +267,17 @@ fn extract_managed_predicate_place( vir_typed::Predicate::MemoryBlockStackDrop(_) | vir_typed::Predicate::LifetimeToken(_) | vir_typed::Predicate::MemoryBlockHeap(_) - | vir_typed::Predicate::MemoryBlockHeapDrop(_) => { + | vir_typed::Predicate::MemoryBlockHeapRange(_) + | vir_typed::Predicate::MemoryBlockHeapDrop(_) + | vir_typed::Predicate::OwnedRange(_) + | vir_typed::Predicate::OwnedSet(_) => { // Unmanaged predicates. Ok(None) } } } -impl CollectPermissionChanges for vir_typed::Inhale { +impl CollectPermissionChanges for vir_typed::InhalePredicate { fn collect<'v, 'tcx>( &self, _encoder: &mut Encoder<'v, 'tcx>, @@ -212,7 +289,7 @@ impl CollectPermissionChanges for vir_typed::Inhale { } } -impl CollectPermissionChanges for vir_typed::Exhale { +impl CollectPermissionChanges for vir_typed::ExhalePredicate { fn collect<'v, 'tcx>( &self, _encoder: &mut Encoder<'v, 'tcx>, @@ -224,6 +301,28 @@ impl CollectPermissionChanges for vir_typed::Exhale { } } +impl CollectPermissionChanges for vir_typed::InhaleExpression { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::ExhaleExpression { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + Ok(()) + } +} + impl CollectPermissionChanges for vir_typed::Consume { fn collect<'v, 'tcx>( &self, @@ -294,6 +393,9 @@ impl CollectPermissionChanges for vir_typed::CopyPlace { consumed_permissions: &mut Vec, produced_permissions: &mut Vec, ) -> SpannedEncodingResult<()> { + // if let Some(source_pointer_place) = self.source.get_first_dereferenced_pointer() { + + // } consumed_permissions.push(Permission::MemoryBlock(self.target.clone())); consumed_permissions.push(Permission::Owned(self.source.clone())); produced_permissions.push(Permission::Owned(self.target.clone())); @@ -383,6 +485,9 @@ impl CollectPermissionChanges for vir_typed::Rvalue { Self::Len(rvalue) => { rvalue.collect(encoder, consumed_permissions, produced_permissions) } + Self::Cast(rvalue) => { + rvalue.collect(encoder, consumed_permissions, produced_permissions) + } Self::UnaryOp(rvalue) => { rvalue.collect(encoder, consumed_permissions, produced_permissions) } @@ -456,7 +561,7 @@ impl CollectPermissionChanges for vir_typed::ast::rvalue::AddressOf { &self, _encoder: &mut Encoder<'v, 'tcx>, consumed_permissions: &mut Vec, - produced_permissions: &mut Vec, + _produced_permissions: &mut Vec, ) -> SpannedEncodingResult<()> { // To take an address of a place on a stack, it must not be moved out. // The following fails to compile: @@ -476,7 +581,6 @@ impl CollectPermissionChanges for vir_typed::ast::rvalue::AddressOf { // } // ``` consumed_permissions.push(Permission::Owned(self.place.clone())); - produced_permissions.push(Permission::Owned(self.place.clone())); Ok(()) } } @@ -494,6 +598,19 @@ impl CollectPermissionChanges for vir_typed::ast::rvalue::Len { } } +impl CollectPermissionChanges for vir_typed::ast::rvalue::Cast { + fn collect<'v, 'tcx>( + &self, + encoder: &mut Encoder<'v, 'tcx>, + consumed_permissions: &mut Vec, + produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + self.operand + .collect(encoder, consumed_permissions, produced_permissions)?; + Ok(()) + } +} + impl CollectPermissionChanges for vir_typed::ast::rvalue::UnaryOp { fn collect<'v, 'tcx>( &self, @@ -617,6 +734,215 @@ impl CollectPermissionChanges for vir_typed::SetUnionVariant { } } +fn add_struct_expansion( + place: &vir_typed::Expression, + fields: Vec, + permissions: &mut Vec, +) { + let position = place.position(); + for field in fields { + permissions.push(Permission::Owned(vir_typed::Expression::field( + place.clone(), + field, + position, + ))); + } +} + +impl CollectPermissionChanges for vir_typed::Pack { + fn collect<'v, 'tcx>( + &self, + encoder: &mut Encoder<'v, 'tcx>, + consumed_permissions: &mut Vec, + produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + if self.place.is_behind_pointer_dereference() { + produced_permissions.push(Permission::Owned(self.place.clone())); + } else { + let type_decl = encoder.encode_type_def_typed(self.place.get_type())?; + if let vir_typed::TypeDecl::Struct(decl) = type_decl { + if decl.is_manually_managed_type() { + produced_permissions.push(Permission::Owned(self.place.clone())); + add_struct_expansion(&self.place, decl.fields, consumed_permissions); + } else { + produced_permissions.push(Permission::Owned(self.place.clone())); + add_struct_expansion(&self.place, decl.fields, consumed_permissions); + // unimplemented!( + // "Unpacking an automatically managed type: {}\n{}", + // self.place, + // self.place.get_type(), + // ); + } + } else { + unimplemented!( + "Report a proper error message that only structs can be unfolded: {:?}", + self.place + ); + } + } + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::Unpack { + fn collect<'v, 'tcx>( + &self, + encoder: &mut Encoder<'v, 'tcx>, + consumed_permissions: &mut Vec, + produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + if self.place.is_behind_pointer_dereference() { + consumed_permissions.push(Permission::Owned(self.place.clone())); + } else { + let type_decl = encoder.encode_type_def_typed(self.place.get_type())?; + if let vir_typed::TypeDecl::Struct(decl) = type_decl { + if decl.is_manually_managed_type() { + consumed_permissions.push(Permission::Owned(self.place.clone())); + add_struct_expansion(&self.place, decl.fields, produced_permissions); + } else { + consumed_permissions.push(Permission::Owned(self.place.clone())); + add_struct_expansion(&self.place, decl.fields, produced_permissions); + // unimplemented!( + // "Unpacking an automatically managed type: {}\n{}", + // self.place, + // self.place.get_type() + // ); + } + } else { + unimplemented!( + "Report a proper error message that only structs can be unfolded: {}", + self.place + ); + } + } + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::Join { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + if !self.place.is_behind_pointer_dereference() { + unimplemented!( + "Report a proper error message that only memory blocks behind \ + a raw pointer could be joined by hand: {}", + self.place + ); + } + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::JoinRange { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::Split { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + if !self.place.is_behind_pointer_dereference() { + unimplemented!( + "Report a proper error message that only memory blocks behind \ + a raw pointer could be split by hand: {}", + self.place + ); + } + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::SplitRange { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::StashRange { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::StashRangeRestore { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::ForgetInitialization { + fn collect<'v, 'tcx>( + &self, + encoder: &mut Encoder<'v, 'tcx>, + consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + if self.place.is_behind_pointer_dereference() { + consumed_permissions.push(Permission::Owned(self.place.clone())); + } else { + let type_decl = encoder.encode_type_def_typed(self.place.get_type())?; + if let vir_typed::TypeDecl::Struct(decl) = &type_decl { + if decl.is_manually_managed_type() { + consumed_permissions.push(Permission::Owned(self.place.clone())); + } else { + unimplemented!( + "Forgetting initialization of an automatically managed type: {:?}\n{:?}", + self.place, + type_decl + ); + } + } else { + unimplemented!( + "Report a proper error message that only structs can be unfolded: {:?}", + self.place + ); + } + } + Ok(()) + } +} + +impl CollectPermissionChanges for vir_typed::RestoreRawBorrowed { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + produced_permissions.push(Permission::Owned(self.restored_place.clone())); + Ok(()) + } +} + impl CollectPermissionChanges for vir_typed::NewLft { fn collect<'v, 'tcx>( &self, @@ -639,6 +965,22 @@ impl CollectPermissionChanges for vir_typed::EndLft { } } +impl CollectPermissionChanges for vir_typed::DeadReference { + fn collect<'v, 'tcx>( + &self, + _encoder: &mut Encoder<'v, 'tcx>, + _consumed_permissions: &mut Vec, + _produced_permissions: &mut Vec, + ) -> SpannedEncodingResult<()> { + unimplemented!(); + // consumed_permissions.push(Permission::Owned(self.target.clone())); + // // FIXME: This is a lie: the permission is actually gone, we should not + // // require it. + // produced_permissions.push(Permission::Owned(self.target.clone())); + Ok(()) + } +} + impl CollectPermissionChanges for vir_typed::DeadLifetime { fn collect<'v, 'tcx>( &self, diff --git a/prusti-viper/src/encoder/high/procedures/inference/state/places.rs b/prusti-viper/src/encoder/high/procedures/inference/state/places.rs index a2a0c30e64f..cd5b4b90433 100644 --- a/prusti-viper/src/encoder/high/procedures/inference/state/places.rs +++ b/prusti-viper/src/encoder/high/procedures/inference/state/places.rs @@ -1,5 +1,5 @@ use std::collections::BTreeMap; -use vir_crate::typed::{self as vir_typed}; +use vir_crate::typed::{self as vir_typed, ty::visitors::TypeWalker, visitors::ExpressionWalker}; pub(in super::super) struct PlaceWithDeadLifetimes { pub(in super::super) place: vir_typed::Expression, @@ -27,6 +27,7 @@ impl Places { } pub(in super::super) fn insert(&mut self, place: vir_typed::Expression) -> bool { + place.check_no_erased_lifetime(); self.places .insert(place.clone().erase_lifetime(), place) .is_none() diff --git a/prusti-viper/src/encoder/high/procedures/inference/state/predicate_state_on_path.rs b/prusti-viper/src/encoder/high/procedures/inference/state/predicate_state_on_path.rs index 11ec253b1ff..17c57891614 100644 --- a/prusti-viper/src/encoder/high/procedures/inference/state/predicate_state_on_path.rs +++ b/prusti-viper/src/encoder/high/procedures/inference/state/predicate_state_on_path.rs @@ -62,7 +62,7 @@ impl PredicateStateOnPath { self.memory_block_stack.is_empty() && self.owned_non_aliased.iter().all(|place| { // `UniqueRef` and `FracRef` predicates can be leaked. - place.get_dereference_base().is_some() + place.get_last_dereferenced_reference().is_some() }) } @@ -373,6 +373,8 @@ impl PredicateStateOnPath { /// Note: since `y` is borrowing not `x`, but `a.f`, `x` can dye /// before `y`. /// + /// FIXME: We do the same as in 2.1. + /// /// In this case, we need to forget about `UniqueRef` parts (delete /// them from fold-unfold state) and replace with `MemoryBlock(x)` /// because we know that this is what we have in the verifier's @@ -399,16 +401,22 @@ impl PredicateStateOnPath { // Case 2.2. let mut partial_dead_references = BTreeSet::new(); for place in all_dead_references { - if let vir_typed::Expression::Deref(vir_typed::Deref { box base, .. }) = &place { - if let vir_typed::Type::Reference(vir_typed::ty::Reference { - lifetime: lft, .. - }) = base.get_type() - { - if lifetime == lft { - self.memory_block_stack.insert(base.clone()); - dead_references.insert(place); - continue; - } + // if let vir_typed::Expression::Deref(vir_typed::Deref { box base, .. }) = &place { + // if let vir_typed::Type::Reference(vir_typed::ty::Reference { + // lifetime: lft, .. + // }) = base.get_type() + // { + // if lifetime == lft { + // self.memory_block_stack.insert(base.clone()); + // dead_references.insert(place); + // continue; + // } + // } + // } + // partial_dead_references.insert(place.into_ref_with_lifetime(lifetime)); + if let Some((deref_lifetime, _)) = place.get_dereference_kind() { + if &deref_lifetime == lifetime { + dead_references.insert(place.clone()); } } partial_dead_references.insert(place.into_ref_with_lifetime(lifetime)); diff --git a/prusti-viper/src/encoder/high/procedures/inference/unfolding_expressions.rs b/prusti-viper/src/encoder/high/procedures/inference/unfolding_expressions.rs new file mode 100644 index 00000000000..6629204e05a --- /dev/null +++ b/prusti-viper/src/encoder/high/procedures/inference/unfolding_expressions.rs @@ -0,0 +1,194 @@ +use crate::encoder::errors::{SpannedEncodingError, SpannedEncodingResult}; +use vir_crate::{ + common::position::Positioned, + typed::{ + self as vir_typed, + operations::ty::Typed, + visitors::{ + default_fallible_fold_binary_op, default_fallible_fold_expression, + ExpressionFallibleFolder, + }, + }, +}; + +pub(super) fn add_unfolding_expressions( + expression: vir_typed::Expression, +) -> SpannedEncodingResult { + let mut ensurer = Ensurer { + syntactically_framed_places: Vec::new(), + }; + ensurer.fallible_fold_expression(expression) +} + +struct Ensurer { + syntactically_framed_places: Vec, +} + +impl Ensurer { + fn add_unfolding( + &self, + place: vir_typed::Expression, + ) -> SpannedEncodingResult { + for framing_place in &self.syntactically_framed_places { + let mut unfolding_stack = Vec::new(); + if self.add_syntactic_unfolding_rec(&place, framing_place, &mut unfolding_stack)? { + let place = self.apply_unfolding_stack(place, unfolding_stack); + return Ok(place); + } + } + let mut unfolding_stack = Vec::new(); + self.add_self_unfolding_rec(&place, &mut unfolding_stack)?; + let place = self.apply_unfolding_stack(place, unfolding_stack); + Ok(place) + } + + fn apply_unfolding_stack( + &self, + mut place: vir_typed::Expression, + unfolding_stack: Vec, + ) -> vir_typed::Expression { + for unfolded_place in unfolding_stack { + let position = place.position(); + place = vir_typed::Expression::unfolding( + vir_typed::Predicate::owned_non_aliased(unfolded_place, position), + place, + position, + ); + } + place + } + + fn add_syntactic_unfolding_rec( + &self, + place: &vir_typed::Expression, + framing_place: &vir_typed::Expression, + unfolding_stack: &mut Vec, + ) -> SpannedEncodingResult { + if place == framing_place { + return Ok(true); + } else if !place.is_deref() { + if let Some(parent) = place.get_parent_ref() { + if self.add_syntactic_unfolding_rec(framing_place, parent, unfolding_stack)? { + unfolding_stack.push(parent.clone()); + return Ok(true); + } + } + }; + Ok(false) + } + + /// Just unfold on all levels except on deref. + /// + /// FIXME: This should take into account what places are actually framed by + /// the structural invariant. For example, if the invariant contains + /// `own!((*self.p).x)` (that is, it frames only one field of the struct), + /// then we currently will generate one unfolding too many (we would + /// generate unfolding of `self.p` even though we should not). + fn add_self_unfolding_rec( + &self, + place: &vir_typed::Expression, + unfolding_stack: &mut Vec, + ) -> SpannedEncodingResult<()> { + if let Some(parent) = place.get_parent_ref() { + if !parent.get_type().is_pointer() { + unfolding_stack.push(parent.clone()); + } + self.add_self_unfolding_rec(parent, unfolding_stack)?; + } + Ok(()) + } + + // fn add_unfolding(&self, place: vir_typed::Expression) -> SpannedEncodingResult { + // for framing_place in &self.syntactically_framed_places { + // let mut unfolding_stack = Vec::new(); + // if let Some(mut new_place) = self.add_unfolding_rec(&place, framing_place, &mut unfolding_stack)? { + // eprintln!("place: {}", place); + // eprintln!("new_place: {}", new_place); + // for unfolded_place in unfolding_stack { + // eprintln!(" unfolded_place: {}", unfolded_place); + // let position =new_place.position(); + // new_place = vir_typed::Expression::unfolding( + // vir_typed::Predicate::owned_non_aliased(unfolded_place, position), + // new_place, position); + // } + // eprintln!("final_place: {}", new_place); + // return Ok(new_place); + // } + // } + // Ok(place) + // } + + // fn add_unfolding_rec(&self, place: &vir_typed::Expression, framing_place: &vir_typed::Expression, + // unfolding_stack: &mut Vec, + // ) -> SpannedEncodingResult> { + // let new_place = if place == framing_place { + // Some(place.clone()) + // } else { + // if place.is_deref() { + // None + // } else { + // if let Some(parent) = place.get_parent_ref() { + // let result = self.add_unfolding_rec(framing_place, parent, unfolding_stack)?; + // if result.is_some() { + // unfolding_stack.push(parent.clone()); + // } + // result + // } else { + // None + // } + // } + // }; + // Ok(new_place) + // } +} + +impl ExpressionFallibleFolder for Ensurer { + type Error = SpannedEncodingError; + + fn fallible_fold_expression( + &mut self, + expression: vir_typed::Expression, + ) -> Result { + if expression.is_place() && expression.get_last_dereferenced_pointer().is_some() { + self.add_unfolding(expression) + } else { + default_fallible_fold_expression(self, expression) + } + } + + fn fallible_fold_binary_op( + &mut self, + mut binary_op: vir_typed::BinaryOp, + ) -> Result { + match binary_op.op_kind { + vir_typed::BinaryOpKind::And => { + if let vir_typed::Expression::AccPredicate(acc_predicate) = &*binary_op.left { + match &*acc_predicate.predicate { + vir_typed::Predicate::LifetimeToken(_) + | vir_typed::Predicate::MemoryBlockStack(_) + | vir_typed::Predicate::MemoryBlockStackDrop(_) + | vir_typed::Predicate::MemoryBlockHeap(_) + | vir_typed::Predicate::MemoryBlockHeapRange(_) + | vir_typed::Predicate::MemoryBlockHeapDrop(_) => { + default_fallible_fold_binary_op(self, binary_op) + } + vir_typed::Predicate::OwnedNonAliased(predicate) => { + let place = predicate.place.clone(); + binary_op.left = self.fallible_fold_expression_boxed(binary_op.left)?; + self.syntactically_framed_places.push(place); + binary_op.right = + self.fallible_fold_expression_boxed(binary_op.right)?; + self.syntactically_framed_places.pop(); + Ok(binary_op) + } + vir_typed::Predicate::OwnedRange(_) => todo!(), + vir_typed::Predicate::OwnedSet(_) => todo!(), + } + } else { + default_fallible_fold_binary_op(self, binary_op) + } + } + _ => default_fallible_fold_binary_op(self, binary_op), + } + } +} diff --git a/prusti-viper/src/encoder/high/procedures/inference/visitor/context.rs b/prusti-viper/src/encoder/high/procedures/inference/visitor/context.rs index 7e3d987ed60..872afe1fe39 100644 --- a/prusti-viper/src/encoder/high/procedures/inference/visitor/context.rs +++ b/prusti-viper/src/encoder/high/procedures/inference/visitor/context.rs @@ -21,8 +21,7 @@ impl<'p, 'v, 'tcx> super::super::ensurer::Context for Visitor<'p, 'v, 'tcx> { // lifetime variables, but concrete values from `ty`. However, for this // it seams we need to use Rust compiler's `SubstsRef` design, which // means one more refactoring… - let normalized_type = ty.normalize_type(); - let type_decl = self.encoder.encode_type_def_typed(&normalized_type)?; + let type_decl = self.encoder.encode_type_def_typed(ty)?; fn expand_fields<'a>( place: &vir_typed::Expression, fields: impl Iterator, @@ -47,14 +46,29 @@ impl<'p, 'v, 'tcx> super::super::ensurer::Context for Visitor<'p, 'v, 'tcx> { let expansion = match type_decl { vir_typed::TypeDecl::Bool | vir_typed::TypeDecl::Int(_) - | vir_typed::TypeDecl::Float(_) - | vir_typed::TypeDecl::Pointer(_) => { - // Primitive type. Convert. - vec![(ExpandedPermissionKind::MemoryBlock, place.clone())] + | vir_typed::TypeDecl::Float(_) => { + // Primitive type. + unreachable!(); + } + vir_typed::TypeDecl::Pointer(_) => { + let target_type = ty.clone().unwrap_pointer().target_type; + let deref_place = + vir_typed::Expression::deref(place.clone(), *target_type, place.position()); + vec![(ExpandedPermissionKind::Same, deref_place)] } vir_typed::TypeDecl::Trusted(_) => unimplemented!("ty: {}", ty), vir_typed::TypeDecl::TypeVar(_) => unimplemented!("ty: {}", ty), - vir_typed::TypeDecl::Struct(decl) => expand_fields(place, decl.fields.iter()), + vir_typed::TypeDecl::Struct(decl) => { + // if decl.is_manually_managed_type() { + // let place_span = self.get_span(guiding_place.position()).unwrap(); + // let error = SpannedEncodingError::incorrect( + // "types with structural invariants are required to be managed manually", + // place_span, + // ); + // return Err(error); + // } + expand_fields(place, decl.fields.iter()) + } vir_typed::TypeDecl::Enum(decl) => { let position = place.position(); let variant_name = place.get_variant_name(guiding_place); diff --git a/prusti-viper/src/encoder/high/procedures/inference/visitor/mod.rs b/prusti-viper/src/encoder/high/procedures/inference/visitor/mod.rs index 592dbb556c2..613c9d18c94 100644 --- a/prusti-viper/src/encoder/high/procedures/inference/visitor/mod.rs +++ b/prusti-viper/src/encoder/high/procedures/inference/visitor/mod.rs @@ -20,7 +20,7 @@ use prusti_rustc_interface::hir::def_id::DefId; use rustc_hash::{FxHashMap, FxHashSet}; use std::collections::{btree_map::Entry, BTreeMap}; use vir_crate::{ - common::{display::cjoin, position::Positioned}, + common::{check_mode::CheckMode, display::cjoin, position::Positioned}, middle::{ self as vir_mid, operations::{TypedToMiddleExpression, TypedToMiddleStatement, TypedToMiddleType}, @@ -34,6 +34,7 @@ mod debugging; pub(super) struct Visitor<'p, 'v, 'tcx> { encoder: &'p mut Encoder<'v, 'tcx>, _proc_def_id: DefId, + check_mode: Option, state_at_entry: BTreeMap, /// Used only for debugging purposes. state_at_exit: BTreeMap, @@ -55,6 +56,7 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { Self { encoder, _proc_def_id: proc_def_id, + check_mode: None, state_at_entry: Default::default(), state_at_exit: Default::default(), procedure_name: None, @@ -74,6 +76,7 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { entry_state: FoldUnfoldState, ) -> SpannedEncodingResult { self.procedure_name = Some(procedure.name.clone()); + self.check_mode = Some(procedure.check_mode); let mut path_disambiguators = BTreeMap::new(); for ((from, to), value) in procedure.get_path_disambiguators() { @@ -114,9 +117,16 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { self.render_crash_graphviz(Some(&label_markers)); } let check_mode = procedure.check_mode; + let non_aliased_places = procedure + .non_aliased_places + .into_iter() + .map(|place| place.typed_to_middle_expression(self.encoder)) + .collect::>()?; let new_procedure = vir_mid::ProcedureDecl { name: self.procedure_name.take().unwrap(), check_mode, + position: procedure.position, + non_aliased_places, entry: self.entry_label.take().unwrap(), exit: self.lower_label(&procedure.exit), basic_blocks: std::mem::take(&mut self.basic_blocks), @@ -195,6 +205,7 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { ); if let vir_typed::Statement::DeadLifetime(dead_lifetime) = statement { self.process_dead_lifetime(dead_lifetime, state)?; + self.add_statements_to_current_block(); return Ok(()); } if let vir_typed::Statement::Assign(vir_typed::Assign { @@ -204,6 +215,7 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { }) = statement { self.process_assign_discriminant(target, discriminant, position, state)?; + self.add_statements_to_current_block(); return Ok(()); } let (consumed_permissions, produced_permissions) = @@ -219,7 +231,7 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { self.process_actions(actions)?; state.remove_permissions(&consumed_permissions)?; state.insert_permissions(produced_permissions)?; - match &statement { + match statement { vir_typed::Statement::ObtainMutRef(_) => { // The requirements already performed the needed changes. } @@ -231,7 +243,7 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { // the end of the exit block). state.clear()?; } - vir_typed::Statement::SetUnionVariant(variant_statement) => { + vir_typed::Statement::SetUnionVariant(ref variant_statement) => { let position = variant_statement.position(); // Split the memory block for the union itself. let parent = variant_statement.variant_place.get_parent_ref().unwrap(); @@ -256,11 +268,137 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { self.current_statements .push(statement.typed_to_middle_statement(self.encoder)?); } + vir_typed::Statement::Pack(pack_statement) => { + // state.remove_manually_managed(&pack_statement.place)?; + let position = pack_statement.position(); + let place = pack_statement + .place + .clone() + .typed_to_middle_expression(self.encoder)?; + let permission = pack_statement + .permission + .map(|permission| permission.clone().typed_to_middle_expression(self.encoder)) + .transpose()?; + // let encoded_statement = vir_mid::Statement::fold_owned(place, None, position); + // FIXME: Code duplication. + let encoded_statement = match pack_statement.predicate_kind { + vir_typed::ast::statement::PredicateKind::Owned => { + vir_mid::Statement::fold_owned(place, None, permission, position) + } + vir_typed::ast::statement::PredicateKind::UniqueRef(predicate_kind) => { + // let first_reference = place + // .get_first_dereferenced_reference() + // .expect("TODO: Report a proper error"); + // let vir_mid::Type::Reference(reference) = first_reference.get_type() else { + // unreachable!() + // }; + // let lifetime = reference.lifetime.clone(); + vir_mid::Statement::fold_ref( + place, + predicate_kind.lifetime.typed_to_middle_type(self.encoder)?, + vir_mid::ty::Uniqueness::Unique, + None, + position, + ) + } + vir_typed::ast::statement::PredicateKind::FracRef(_) => todo!(), + }; + self.current_statements.push(encoded_statement); + } + vir_typed::Statement::Unpack(unpack_statement) => { + // state.insert_manually_managed(unpack_statement.place.clone())?; + let position = unpack_statement.position(); + let place = unpack_statement + .place + .typed_to_middle_expression(self.encoder)?; + let permission = unpack_statement + .permission + .map(|permission| permission.clone().typed_to_middle_expression(self.encoder)) + .transpose()?; + // FIXME: Code duplication. + let encoded_statement = match unpack_statement.predicate_kind { + vir_typed::ast::statement::PredicateKind::Owned => { + vir_mid::Statement::unfold_owned(place, None, permission, position) + } + vir_typed::ast::statement::PredicateKind::UniqueRef(predicate_kind) => { + // let first_reference = place + // .get_first_dereferenced_reference() + // .expect("TODO: Report a proper error"); + // let vir_mid::Type::Reference(reference) = first_reference.get_type() else { + // unreachable!() + // }; + // let lifetime = reference.lifetime.clone(); + vir_mid::Statement::unfold_ref( + place, + predicate_kind.lifetime.typed_to_middle_type(self.encoder)?, + vir_mid::ty::Uniqueness::Unique, + None, + position, + ) + } + vir_typed::ast::statement::PredicateKind::FracRef(_) => todo!(), + }; + self.current_statements.push(encoded_statement); + } + vir_typed::Statement::Join(join_statement) => { + let position = join_statement.position(); + let place = join_statement + .place + .typed_to_middle_expression(self.encoder)?; + let encoded_statement = vir_mid::Statement::join_block(place, None, None, position); + self.current_statements.push(encoded_statement); + } + vir_typed::Statement::Split(split_statement) => { + let position = split_statement.position(); + let place = split_statement + .place + .typed_to_middle_expression(self.encoder)?; + let encoded_statement = + vir_mid::Statement::split_block(place, None, None, position); + self.current_statements.push(encoded_statement); + } + vir_typed::Statement::ForgetInitialization(forget_statement) => { + // state.insert_manually_managed(forget_statement.place.clone())?; + let position = forget_statement.position(); + let place = forget_statement + .place + .typed_to_middle_expression(self.encoder)?; + let encoded_statement = + vir_mid::Statement::convert_owned_into_memory_block(place, None, position); + self.current_statements.push(encoded_statement); + } + vir_typed::Statement::InhaleExpression(mut inhale_statement) => { + if self.check_mode.unwrap() != CheckMode::PurificationFunctional { + inhale_statement.expression = + super::unfolding_expressions::add_unfolding_expressions( + inhale_statement.expression, + )?; + } + let inhale_statement = inhale_statement.typed_to_middle_statement(self.encoder)?; + self.current_statements + .push(vir_mid::Statement::InhaleExpression(inhale_statement)); + } + vir_typed::Statement::ExhaleExpression(mut exhale_statement) => { + if self.check_mode.unwrap() != CheckMode::PurificationFunctional { + exhale_statement.expression = + super::unfolding_expressions::add_unfolding_expressions( + exhale_statement.expression, + )?; + } + let exhale_statement = exhale_statement.typed_to_middle_statement(self.encoder)?; + self.current_statements + .push(vir_mid::Statement::ExhaleExpression(exhale_statement)); + } _ => { self.current_statements .push(statement.typed_to_middle_statement(self.encoder)?); } } + self.add_statements_to_current_block(); + Ok(()) + } + + fn add_statements_to_current_block(&mut self) { let new_block = self .basic_blocks .get_mut(self.current_label.as_ref().unwrap()) @@ -268,7 +406,6 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { new_block .statements .extend(std::mem::take(&mut self.current_statements)); - Ok(()) } fn process_actions(&mut self, actions: Vec) -> SpannedEncodingResult<()> { @@ -295,6 +432,7 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { vir_mid::Statement::unfold_owned( place.typed_to_middle_expression(self.encoder)?, condition, + None, // FIXME: should track the permission amounts. position, ) } @@ -319,6 +457,7 @@ impl<'p, 'v, 'tcx> Visitor<'p, 'v, 'tcx> { vir_mid::Statement::fold_owned( place.typed_to_middle_expression(self.encoder)?, condition, + None, // FIXME: should track the permission amounts. position, ) } diff --git a/prusti-viper/src/encoder/high/procedures/interface.rs b/prusti-viper/src/encoder/high/procedures/interface.rs index 94d76dec50f..bbc8837a3b5 100644 --- a/prusti-viper/src/encoder/high/procedures/interface.rs +++ b/prusti-viper/src/encoder/high/procedures/interface.rs @@ -82,9 +82,16 @@ impl<'v, 'tcx: 'v> Private for super::super::super::Encoder<'v, 'tcx> { self.lower_block(block)?, ); } + let non_aliased_places = procedure_high + .non_aliased_places + .into_iter() + .map(|place| place.high_to_typed_expression(self)) + .collect::>()?; let procedure_typed = vir_typed::ProcedureDecl { name: procedure_high.name, check_mode: procedure_high.check_mode, + position: procedure_high.position, + non_aliased_places, entry: procedure_high.entry.high_to_typed_statement(self)?, exit: procedure_high.exit.high_to_typed_statement(self)?, basic_blocks, @@ -98,7 +105,7 @@ pub(crate) trait HighProcedureEncoderInterface<'tcx> { &mut self, proc_def_id: DefId, check_mode: CheckMode, - ) -> SpannedEncodingResult; + ) -> SpannedEncodingResult>; fn encode_type_core_proof( &mut self, ty: ty::Ty<'tcx>, @@ -111,14 +118,17 @@ impl<'v, 'tcx: 'v> HighProcedureEncoderInterface<'tcx> for super::super::super:: &mut self, proc_def_id: DefId, check_mode: CheckMode, - ) -> SpannedEncodingResult { - let procedure_high = self.encode_procedure_core_proof_high(proc_def_id, check_mode)?; - debug!("procedure_high:\n{}", procedure_high); - let procedure_typed = self.procedure_high_to_typed(procedure_high)?; - debug!("procedure_typed:\n{}", procedure_typed); - let procedure = - super::inference::infer_shape_operations(self, proc_def_id, procedure_typed)?; - Ok(procedure) + ) -> SpannedEncodingResult> { + let mut procedures = Vec::new(); + for procedure_high in self.encode_procedure_core_proof_high(proc_def_id, check_mode)? { + debug!("procedure_high:\n{}", procedure_high); + let procedure_typed = self.procedure_high_to_typed(procedure_high)?; + debug!("procedure_typed:\n{}", procedure_typed); + let procedure = + super::inference::infer_shape_operations(self, proc_def_id, procedure_typed)?; + procedures.push(procedure); + } + Ok(procedures) } fn encode_type_core_proof( @@ -126,7 +136,7 @@ impl<'v, 'tcx: 'v> HighProcedureEncoderInterface<'tcx> for super::super::super:: ty: ty::Ty<'tcx>, check_mode: CheckMode, ) -> SpannedEncodingResult { - assert_eq!(check_mode, CheckMode::CoreProof); + assert_eq!(check_mode, CheckMode::MemorySafety); let ty_high = self.encode_type_high(ty)?; ty_high.high_to_middle(self) } diff --git a/prusti-viper/src/encoder/high/pure_functions/interface.rs b/prusti-viper/src/encoder/high/pure_functions/interface.rs index 6454aaf88d7..554da9924bf 100644 --- a/prusti-viper/src/encoder/high/pure_functions/interface.rs +++ b/prusti-viper/src/encoder/high/pure_functions/interface.rs @@ -98,7 +98,8 @@ impl<'v, 'tcx: 'v> HighPureFunctionEncoderInterface<'tcx> // FIXME: Should use encode_builtin_function_use. let name = "subslice"; let element_type = extract_container_element_type(&container)?; - let pure_lifetime = vir_high::ty::LifetimeConst::erased(); + // let pure_lifetime = vir_high::ty::LifetimeConst::erased(); + let pure_lifetime = unimplemented!(); let return_type = vir_high::Type::reference( pure_lifetime, vir_high::ty::Uniqueness::Shared, diff --git a/prusti-viper/src/encoder/high/to_typed/expression.rs b/prusti-viper/src/encoder/high/to_typed/expression.rs index 9d148071769..cd0ba792370 100644 --- a/prusti-viper/src/encoder/high/to_typed/expression.rs +++ b/prusti-viper/src/encoder/high/to_typed/expression.rs @@ -1,7 +1,10 @@ use crate::encoder::errors::{SpannedEncodingError, SpannedEncodingResult}; use vir_crate::{ - high as vir_high, typed as vir_typed, - typed::operations::{HighToTypedExpressionLowerer, HighToTypedType}, + high as vir_high, + typed::{ + self as vir_typed, + operations::{HighToTypedExpressionLowerer, HighToTypedPredicateLowerer, HighToTypedType}, + }, }; impl<'v, 'tcx> HighToTypedExpressionLowerer for crate::encoder::Encoder<'v, 'tcx> { @@ -64,4 +67,11 @@ impl<'v, 'tcx> HighToTypedExpressionLowerer for crate::encoder::Encoder<'v, 'tcx index: variant_index.index, }) } + + fn high_to_typed_expression_predicate( + &mut self, + predicate: vir_high::Predicate, + ) -> Result { + self.high_to_typed_predicate_predicate(predicate) + } } diff --git a/prusti-viper/src/encoder/high/to_typed/type_decl.rs b/prusti-viper/src/encoder/high/to_typed/type_decl.rs index 45564009fff..6a005e66053 100644 --- a/prusti-viper/src/encoder/high/to_typed/type_decl.rs +++ b/prusti-viper/src/encoder/high/to_typed/type_decl.rs @@ -54,11 +54,13 @@ impl<'v, 'tcx> HighToTypedTypeDeclLowerer for crate::encoder::Encoder<'v, 'tcx> self.generate_tuple_name(&arguments)?, decl.lifetimes.high_to_typed_type(self)?, decl.const_parameters.high_to_typed_expression(self)?, + None, arguments .into_iter() .enumerate() .map(|(index, ty)| vir_typed::FieldDecl::new(format!("tuple_{index}"), index, ty)) .collect(), + Default::default(), )) } @@ -131,4 +133,11 @@ impl<'v, 'tcx> HighToTypedTypeDeclLowerer for crate::encoder::Encoder<'v, 'tcx> variants: decl.variants.high_to_typed_type_decl(self)?, }) } + + fn high_to_typed_type_decl_position( + &mut self, + position: vir_high::Position, + ) -> Result { + Ok(position) + } } diff --git a/prusti-viper/src/encoder/high/to_typed/types/interface.rs b/prusti-viper/src/encoder/high/to_typed/types/interface.rs index 286bf1a57bf..e2ad8e50382 100644 --- a/prusti-viper/src/encoder/high/to_typed/types/interface.rs +++ b/prusti-viper/src/encoder/high/to_typed/types/interface.rs @@ -34,8 +34,15 @@ impl<'v, 'tcx: 'v> HighToTypedTypeEncoderInterface &mut self, ty: &vir_typed::Type, ) -> SpannedEncodingResult { - let high_type = &self.typed_type_encoder_state.encoded_types_inverse[ty]; - let type_decl_high = self.encode_type_def_high(high_type)?; + let normalized_type = ty.normalize_type(); + let high_type = &self + .typed_type_encoder_state + .encoded_types_inverse + .get(&normalized_type) + .unwrap_or_else(|| { + panic!("Type {normalized_type} was not encoded by the HighToTypedTypeEncoder",) + }); + let type_decl_high = self.encode_type_def_high(high_type, true)?; type_decl_high.high_to_typed_type_decl(self) } diff --git a/prusti-viper/src/encoder/high/types/fields.rs b/prusti-viper/src/encoder/high/types/fields.rs index 1ed74cdf284..5b4fbfa6f0b 100644 --- a/prusti-viper/src/encoder/high/types/fields.rs +++ b/prusti-viper/src/encoder/high/types/fields.rs @@ -56,6 +56,8 @@ pub(crate) fn create_value_field(ty: vir::Type) -> EncodingResult { unreachable!() } diff --git a/prusti-viper/src/encoder/high/types/interface.rs b/prusti-viper/src/encoder/high/types/interface.rs index 9984bffa455..af722ae870a 100644 --- a/prusti-viper/src/encoder/high/types/interface.rs +++ b/prusti-viper/src/encoder/high/types/interface.rs @@ -65,7 +65,7 @@ impl<'v, 'tcx: 'v> HighTypeEncoderInterfacePrivate for super::super::super::Enco let encoded_type = &self.high_type_encoder_state.lowered_types_inverse.borrow() [predicate_name] .clone(); - let encoded_type_decl = self.encode_type_def_high(encoded_type)?; + let encoded_type_decl = self.encode_type_def_high(encoded_type, false)?; // FIXME: Change not to use `with_default_span` here. let predicates = encoded_type_decl .lower(encoded_type, self) @@ -288,7 +288,7 @@ impl<'v, 'tcx: 'v> HighTypeEncoderInterface<'tcx> for super::super::super::Encod ) -> SpannedEncodingResult { let high_type = self.decode_type_mid_into_high(ty.erase_lifetimes().erase_const_generics())?; - let high_type_decl = self.encode_type_def_high(&high_type)?; + let high_type_decl = self.encode_type_def_high(&high_type, true)?; high_type_decl.high_to_middle(self) } } diff --git a/prusti-viper/src/encoder/middle/core_proof/addresses/encoder.rs b/prusti-viper/src/encoder/middle/core_proof/addresses/encoder.rs index 0d7790a1916..6162c8cf46e 100644 --- a/prusti-viper/src/encoder/middle/core_proof/addresses/encoder.rs +++ b/prusti-viper/src/encoder/middle/core_proof/addresses/encoder.rs @@ -1,15 +1,46 @@ use super::{super::utils::place_domain_encoder::PlaceExpressionDomainEncoder, AddressesInterface}; -use crate::encoder::{errors::SpannedEncodingResult, middle::core_proof::lowerer::Lowerer}; +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + lowerer::Lowerer, pointers::PointersInterface, references::ReferencesInterface, + snapshots::IntoProcedureSnapshot, + }, +}; use vir_crate::{ low as vir_low, - middle::{self as vir_mid}, + middle::{self as vir_mid, operations::ty::Typed}, }; -pub(super) struct PlaceAddressEncoder {} +#[derive(Debug, Clone, PartialEq, Eq)] +enum EncodingContext { + Procedure, + Predicate { self_address: vir_low::Expression }, +} + +pub(super) struct PlaceAddressEncoder { + old_label: Option, + encoding_context: EncodingContext, +} + +impl PlaceAddressEncoder { + pub(super) fn new_in_procedure() -> Self { + Self { + old_label: None, + encoding_context: EncodingContext::Procedure, + } + } + + pub(super) fn new_in_predicate(self_address: vir_low::Expression) -> Self { + Self { + old_label: None, + encoding_context: EncodingContext::Predicate { self_address }, + } + } +} impl PlaceExpressionDomainEncoder for PlaceAddressEncoder { - fn domain_name(&mut self, _lowerer: &mut Lowerer) -> &str { - "Address" + fn domain_name(&mut self, lowerer: &mut Lowerer) -> &str { + lowerer.address_domain() } fn encode_local( @@ -17,16 +48,32 @@ impl PlaceExpressionDomainEncoder for PlaceAddressEncoder { local: &vir_mid::expression::Local, lowerer: &mut Lowerer, ) -> SpannedEncodingResult { - lowerer.root_address(local) + match &self.encoding_context { + EncodingContext::Procedure => lowerer.root_address(local, &self.old_label), + EncodingContext::Predicate { self_address } => { + assert!(self.old_label.is_none()); + assert!(local.variable.is_self_variable()); + Ok(self_address.clone()) + } + } } fn encode_deref( &mut self, - _deref: &vir_mid::expression::Deref, - _lowerer: &mut Lowerer, + deref: &vir_mid::expression::Deref, + lowerer: &mut Lowerer, _arg: vir_low::Expression, ) -> SpannedEncodingResult { - unreachable!("The address cannot be dereferenced; use the value instead.") + // FIXME: Code duplication with AddressesInterface::extract_root_address + // FIXME: Code duplication with AssertionEncoder. + let base_snapshot = deref.base.to_procedure_snapshot(lowerer)?; + let ty = deref.base.get_type(); + let result = if ty.is_reference() { + lowerer.reference_address(ty, base_snapshot, deref.position)? + } else { + lowerer.pointer_address(ty, base_snapshot, deref.position)? + }; + Ok(result) } fn encode_array_index_axioms( @@ -36,4 +83,12 @@ impl PlaceExpressionDomainEncoder for PlaceAddressEncoder { ) -> SpannedEncodingResult<()> { Ok(()) } + + fn encode_labelled_old( + &mut self, + _expression: &vir_mid::expression::LabelledOld, + _lowerer: &mut Lowerer, + ) -> SpannedEncodingResult { + todo!() + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/addresses/interface.rs b/prusti-viper/src/encoder/middle/core_proof/addresses/interface.rs index 93b37fb1b7c..a871d26deb2 100644 --- a/prusti-viper/src/encoder/middle/core_proof/addresses/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/addresses/interface.rs @@ -4,33 +4,57 @@ use super::{ use crate::encoder::{ errors::SpannedEncodingResult, middle::core_proof::{ - lowerer::{DomainsLowererInterface, Lowerer, VariablesLowererInterface}, - references::ReferencesInterface, - snapshots::IntoProcedureSnapshot, + lowerer::{DomainsLowererInterface, Lowerer}, + pointers::PointersInterface, + snapshots::SnapshotVariablesInterface, + type_layouts::TypeLayoutsInterface, }, }; use vir_crate::{ + common::{expression::QuantifierHelpers, position::Positioned}, low as vir_low, - middle::{self as vir_mid, operations::ty::Typed}, + middle::{self as vir_mid}, }; pub(in super::super) trait AddressesInterface { + fn address_domain(&self) -> &'static str; fn address_type(&mut self) -> SpannedEncodingResult; + fn address_null( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn address_offset( + &mut self, + size: vir_low::Expression, + address: vir_low::Expression, + offset: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; /// Constructs a variable representing the address of the given MIR-level /// variable. fn root_address( &mut self, local: &vir_mid::expression::Local, + old_label: &Option, ) -> SpannedEncodingResult; /// Get the variable representing the root address of this place. fn extract_root_address( &mut self, place: &vir_mid::Expression, ) -> SpannedEncodingResult; + /// Emits code that represents the place's address. This method is supposed + /// to be used in procedures for places whose root addresses are tracked + /// with SSA variables. For addresses inside predicates, use + /// `encode_expression_as_place_address_in_predicate`. fn encode_expression_as_place_address( &mut self, place: &vir_mid::Expression, ) -> SpannedEncodingResult; + // fn encode_expression_as_place_address_in_predicate( + // &mut self, + // place: &vir_mid::Expression, + // self_address: vir_low::Expression, + // ) -> SpannedEncodingResult; fn encode_field_address( &mut self, base_type: &vir_mid::Type, @@ -45,45 +69,140 @@ pub(in super::super) trait AddressesInterface { base_address: vir_low::Expression, position: vir_mid::Position, ) -> SpannedEncodingResult; + fn encode_index_address( + &mut self, + base_type: &vir_mid::Type, + base_address: vir_low::Expression, + index: vir_low::Expression, + position: vir_mid::Position, + ) -> SpannedEncodingResult; } impl<'p, 'v: 'p, 'tcx: 'v> AddressesInterface for Lowerer<'p, 'v, 'tcx> { + fn address_domain(&self) -> &'static str { + "Address" + } fn address_type(&mut self) -> SpannedEncodingResult { - self.domain_type("Address") + self.domain_type(self.address_domain()) + } + fn address_null( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let address_type = self.address_type()?; + self.create_domain_func_app( + "Address", + "null_address$", + Vec::new(), + address_type, + position, + ) + } + fn address_offset( + &mut self, + size: vir_low::Expression, + address: vir_low::Expression, + offset: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let address_type = self.address_type()?; + if !self.address_state.is_address_offset_axiom_encoded { + self.address_state.is_address_offset_axiom_encoded = true; + use vir_low::macros::*; + let size_type = self.size_type()?; + var_decls! { + address: Address, + index: Int, + size: {size_type} + } + let call = self.create_domain_func_app( + "Address", + "offset_address$", + vec![ + size.clone().into(), + address.clone().into(), + index.clone().into(), + ], + address_type.clone(), + position, + )?; + let injective_call = self.create_domain_func_app( + "Address", + "offset_address$inverse", + vec![address.clone().into(), call.clone()], + vir_low::Type::Int, + position, + )?; + let forall_body = expr! { + [injective_call] == index + }; + let body = vir_low::Expression::forall( + vec![size, address, index], + vec![vir_low::Trigger::new(vec![call])], + forall_body, + ); + let axiom = vir_low::DomainAxiomDecl::new(None, "offset_address$injective", body); + self.declare_axiom("Address", axiom)?; + } + self.create_domain_func_app( + "Address", + "offset_address$", + vec![size, address, offset], + address_type, + position, + ) } fn root_address( &mut self, local: &vir_mid::expression::Local, + old_label: &Option, ) -> SpannedEncodingResult { - let name = format!("{}$address", local.variable.name); - let ty = self.address_type()?; - let address_variable = self.create_variable(name, ty)?; + let address_variable = + self.address_variable_version_at_label(&local.variable.name, old_label)?; Ok(vir_low::Expression::local(address_variable, local.position)) } fn extract_root_address( &mut self, place: &vir_mid::Expression, ) -> SpannedEncodingResult { - assert!(place.is_place()); - let result = match place { - vir_mid::Expression::Local(local) => self.root_address(local)?, - vir_mid::Expression::LabelledOld(_) => unimplemented!(), - vir_mid::Expression::Deref(deref) => { - let base_snapshot = deref.base.to_procedure_snapshot(self)?; - self.reference_address(deref.base.get_type(), base_snapshot, Default::default())? - } - _ => self.extract_root_address(place.get_parent_ref().unwrap())?, - }; - Ok(result) + unimplemented!("outdated code: {place}"); + // assert!(place.is_place()); + // let result = match place { + // vir_mid::Expression::Local(local) => self.root_address(local, &None)?, + // vir_mid::Expression::LabelledOld(_) => unimplemented!(), + // vir_mid::Expression::Deref(deref) => { + // // FIXME: Code duplication with PlaceAddressEncoder + // let mut place_encoder = + // PlaceToSnapshot::for_address(PredicateKind::Owned); + // let base_snapshot = + // place_encoder.expression_to_snapshot(self, &deref.base, false)?; + // // let base_snapshot = deref.base.to_procedure_snapshot(self)?; + // let ty = deref.base.get_type(); + // if ty.is_reference() { + // self.reference_address(ty, base_snapshot, place.position())? + // } else { + // self.pointer_address(ty, base_snapshot, place.position())? + // } + // } + // _ => self.extract_root_address(place.get_parent_ref().unwrap())?, + // }; + // Ok(result) } - /// Emits code that represents the place's address. fn encode_expression_as_place_address( &mut self, place: &vir_mid::Expression, ) -> SpannedEncodingResult { - let mut encoder = PlaceAddressEncoder {}; + let mut encoder = PlaceAddressEncoder::new_in_procedure(); encoder.encode_expression(place, self) } + // fn encode_expression_as_place_address_in_predicate( + // &mut self, + // place: &vir_mid::Expression, + // self_address: vir_low::Expression, + // ) -> SpannedEncodingResult { + // let mut encoder = PlaceAddressEncoder::new_in_predicate(self_address); + // encoder.encode_expression(place, self) + // } fn encode_field_address( &mut self, base_type: &vir_mid::Type, @@ -108,4 +227,20 @@ impl<'p, 'v: 'p, 'tcx: 'v> AddressesInterface for Lowerer<'p, 'v, 'tcx> { position, ) } + fn encode_index_address( + &mut self, + base_type: &vir_mid::Type, + base_address: vir_low::Expression, + index: vir_low::Expression, + position: vir_mid::Position, + ) -> SpannedEncodingResult { + // FIXME: This implementation is most likely wrong. Test it properly. + let vir_mid::Type::Pointer(pointer_type) = base_type else { + unreachable!() + }; + let size = self + .encode_type_size_expression2(&pointer_type.target_type, &*pointer_type.target_type)?; + let start_address = self.pointer_address(base_type, base_address, position)?; + self.address_offset(size, start_address, index, position) + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/addresses/mod.rs b/prusti-viper/src/encoder/middle/core_proof/addresses/mod.rs index 472846c6772..8285a383997 100644 --- a/prusti-viper/src/encoder/middle/core_proof/addresses/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/addresses/mod.rs @@ -1,4 +1,5 @@ mod encoder; mod interface; +mod state; -pub(super) use self::interface::AddressesInterface; +pub(super) use self::{interface::AddressesInterface, state::AddressState}; diff --git a/prusti-viper/src/encoder/middle/core_proof/addresses/state.rs b/prusti-viper/src/encoder/middle/core_proof/addresses/state.rs new file mode 100644 index 00000000000..dbb584af0e3 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/addresses/state.rs @@ -0,0 +1,4 @@ +#[derive(Default)] +pub(in super::super) struct AddressState { + pub(super) is_address_offset_axiom_encoded: bool, +} diff --git a/prusti-viper/src/encoder/middle/core_proof/adts/interface.rs b/prusti-viper/src/encoder/middle/core_proof/adts/interface.rs index 3b64a1054dc..f2f5208a75f 100644 --- a/prusti-viper/src/encoder/middle/core_proof/adts/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/adts/interface.rs @@ -2,10 +2,11 @@ use crate::encoder::{ errors::SpannedEncodingResult, middle::core_proof::lowerer::{DomainsLowererInterface, Lowerer}, }; +use prusti_common::config; use rustc_hash::FxHashSet; use std::borrow::Cow; use vir_crate::{ - common::expression::{ExpressionIterator, QuantifierHelpers}, + common::expression::QuantifierHelpers, low::{self as vir_low}, }; @@ -123,6 +124,7 @@ pub(in super::super) trait AdtsInterface { &mut self, domain_name: &str, variant_name: &str, + // operation: Option<(vir_mid::BinaryOpKind, vir_mid::Type)>, use_main_constructor_destructors: bool, parameters: Vec, generate_injectivity_axioms: bool, @@ -203,6 +205,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> AdtsInterface for Lowerer<'p, 'v, 'tcx> { self.adt_register_variant_constructor( domain_name, "", + // None, false, parameters, generate_injectivity_axioms, @@ -213,6 +216,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> AdtsInterface for Lowerer<'p, 'v, 'tcx> { &mut self, domain_name: &str, variant_name: &str, + // operation: Option<(vir_mid::BinaryOpKind, vir_mid::Type)>, use_main_constructor_destructors: bool, parameters: Vec, generate_injectivity_axioms: bool, @@ -265,20 +269,16 @@ impl<'p, 'v: 'p, 'tcx: 'v> AdtsInterface for Lowerer<'p, 'v, 'tcx> { } // Injectivity axioms. - if parameters.is_empty() { - // No need to generate injectivity axioms if the constructor has no parameters. - return Ok(()); - } - if generate_injectivity_axioms { // We do not generate injectivity axioms for alternative // constructors (that would be unsound). use vir_low::macros::*; // Bottom-up injectivity axiom. - { + if !parameters.is_empty() { + // We need something to quantify over, so parameters cannot be empty. let mut triggers = Vec::new(); - let mut conjuncts = Vec::new(); + // let mut conjuncts = Vec::new(); let constructor_call = self.adt_constructor_variant_call( domain_name, variant_name, @@ -287,6 +287,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> AdtsInterface for Lowerer<'p, 'v, 'tcx> { .map(|argument| argument.clone().into()) .collect(), )?; + triggers.push(vir_low::Trigger::new(vec![constructor_call.clone()])); for parameter in ¶meters { let destructor_call = self.adt_destructor_variant_call( domain_name, @@ -295,20 +296,32 @@ impl<'p, 'v: 'p, 'tcx: 'v> AdtsInterface for Lowerer<'p, 'v, 'tcx> { parameter.ty.clone(), constructor_call.clone(), )?; - triggers.push(vir_low::Trigger::new(vec![constructor_call.clone()])); - conjuncts.push(expr! { [destructor_call] == parameter }); + // conjuncts.push(expr! { [destructor_call] == parameter }); + let axiom = vir_low::DomainRewriteRuleDecl { + comment: None, + name: format!( + "{constructor_name}$bottom_up_injectivity_axiom${}", + parameter.name + ), + egg_only: false, + variables: parameters.clone(), + triggers: Some(triggers.clone()), + source: destructor_call, + target: parameter.clone().into(), + }; + self.declare_rewrite_rule(domain_name, axiom)?; } - let body = vir_low::Expression::forall( - parameters.clone(), - triggers, - conjuncts.into_iter().conjoin(), - ); - let axiom = vir_low::DomainAxiomDecl { - comment: None, - name: format!("{constructor_name}$bottom_up_injectivity_axiom"), - body, - }; - self.declare_axiom(domain_name, axiom)?; + // let body = vir_low::Expression::forall( + // parameters.clone(), + // triggers, + // conjuncts.into_iter().conjoin(), + // ); + // let axiom = vir_low::DomainAxiomDecl { + // comment: None, + // name: format!("{constructor_name}$bottom_up_injectivity_axiom"), + // body, + // }; + // self.declare_axiom(domain_name, axiom)?; } // Top-down injectivity axiom. @@ -344,12 +357,26 @@ impl<'p, 'v: 'p, 'tcx: 'v> AdtsInterface for Lowerer<'p, 'v, 'tcx> { } let constructor_call = self.adt_constructor_variant_call(domain_name, variant_name, arguments)?; + if parameters.is_empty() { + if let Some(guard) = &trigger_guard { + triggers.push(vir_low::Trigger::new(vec![guard.clone()])); + } else { + unimplemented!("figure out what triggers to choose!"); + } + } + if !config::use_snapshot_parameters_in_predicates() && !parameters.is_empty() { + triggers.push(vir_low::Trigger::new(vec![constructor_call.clone()])); + } let equality = expr! { value == [constructor_call] }; let forall_body = if let Some(guard) = guard { expr! { [guard] ==> [equality] } } else { equality }; + assert!( + !triggers.is_empty(), + "empty triggers for {constructor_name}" + ); let axiom = vir_low::DomainAxiomDecl { comment: None, name: format!("{constructor_name}$top_down_injectivity_axiom"), diff --git a/prusti-viper/src/encoder/middle/core_proof/block_markers/interface.rs b/prusti-viper/src/encoder/middle/core_proof/block_markers/interface.rs index a36ee77e3dd..6c9d1a700f8 100644 --- a/prusti-viper/src/encoder/middle/core_proof/block_markers/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/block_markers/interface.rs @@ -1,6 +1,6 @@ use crate::encoder::{ errors::SpannedEncodingResult, - middle::core_proof::lowerer::{Lowerer, VariablesLowererInterface}, + middle::core_proof::{lowerer::Lowerer, snapshots::SnapshotVariablesInterface}, }; use vir_crate::{ common::expression::{ExpressionIterator, UnaryOperationHelpers}, @@ -12,7 +12,7 @@ pub(in super::super) trait BlockMarkersInterface { fn create_block_marker( &mut self, label: &vir_mid::BasicBlockId, - ) -> SpannedEncodingResult; + ) -> SpannedEncodingResult; fn lower_block_marker_condition( &mut self, condition: vir_mid::BlockMarkerCondition, @@ -23,8 +23,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> BlockMarkersInterface for Lowerer<'p, 'v, 'tcx> { fn create_block_marker( &mut self, label: &vir_mid::BasicBlockId, - ) -> SpannedEncodingResult { - self.create_variable(format!("{label}$marker"), vir_low::Type::Bool) + ) -> SpannedEncodingResult { + // self.create_variable(format!("{label}$marker"), vir_low::Type::Bool) + Ok(vir_mid::VariableDecl::new( + format!("{label}$marker"), + vir_mid::Type::MBool, + )) } fn lower_block_marker_condition( &mut self, @@ -33,6 +37,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BlockMarkersInterface for Lowerer<'p, 'v, 'tcx> { let mut conjuncts: Vec = Vec::new(); for element in condition.elements { let marker = self.create_block_marker(&element.basic_block_id)?; + let marker = self.current_snapshot_variable_version(&marker)?; let condition = if element.visited { marker.into() } else { diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/assertion_encoder.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/assertion_encoder.rs new file mode 100644 index 00000000000..8ea8fb4c0cb --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/assertion_encoder.rs @@ -0,0 +1,462 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + high::types::HighTypeEncoderInterface, + middle::core_proof::{ + builtin_methods::CallContext, + lowerer::Lowerer, + places::PlacesInterface, + pointers::PointersInterface, + predicates::{PredicatesMemoryBlockInterface, PredicatesOwnedInterface}, + references::ReferencesInterface, + snapshots::{IntoSnapshotLowerer, SnapshotValuesInterface, SnapshotVariablesInterface}, + }, +}; + +use std::collections::BTreeMap; +use vir_crate::{ + common::{expression::BinaryOperationHelpers, position::Positioned}, + low::{self as vir_low}, + middle::{self as vir_mid, operations::ty::Typed}, +}; + +// TODO: Delete this file. +pub(in super::super::super) struct AssertionEncoder<'a> { + /// A map from field names to arguments that are being assigned to these + /// fields. + field_arguments: BTreeMap, + heap: &'a Option, + result_value: Option, + replace_self_with_result_value: bool, + in_function: bool, +} + +impl<'a> AssertionEncoder<'a> { + pub(in super::super::super) fn new( + decl: &vir_mid::type_decl::Struct, + operand_values: Vec, + heap: &'a Option, + ) -> Self { + let mut field_arguments = BTreeMap::default(); + // assert_eq!(decl.fields.len(), operand_values.len()); FIXME: Split + // into two assertion encoders: one that uses result value and one that + // usess field_arguments. + for (field, operand) in decl.fields.iter().zip(operand_values.into_iter()) { + assert!(field_arguments + .insert(field.name.clone(), operand) + .is_none()); + } + Self { + field_arguments, + heap, + result_value: None, + replace_self_with_result_value: false, + in_function: false, + } + } + + // FIXME: Code duplication. + fn pointer_deref_into_address<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + if let Some(deref_place) = place.get_last_dereferenced_pointer() { + let base_snapshot = self.expression_to_snapshot(lowerer, deref_place, true)?; + let ty = deref_place.get_type(); + lowerer.pointer_address(ty, base_snapshot, place.position()) + // match deref_place { + // vir_mid::Expression::Deref(deref) => { + // let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, true)?; + // let ty = deref.base.get_type(); + // assert!(ty.is_pointer()); + // lowerer.pointer_address(ty, base_snapshot, place.position()) + // } + // _ => unreachable!(), + // } + } else { + unreachable!() + } + // PlaceExpressionDomainEncoder::encode_expression(self, place, lowerer) + } + + pub(super) fn address_in_heap<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + let pointer = self.expression_to_snapshot(lowerer, pointer_place, true)?; + let address = + lowerer.pointer_address(pointer_place.get_type(), pointer, pointer_place.position())?; + let in_heap = vir_low::Expression::container_op_no_pos( + vir_low::ContainerOpKind::MapContains, + self.heap.as_ref().unwrap().ty.clone(), + vec![self.heap.clone().unwrap().into(), address], + ); + Ok(in_heap) + } + + // pub(in super::super::super) fn set_in_function(&mut self) { + // assert!(!self.in_function); + // self.in_function = true; + // } + + pub(in super::super::super) fn set_result_value( + &mut self, + result_value: vir_low::VariableDecl, + ) { + assert!(self.result_value.is_none()); + self.result_value = Some(result_value); + } + + pub(super) fn unset_result_value(&mut self) { + assert!(self.result_value.is_some()); + self.result_value = None; + } + + fn acc_predicate_to_snapshot_precondition<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + acc_predicate: &vir_mid::AccPredicate, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + assert!(expect_math_bool); + let expression = match &*acc_predicate.predicate { + vir_mid::Predicate::OwnedNonAliased(_predicate) => { + unimplemented!("Outdated code? TODO: Remove?"); + // let ty = predicate.place.get_type(); + // let place = lowerer.encode_expression_as_place(&predicate.place)?; + // // eprintln!("predicate: {}", predicate); + // let root_address = self.pointer_deref_into_address(lowerer, &predicate.place)?; + // // eprintln!("root_address2: {}", root_address); + // // let deref = predicate.place.clone().unwrap_deref(); + // // let base_snapshot = + // // self.expression_to_snapshot(lowerer, &deref.base, expect_math_bool)?; + // // let snapshot = lowerer.pointer_target_snapshot_in_heap( + // // deref.base.get_type(), + // // self.heap.clone(), + // // base_snapshot, + // // deref.position, + // // )?; + + // let snapshot = if config::use_snapshot_parameters_in_predicates() { + // self.expression_to_snapshot(lowerer, &predicate.place, expect_math_bool)? + // } else { + // // FIXME: cleanup code + // if lowerer.use_heap_variable()? { + // let deref = predicate.place.clone().unwrap_deref(); + // let base_snapshot = + // self.expression_to_snapshot(lowerer, &deref.base, expect_math_bool)?; + + // lowerer.pointer_target_snapshot_in_heap( + // deref.base.get_type(), + // self.heap.clone().unwrap(), + // base_snapshot, + // deref.position, + // )? + // } else { + // true.into() + // } + // }; + + // if lowerer.use_heap_variable()? { + // // let snapshot = self.expression_to_snapshot(lowerer, &predicate.place, expect_math_bool)?; + // lowerer.owned_non_aliased( + // CallContext::BuiltinMethod, + // ty, + // ty, + // place, + // root_address, + // snapshot, + // None, + // )? + // } else { + // lowerer.owned_non_aliased( + // CallContext::BuiltinMethod, + // ty, + // ty, + // place, + // root_address, + // snapshot, + // None, + // )? + // } + } + vir_mid::Predicate::MemoryBlockHeap(predicate) => { + let place = lowerer.encode_expression_as_place(&predicate.address)?; + let root_address = self.pointer_deref_into_address(lowerer, &predicate.address)?; + use vir_low::macros::*; + let compute_address = ty!(Address); + let address = expr! { + ComputeAddress::compute_address([place], [root_address]) + }; + let size = + self.expression_to_snapshot(lowerer, &predicate.size, expect_math_bool)?; + lowerer.encode_memory_block_stack_acc(address, size, acc_predicate.position)? + } + vir_mid::Predicate::MemoryBlockHeapDrop(predicate) => { + // FIXME: Why this does not match the encoding of MemoryBlockHeap? + let address = self.pointer_deref_into_address(lowerer, &predicate.address)?; + let size = + self.expression_to_snapshot(lowerer, &predicate.size, expect_math_bool)?; + lowerer.encode_memory_block_heap_drop_acc(address, size, acc_predicate.position)? + } + _ => unimplemented!("{acc_predicate}"), + }; + Ok(expression) + } + + fn acc_predicate_to_snapshot_postcondition<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + acc_predicate: &vir_mid::AccPredicate, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + assert!(expect_math_bool); + let expression = match &*acc_predicate.predicate { + vir_mid::Predicate::OwnedNonAliased(predicate) => { + let position = predicate.position; + let ty = predicate.place.get_type(); + let place = lowerer.encode_expression_as_place(&predicate.place)?; + let old_value = self.replace_self_with_result_value; + self.replace_self_with_result_value = true; + let root_address_self = + self.pointer_deref_into_address(lowerer, &predicate.place)?; + self.replace_self_with_result_value = old_value; + let snap_call_self = lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + ty, + ty, + place.clone(), + root_address_self, + position, + )?; + if self.in_function { + let snap_call_result_value = + self.expression_to_snapshot(lowerer, &predicate.place, expect_math_bool)?; + vir_low::Expression::equals(snap_call_result_value, snap_call_self) + } else { + let root_address_parameter = + self.pointer_deref_into_address(lowerer, &predicate.place)?; + let snap_call_parameter = lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + ty, + ty, + place, + root_address_parameter, + position, + )?; + vir_low::Expression::equals( + snap_call_parameter, + vir_low::Expression::labelled_old(None, snap_call_self, position), + ) + } + } + vir_mid::Predicate::MemoryBlockHeap(_) | vir_mid::Predicate::MemoryBlockHeapDrop(_) => { + true.into() + } + _ => unimplemented!("{acc_predicate}"), + }; + Ok(expression) + } +} + +// impl<'a> PlaceExpressionDomainEncoder for AssertionEncoder<'a> { +// fn domain_name(&mut self, lowerer: &mut Lowerer) -> &str { +// lowerer.address_domain() +// } + +// fn encode_local( +// &mut self, +// local: &vir_mid::expression::Local, +// lowerer: &mut Lowerer, +// ) -> SpannedEncodingResult { +// lowerer.root_address(local, &None) +// } + +// fn encode_deref( +// &mut self, +// deref: &vir_mid::expression::Deref, +// lowerer: &mut Lowerer, +// _arg: vir_low::Expression, +// ) -> SpannedEncodingResult { +// let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, true)?; +// let ty = deref.base.get_type(); +// let result = if ty.is_reference() { +// lowerer.reference_address(ty, base_snapshot, deref.position)? +// } else { +// lowerer.pointer_address(ty, base_snapshot, deref.position)? +// }; +// Ok(result) +// } + +// fn encode_labelled_old( +// &mut self, +// _expression: &vir_mid::expression::LabelledOld, +// _lowerer: &mut Lowerer, +// ) -> SpannedEncodingResult { +// todo!() +// } + +// fn encode_array_index_axioms( +// &mut self, +// _base_type: &vir_mid::Type, +// _lowerer: &mut Lowerer, +// ) -> SpannedEncodingResult<()> { +// todo!() +// } +// } + +impl<'a, 'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for AssertionEncoder<'a> { + fn variable_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + variable: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult { + if self.replace_self_with_result_value || self.in_function { + assert!(variable.is_self_variable()); + Ok(self.result_value.clone().unwrap()) + } else { + Ok(vir_low::VariableDecl { + name: variable.name.clone(), + ty: self.type_to_snapshot(lowerer, &variable.ty)?, + }) + } + } + + fn labelled_old_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _old: &vir_mid::LabelledOld, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn func_app_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _app: &vir_mid::FuncApp, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn acc_predicate_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + acc_predicate: &vir_mid::AccPredicate, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let expression = if self.result_value.is_some() { + self.acc_predicate_to_snapshot_postcondition(lowerer, acc_predicate, expect_math_bool)? + } else { + self.acc_predicate_to_snapshot_precondition(lowerer, acc_predicate, expect_math_bool)? + }; + Ok(expression) + } + + fn field_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + field: &vir_mid::Field, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + match &*field.base { + vir_mid::Expression::Local(local) + if !self.replace_self_with_result_value && !self.in_function => + { + assert!(local.variable.is_self_variable()); + Ok(self.field_arguments[&field.field.name].clone()) + // if self.replace_self_with_result_value { + // Ok(self.result_value.clone().unwrap().into()) + // } else + // {Ok(self.field_arguments[&field.field.name].clone())} + } + _ => { + // FIXME: Code duplication because Rust does not have syntax for calling + // overriden methods. + let base_snapshot = + self.expression_to_snapshot(lowerer, &field.base, expect_math_bool)?; + let result = if field.field.is_discriminant() { + let ty = field.base.get_type(); + // FIXME: Create a method for obtainging the discriminant type. + let type_decl = lowerer.encoder.get_type_decl_mid(ty)?; + let enum_decl = type_decl.unwrap_enum(); + let discriminant_call = + lowerer.obtain_enum_discriminant(base_snapshot, ty, field.position)?; + lowerer.construct_constant_snapshot( + &enum_decl.discriminant_type, + discriminant_call, + field.position, + )? + } else { + lowerer.obtain_struct_field_snapshot( + field.base.get_type(), + &field.field, + base_snapshot, + field.position, + )? + }; + self.ensure_bool_expression(lowerer, field.get_type(), result, expect_math_bool) + } + } + } + + fn deref_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + deref: &vir_mid::Deref, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, expect_math_bool)?; + let ty = deref.base.get_type(); + let result = if ty.is_reference() { + lowerer.reference_target_current_snapshot(ty, base_snapshot, Default::default())? + } else if lowerer.use_heap_variable()? { + lowerer.pointer_target_snapshot_in_heap( + deref.base.get_type(), + self.heap.clone().unwrap(), + base_snapshot, + deref.position, + )? + } else { + // eprintln!("deref: {}", deref); + // unimplemented!() + true.into() // TODO + }; + self.ensure_bool_expression(lowerer, deref.get_type(), result, expect_math_bool) + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_snapshot: &vir_mid::Expression, + ) -> SpannedEncodingResult { + unimplemented!() + } + + fn call_context(&self) -> CallContext { + CallContext::BuiltinMethod + } + + fn push_bound_variables( + &mut self, + _variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + todo!() + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + todo!() + } + + // fn unfolding_to_snapshot( + // &mut self, + // _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // _unfolding: &vir_mid::Unfolding, + // _expect_math_bool: bool, + // ) -> SpannedEncodingResult { + // todo!() + // } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/change_unique_ref_place.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/change_unique_ref_place.rs index 2a11f96b51b..cd022b76a7c 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/change_unique_ref_place.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/change_unique_ref_place.rs @@ -6,7 +6,7 @@ use crate::encoder::{ lifetimes::LifetimesInterface, lowerer::Lowerer, places::PlacesInterface, - predicates::UniqueRefUseBuilder, + predicates::PredicatesOwnedInterface, references::ReferencesInterface, snapshots::{IntoPureSnapshot, IntoSnapshot}, }, @@ -72,27 +72,27 @@ impl<'l, 'p, 'v, 'tcx> ChangeUniqueRefPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super::super::super) fn add_same_address_precondition( &mut self, ) -> SpannedEncodingResult<()> { - use vir_low::macros::*; - let root_address = self.inner.lowerer.reference_address( - self.inner.ty, - self.source_snapshot.clone().into(), - self.inner.position, - )?; - let deref_source_place = self - .inner - .lowerer - .reference_deref_place(self.source_place.clone().into(), self.inner.position)?; - let deref_target_place = self - .inner - .lowerer - .reference_deref_place(self.target_place.clone().into(), self.inner.position)?; - let source_address = - self.compute_address_expression(deref_source_place, root_address.clone()); - let target_address = self.compute_address_expression(deref_target_place, root_address); - let expression = expr! { - [target_address] == [source_address] - }; - self.add_precondition(expression); + // use vir_low::macros::*; + // let root_address = self.inner.lowerer.reference_address( + // self.inner.ty, + // self.source_snapshot.clone().into(), + // self.inner.position, + // )?; + // let deref_source_place = self + // .inner + // .lowerer + // .reference_deref_place(self.source_place.clone().into(), self.inner.position)?; + // let deref_target_place = self + // .inner + // .lowerer + // .reference_deref_place(self.target_place.clone().into(), self.inner.position)?; + // let source_address = + // self.compute_address_expression(deref_source_place, root_address.clone()); + // let target_address = self.compute_address_expression(deref_target_place, root_address); + // let expression = expr! { + // [target_address] == [source_address] + // }; + // self.add_precondition(expression); Ok(()) } @@ -106,6 +106,11 @@ impl<'l, 'p, 'v, 'tcx> ChangeUniqueRefPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.source_snapshot.clone().into(), self.inner.position, )?; + let slice_len = self.inner.lowerer.reference_slice_len( + self.inner.ty, + self.source_snapshot.clone().into(), + self.inner.position, + )?; let deref_source_place = self .inner .lowerer @@ -123,7 +128,7 @@ impl<'l, 'p, 'v, 'tcx> ChangeUniqueRefPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.source_snapshot.clone().into(), self.inner.position, )?; - let final_snapshot = self.inner.lowerer.reference_target_final_snapshot( + let _final_snapshot = self.inner.lowerer.reference_target_final_snapshot( self.inner.ty, self.source_snapshot.clone().into(), self.inner.position, @@ -133,36 +138,32 @@ impl<'l, 'p, 'v, 'tcx> ChangeUniqueRefPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { .lowerer .encode_lifetime_const_into_pure_is_alive_variable(lifetime)?; let lifetime = lifetime.to_pure_snapshot(self.inner.lowerer)?; - let mut builder = UniqueRefUseBuilder::new( - self.lowerer(), + let source_expression = self.inner.lowerer.unique_ref_with_current_snapshot( CallContext::BuiltinMethod, &target_type, &target_type_decl, deref_source_place, root_address.clone(), current_snapshot.clone(), - final_snapshot.clone(), lifetime.clone().into(), + slice_len.clone(), + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let source_expression = builder.build(); self.add_precondition(expr! { [lifetime_alive.clone().into()] ==> [source_expression] }); - let mut builder = UniqueRefUseBuilder::new( - self.lowerer(), + let target_expression = self.inner.lowerer.unique_ref_with_current_snapshot( CallContext::BuiltinMethod, &target_type, &target_type_decl, deref_target_place, root_address, current_snapshot, - final_snapshot, lifetime.into(), + slice_len, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let target_expression = builder.build(); self.add_postcondition(expr! { [lifetime_alive.into()] ==> [target_expression] }); Ok(()) } diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/common.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/common.rs index 79631192179..20d7abf8660 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/common.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/common.rs @@ -232,13 +232,14 @@ where fn add_join_memory_block_call( &mut self, - place: &vir_low::VariableDecl, - root_address: &vir_low::VariableDecl, + _place: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, + // root_address: &vir_low::VariableDecl, snapshot: &vir_low::VariableDecl, ) -> SpannedEncodingResult<()> { let inner = self.inner(); inner.lowerer.encode_memory_block_join_method(inner.ty)?; - let address = inner.compute_address(place, root_address); + // let address = inner.compute_address(place, root_address); let discriminant_call = inner.discriminant(snapshot)?; let mut builder = BuiltinMethodCallBuilder::new( inner.lowerer, @@ -248,7 +249,7 @@ where inner.type_decl, inner.position, )?; - builder.add_argument(address); + builder.add_argument(address.clone().into()); builder.add_full_permission_argument(); if let Some(discriminant_call) = discriminant_call { builder.add_argument(discriminant_call); diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/copy_place.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/copy_place.rs index 2ae1cebfedc..9eab245d082 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/copy_place.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/copy_place.rs @@ -8,7 +8,6 @@ use crate::encoder::{ builtin_methods::{BuiltinMethodCallsInterface, BuiltinMethodsInterface, CallContext}, lowerer::Lowerer, places::PlacesInterface, - predicates::PredicatesOwnedInterface, snapshots::SnapshotValuesInterface, }, }; @@ -63,7 +62,7 @@ impl<'l, 'p, 'v, 'tcx> CopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .inner .parameters - .push(self.inner.target_root_address.clone()); + .push(self.inner.target_address.clone()); self.inner .inner .parameters @@ -71,7 +70,7 @@ impl<'l, 'p, 'v, 'tcx> CopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .inner .parameters - .push(self.inner.source_root_address.clone()); + .push(self.inner.source_address.clone()); self.inner .inner .parameters @@ -104,21 +103,40 @@ impl<'l, 'p, 'v, 'tcx> CopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super::super::super) fn create_source_owned( &mut self, ) -> SpannedEncodingResult { - self.inner.inner.lowerer.owned_non_aliased( - CallContext::BuiltinMethod, - self.inner.inner.ty, - self.inner.inner.type_decl, - self.inner.source_place.clone().into(), - self.inner.source_root_address.clone().into(), - self.inner.source_snapshot.clone().into(), - Some(self.source_permission_amount.clone().into()), - ) + self.inner + .create_source_owned(false, Some(self.source_permission_amount.clone().into())) + // self.inner.inner.lowerer.owned_non_aliased( + // CallContext::BuiltinMethod, + // self.inner.inner.ty, + // self.inner.inner.type_decl, + // self.inner.source_place.clone().into(), + // self.inner.source_address.clone().into(), + // Some(self.source_permission_amount.clone().into()), + // self.inner.inner.position, + // ) + } + + pub(in super::super::super::super) fn create_source_owned_predicate( + &mut self, + ) -> SpannedEncodingResult { + self.inner + .create_source_owned(true, Some(self.source_permission_amount.clone().into())) + // self.inner.inner.lowerer.owned_non_aliased( + // CallContext::BuiltinMethod, + // self.inner.inner.ty, + // self.inner.inner.type_decl, + // self.inner.source_place.clone().into(), + // self.inner.source_address.clone().into(), + // Some(self.source_permission_amount.clone().into()), + // self.inner.inner.position, + // ) } pub(in super::super::super::super) fn create_target_owned( &mut self, + must_be_predicate: bool, ) -> SpannedEncodingResult { - self.inner.create_target_owned() + self.inner.create_target_owned(must_be_predicate) } pub(in super::super::super::super) fn add_target_validity_postcondition( @@ -176,9 +194,9 @@ impl<'l, 'p, 'v, 'tcx> CopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { &field.ty, self.inner.inner.position, target_field_place, - self.inner.target_root_address.clone().into(), + self.inner.target_address.clone().into(), source_field_place, - self.inner.source_root_address.clone().into(), + self.inner.source_address.clone().into(), source_field_snapshot, self.source_permission_amount.clone().into(), )?; diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/duplicate_frac_ref.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/duplicate_frac_ref.rs index 58f11ea5cff..7f00ea65bc7 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/duplicate_frac_ref.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/duplicate_frac_ref.rs @@ -5,7 +5,7 @@ use crate::encoder::{ builtin_methods::CallContext, lowerer::Lowerer, places::PlacesInterface, - predicates::FracRefUseBuilder, + predicates::PredicatesOwnedInterface, references::ReferencesInterface, snapshots::{IntoPureSnapshot, IntoSnapshot}, }, @@ -98,7 +98,7 @@ impl<'l, 'p, 'v, 'tcx> DuplicateFracRefMethodBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super::super::super) fn add_frac_ref_pre_postcondition( &mut self, ) -> SpannedEncodingResult<()> { - let root_address = self.inner.lowerer.reference_address( + let address = self.inner.lowerer.reference_address( self.inner.ty, self.source_snapshot.clone().into(), self.inner.position, @@ -115,40 +115,64 @@ impl<'l, 'p, 'v, 'tcx> DuplicateFracRefMethodBuilder<'l, 'p, 'v, 'tcx> { .inner .lowerer .reference_deref_place(self.target_place.clone().into(), self.inner.position)?; - let current_snapshot = self.inner.lowerer.reference_target_current_snapshot( + let _current_snapshot = self.inner.lowerer.reference_target_current_snapshot( self.inner.ty, self.source_snapshot.clone().into(), self.inner.position, )?; let lifetime = lifetime.to_pure_snapshot(self.inner.lowerer)?; - let mut builder = FracRefUseBuilder::new( - self.lowerer(), + // let mut builder = FracRefUseBuilder::new( + // self.lowerer(), + // CallContext::BuiltinMethod, + // &target_type, + // &target_type_decl, + // deref_source_place, + // address.clone(), + // // current_snapshot.clone(), + // lifetime.clone().into(), + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // let source_expression = builder.build()?; + let TODO_source_slice_len = None; + let source_expression = self.inner.lowerer.frac_ref( CallContext::BuiltinMethod, &target_type, &target_type_decl, deref_source_place, - root_address.clone(), - current_snapshot.clone(), + address.clone(), lifetime.clone().into(), + TODO_source_slice_len, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let source_expression = builder.build(); self.add_precondition(source_expression.clone()); self.add_postcondition(source_expression); - let mut builder = FracRefUseBuilder::new( - self.lowerer(), + // let mut builder = FracRefUseBuilder::new( + // self.lowerer(), + // CallContext::BuiltinMethod, + // &target_type, + // &target_type_decl, + // deref_target_place, + // address, + // // current_snapshot, + // lifetime.into(), + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // let target_expression = builder.build(); + let TODO_target_slice_len = None; + let target_expression = self.inner.lowerer.frac_ref( CallContext::BuiltinMethod, &target_type, &target_type_decl, deref_target_place, - root_address, - current_snapshot, + address, lifetime.into(), + TODO_target_slice_len, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let target_expression = builder.build(); self.add_postcondition(target_expression); Ok(()) } diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_into.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_into.rs index 7e753f27d06..536850443eb 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_into.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_into.rs @@ -9,7 +9,7 @@ use crate::encoder::{ }, lowerer::Lowerer, places::PlacesInterface, - predicates::OwnedNonAliasedUseBuilder, + predicates::PredicatesOwnedInterface, snapshots::{IntoSnapshot, SnapshotValuesInterface}, }, }; @@ -21,7 +21,7 @@ use vir_crate::{ pub(in super::super::super::super) struct IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { inner: BuiltinMethodBuilder<'l, 'p, 'v, 'tcx>, place: vir_low::VariableDecl, - root_address: vir_low::VariableDecl, + address: vir_low::VariableDecl, snapshot: vir_low::VariableDecl, } @@ -42,15 +42,15 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { type_decl: &'l vir_mid::TypeDecl, error_kind: BuiltinMethodKind, ) -> SpannedEncodingResult { - let place = vir_low::VariableDecl::new("place", lowerer.place_type()?); - let root_address = vir_low::VariableDecl::new("root_address", lowerer.address_type()?); + let place = vir_low::VariableDecl::new("place", lowerer.place_option_type()?); + let address = vir_low::VariableDecl::new("address", lowerer.address_type()?); let snapshot = vir_low::VariableDecl::new("snapshot", ty.to_snapshot(lowerer)?); let inner = BuiltinMethodBuilder::new(lowerer, kind, method_name, ty, type_decl, error_kind)?; Ok(Self { inner, place, - root_address, + address, snapshot, }) } @@ -63,7 +63,7 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { &mut self, ) -> SpannedEncodingResult<()> { self.inner.parameters.push(self.place.clone()); - self.inner.parameters.push(self.root_address.clone()); + self.inner.parameters.push(self.address.clone()); self.inner.parameters.push(self.snapshot.clone()); self.create_lifetime_parameters()?; self.create_const_parameters()?; @@ -73,25 +73,37 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { // FIXME: Remove code duplication with create_source_owned. pub(in super::super::super::super) fn create_owned( &mut self, + exclude_snapshot_equality: bool, ) -> SpannedEncodingResult { - let mut builder = OwnedNonAliasedUseBuilder::new( - self.inner.lowerer, - CallContext::BuiltinMethod, - self.inner.ty, - self.inner.type_decl, - self.place.clone().into(), - self.root_address.clone().into(), - self.snapshot.clone().into(), - )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - Ok(builder.build()) + if exclude_snapshot_equality { + self.inner.lowerer.owned_non_aliased_full_vars( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + &self.place, + &self.address, + self.inner.position, + ) + } else { + self.inner + .lowerer + .owned_non_aliased_full_vars_with_snapshot( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + &self.place, + &self.address, + &self.snapshot, + self.inner.position, + ) + } } pub(in super::super::super::super) fn create_target_memory_block( &mut self, ) -> SpannedEncodingResult { - self.create_memory_block(self.compute_address(&self.place, &self.root_address)) + // self.create_memory_block(self.compute_address(&self.place, &self.address)) + self.create_memory_block(self.address.clone().into()) } pub(in super::super::super::super) fn add_into_memory_block_call_for_field( @@ -104,6 +116,12 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { self.place.clone().into(), self.inner.position, )?; + let field_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + field, + self.address.clone().into(), + self.inner.position, + )?; let field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( self.inner.ty, field, @@ -122,7 +140,7 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.position, )?; builder.add_argument(field_place); - builder.add_argument(self.root_address.clone().into()); + builder.add_argument(field_address); builder.add_argument(field_snapshot); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; @@ -152,6 +170,12 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { self.place.clone().into(), self.inner.position, )?; + let variant_address = self.inner.lowerer.encode_enum_variant_address( + self.inner.ty, + &variant_index, + self.address.clone().into(), + self.inner.position, + )?; let variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( self.inner.ty, &variant_index, @@ -171,7 +195,7 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.position, )?; builder.add_argument(variant_place); - builder.add_argument(self.root_address.clone().into()); + builder.add_argument(variant_address); builder.add_argument(variant_snapshot); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; @@ -196,6 +220,12 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { self.place.clone().into(), self.inner.position, )?; + let discriminant_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + &discriminant_field, + self.address.clone().into(), + self.inner.position, + )?; let discriminant_call = self.inner.lowerer.obtain_enum_discriminant( self.snapshot.clone().into(), self.inner.ty, @@ -215,7 +245,7 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.position, )?; builder.add_argument(discriminant_place); - builder.add_argument(self.root_address.clone().into()); + builder.add_argument(discriminant_address); builder.add_argument(discriminant_snashot); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; @@ -228,6 +258,6 @@ impl<'l, 'p, 'v, 'tcx> IntoMemoryBlockMethodBuilder<'l, 'p, 'v, 'tcx> { &mut self, ) -> SpannedEncodingResult<()> { self.inner - .add_join_memory_block_call(&self.place, &self.root_address, &self.snapshot) + .add_join_memory_block_call(&self.place, &self.address, &self.snapshot) } } diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_join.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_join.rs new file mode 100644 index 00000000000..3437054cdb1 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_join.rs @@ -0,0 +1,377 @@ +use super::{ + common::{BuiltinMethodBuilder, BuiltinMethodBuilderMethods}, + memory_block_range_split_join_common::MemoryBlockRangeSplitJoinMethodBuilder, + memory_block_split_join_common::BuiltinMethodSplitJoinBuilderMethods, +}; +use crate::encoder::{ + errors::{BuiltinMethodKind, SpannedEncodingResult}, + middle::core_proof::{ + addresses::AddressesInterface, lowerer::Lowerer, + predicates::PredicatesMemoryBlockInterface, snapshots::SnapshotValuesInterface, + type_layouts::TypeLayoutsInterface, + }, +}; +use vir_crate::{ + common::expression::{BinaryOperationHelpers, QuantifierHelpers}, + low::{self as vir_low}, + middle as vir_mid, +}; + +pub(in super::super::super::super) struct MemoryBlockRangeJoinMethodBuilder<'l, 'p, 'v, 'tcx> { + inner: MemoryBlockRangeSplitJoinMethodBuilder<'l, 'p, 'v, 'tcx>, +} + +impl<'l, 'p, 'v, 'tcx> BuiltinMethodBuilderMethods<'l, 'p, 'v, 'tcx> + for MemoryBlockRangeJoinMethodBuilder<'l, 'p, 'v, 'tcx> +{ + fn inner(&mut self) -> &mut BuiltinMethodBuilder<'l, 'p, 'v, 'tcx> { + self.inner.inner() + } +} + +impl<'l, 'p, 'v, 'tcx> BuiltinMethodSplitJoinBuilderMethods<'l, 'p, 'v, 'tcx> + for MemoryBlockRangeJoinMethodBuilder<'l, 'p, 'v, 'tcx> +{ +} + +impl<'l, 'p, 'v, 'tcx> MemoryBlockRangeJoinMethodBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + kind: vir_low::MethodKind, + method_name: &'l str, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + error_kind: BuiltinMethodKind, + ) -> SpannedEncodingResult { + Ok(Self { + inner: MemoryBlockRangeSplitJoinMethodBuilder::new( + lowerer, + kind, + method_name, + ty, + type_decl, + error_kind, + )?, + }) + } + + pub(in super::super::super::super) fn build(self) -> vir_low::MethodDecl { + self.inner.build() + } + + pub(in super::super::super::super) fn create_parameters( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.create_parameters() + } + + // pub(in super::super::super::super) fn add_permission_amount_positive_precondition( + // &mut self, + // ) -> SpannedEncodingResult<()> { + // self.inner.add_permission_amount_positive_precondition() + // } + + pub(in super::super::super::super) fn add_whole_memory_block_postcondition( + &mut self, + ) -> SpannedEncodingResult<()> { + let memory_block = self.inner.create_whole_block_acc()?; + self.add_postcondition(memory_block); + Ok(()) + } + + pub(in super::super::super::super) fn add_memory_block_range_precondition( + &mut self, + ) -> SpannedEncodingResult<()> { + let memory_block_range = self.inner.create_memory_block_range_acc()?; + self.add_precondition(memory_block_range); + Ok(()) + } + + // FIXME: Code duplication. + pub(in super::super::super::super) fn add_byte_values_preserved_postcondition( + &mut self, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let element_size = self + .inner + .inner + .lowerer + .encode_type_size_expression2(self.inner.inner.ty, self.inner.inner.type_decl)?; + let length = self.inner.length()?; + let whole_size = self + .inner + .inner + .lowerer + .encode_type_size_expression_repetitions( + self.inner.inner.ty, + self.inner.inner.type_decl, + length, + self.inner.inner.position, + )?; + let size_type = self.inner.inner.lowerer.size_type_mid()?; + var_decls! { + index: Int, + byte_index: Int + } + let address: vir_low::Expression = self.inner.address.clone().into(); + let element_address = self.inner.inner.lowerer.address_offset( + element_size.clone(), + address.clone(), + index.clone().into(), + self.inner.inner.position, + )?; + let start_index = self.inner.inner.lowerer.obtain_constant_value( + &size_type, + self.inner.start_index.clone().into(), + self.inner.inner.position, + )?; + let end_index = self.inner.inner.lowerer.obtain_constant_value( + &size_type, + self.inner.end_index.clone().into(), + self.inner.inner.position, + )?; + let element_bytes = self + .inner + .inner + .lowerer + .encode_memory_block_bytes_expression(element_address, element_size.clone())?; + let whole_bytes = self + .inner + .inner + .lowerer + .encode_memory_block_bytes_expression(address, whole_size)?; + let read_element_byte = self.inner.inner.lowerer.encode_read_byte_expression_int( + vir_low::Expression::labelled_old( + None, + element_bytes.clone(), + self.inner.inner.position, + ), + byte_index.clone().into(), + self.inner.inner.position, + )?; + let block_size = self.inner.inner.lowerer.obtain_constant_value( + &size_type, + element_size.clone(), + self.inner.inner.position, + )?; + let block_start_index = vir_low::Expression::multiply(block_size, index.clone().into()); + let whole_byte_index = + vir_low::Expression::add(block_start_index, byte_index.clone().into()); + // let whole_byte_index = self.inner.inner.lowerer.create_domain_func_app( + // "Arithmetic", + // "mul_add", + // vec![block_size, index.clone().into(), byte_index.clone().into()], + // vir_low::Type::Int, + // self.inner.inner.position, + // )?; + let read_whole_byte = self.inner.inner.lowerer.encode_read_byte_expression_int( + whole_bytes, + whole_byte_index, + self.inner.inner.position, + )?; + let element_size_int = self.inner.inner.lowerer.obtain_constant_value( + &size_type, + element_size, + self.inner.inner.position, + )?; + let body = expr!( + ((([start_index] <= index) && (index < [end_index])) && + (([0.into()] <= byte_index) && (byte_index < [element_size_int]))) ==> + ([read_element_byte] == [read_whole_byte]) + ); + let trigger = self.inner.inner.lowerer.encode_read_byte_expression_int( + element_bytes, + byte_index.clone().into(), + self.inner.inner.position, + )?; + let expression = vir_low::Expression::forall( + vec![index, byte_index], + vec![vir_low::Trigger::new(vec![trigger])], + body, + ); + self.add_postcondition(expression); + Ok(()) + } + + // pub(in super::super::super::super) fn add_padding_memory_block_precondition( + // &mut self, + // ) -> SpannedEncodingResult<()> { + // let expression = self.inner.create_padding_memory_block_acc()?; + // self.add_precondition(expression); + // Ok(()) + // } + + // pub(in super::super::super::super) fn add_field_memory_block_precondition( + // &mut self, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult<()> { + // let field_block = self.inner.create_field_memory_block_acc(field)?; + // self.add_precondition(field_block); + // Ok(()) + // } + + // pub(in super::super::super::super) fn add_discriminant_precondition( + // &mut self, + // decl: &vir_mid::type_decl::Enum, + // ) -> SpannedEncodingResult<()> { + // let discriminant_block = self.inner.create_discriminant_acc(decl)?; + // self.add_precondition(discriminant_block); + // Ok(()) + // } + + // pub(in super::super::super::super) fn add_variant_memory_block_precondition( + // &mut self, + // discriminant_value: vir_mid::DiscriminantValue, + // variant: &vir_mid::type_decl::Struct, + // ) -> SpannedEncodingResult<()> { + // let expression = self + // .inner + // .create_variant_memory_block_acc(discriminant_value, variant)?; + // self.add_precondition(expression); + // Ok(()) + // } + + // pub(in super::super::super::super) fn create_field_to_bytes_equality( + // &mut self, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult { + // let expression = self.inner.create_field_to_bytes_equality(field)?; + // Ok(vir_low::Expression::labelled_old_no_pos(None, expression)) + // } + + // pub(in super::super::super::super) fn add_fields_to_bytes_equalities_postcondition( + // &mut self, + // field_to_bytes_equalities: Vec, + // ) -> SpannedEncodingResult<()> { + // use vir_low::macros::*; + // let address = self.inner.address(); + // let inner = self.inner(); + // let to_bytes = ty! { Bytes }; + // let ty = inner.ty; + // let size_of = inner + // .lowerer + // .encode_type_size_expression2(inner.ty, inner.type_decl)?; + // let memory_block_bytes = inner + // .lowerer + // .encode_memory_block_bytes_expression(address, size_of)?; + // let bytes_quantifier = expr! { + // forall( + // snapshot: {ty.to_snapshot(inner.lowerer)?} :: + // [ { (Snap::to_bytes(snapshot)) } ] + // [ field_to_bytes_equalities.into_iter().conjoin() ] ==> + // ([memory_block_bytes] == (Snap::to_bytes(snapshot))) + // ) + // }; + // self.add_postcondition(bytes_quantifier); + // Ok(()) + // } + + // pub(in super::super::super::super) fn create_variant_to_bytes_equality( + // &mut self, + // discriminant_value: vir_mid::DiscriminantValue, + // variant: &vir_mid::type_decl::Struct, + // decl: &vir_mid::type_decl::Enum, + // safety: vir_mid::ty::EnumSafety, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let discriminant = self.inner.discriminant.as_ref().unwrap(); + // let ty = self.inner.inner.ty; + // let to_bytes = ty! { Bytes }; + // let snapshot: vir_low::Expression = + // var! { snapshot: {self.inner.inner.ty.to_snapshot(self.inner.inner.lowerer)?} }.into(); + // let variant_index = variant.name.clone().into(); + // let variant_snapshot = self.inner.inner.lowerer.obtain_enum_variant_snapshot( + // ty, + // &variant_index, + // snapshot.clone(), + // self.inner.inner.position, + // )?; + // let variant_address = self.inner.inner.lowerer.encode_enum_variant_address( + // self.inner.inner.ty, + // &variant_index, + // self.inner.address.clone().into(), + // self.inner.inner.position, + // )?; + // let variant_type = &self.inner.inner.ty.clone().variant(variant_index); + // let variant_size_of = self + // .inner + // .inner + // .lowerer + // .encode_type_size_expression2(variant_type, variant)?; + // let memory_block_variant_bytes = self + // .inner + // .inner + // .lowerer + // .encode_memory_block_bytes_expression(variant_address, variant_size_of)?; + // let memory_block_bytes = self + // .inner + // .inner + // .create_memory_block_bytes(self.inner.address.clone().into())?; + // let discriminant_to_bytes = if safety.is_enum() { + // let discriminant_type = &decl.discriminant_type; + // let discriminant_size_of = self + // .inner + // .inner + // .lowerer + // .encode_type_size_expression2(&decl.discriminant_type, &decl.discriminant_type)?; + // let discriminant_field = decl.discriminant_field(); + // let discriminant_address = self.inner.inner.lowerer.encode_field_address( + // self.inner.inner.ty, + // &discriminant_field, + // self.inner.address.clone().into(), + // self.inner.inner.position, + // )?; + // let memory_block_discriminant_bytes = self + // .inner + // .inner + // .lowerer + // .encode_memory_block_bytes_expression(discriminant_address, discriminant_size_of)?; + // let discriminant_call = self.inner.inner.lowerer.obtain_enum_discriminant( + // snapshot.clone(), + // self.inner.inner.ty, + // self.inner.inner.position, + // )?; + // let discriminant_snapshot = self.inner.inner.lowerer.construct_constant_snapshot( + // discriminant_type, + // discriminant_call, + // self.inner.inner.position, + // )?; + // expr! { + // ((old([memory_block_discriminant_bytes])) == + // (Snap::to_bytes([discriminant_snapshot]))) + // } + // } else { + // true.into() + // }; + // let expression = expr! { + // (discriminant == [discriminant_value.into()]) ==> + // ( + // ( + // [discriminant_to_bytes] && + // ((old([memory_block_variant_bytes])) == + // (Snap::to_bytes([variant_snapshot]))) + // ) ==> + // ([memory_block_bytes] == (Snap::to_bytes([snapshot]))) + // ) + // }; + // Ok(expression) + // } + + // pub(in super::super::super::super) fn add_variants_to_bytes_equalities_postcondition( + // &mut self, + // variant_to_bytes_equalities: Vec, + // ) -> SpannedEncodingResult<()> { + // use vir_low::macros::*; + // let ty = self.inner.inner.ty; + // let to_bytes = ty! { Bytes }; + // let expression = expr! { + // forall( + // snapshot: {ty.to_snapshot(self.inner.inner.lowerer)?} :: + // [ { (Snap::to_bytes(snapshot)) } ] + // [ variant_to_bytes_equalities.into_iter().conjoin() ] + // ) + // }; + // self.add_postcondition(expression); + // Ok(()) + // } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_split.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_split.rs new file mode 100644 index 00000000000..31e3814d6d1 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_split.rs @@ -0,0 +1,189 @@ +use super::{ + common::{BuiltinMethodBuilder, BuiltinMethodBuilderMethods}, + memory_block_range_split_join_common::MemoryBlockRangeSplitJoinMethodBuilder, + memory_block_split_join_common::BuiltinMethodSplitJoinBuilderMethods, +}; +use crate::encoder::{ + errors::{BuiltinMethodKind, SpannedEncodingResult}, + middle::core_proof::{ + addresses::AddressesInterface, lowerer::Lowerer, + predicates::PredicatesMemoryBlockInterface, snapshots::SnapshotValuesInterface, + type_layouts::TypeLayoutsInterface, + }, +}; +use vir_crate::{ + common::expression::{BinaryOperationHelpers, QuantifierHelpers}, + low::{self as vir_low}, + middle as vir_mid, +}; + +pub(in super::super::super::super) struct MemoryBlockRangeSplitMethodBuilder<'l, 'p, 'v, 'tcx> { + inner: MemoryBlockRangeSplitJoinMethodBuilder<'l, 'p, 'v, 'tcx>, +} + +impl<'l, 'p, 'v, 'tcx> BuiltinMethodBuilderMethods<'l, 'p, 'v, 'tcx> + for MemoryBlockRangeSplitMethodBuilder<'l, 'p, 'v, 'tcx> +{ + fn inner(&mut self) -> &mut BuiltinMethodBuilder<'l, 'p, 'v, 'tcx> { + self.inner.inner() + } +} + +impl<'l, 'p, 'v, 'tcx> BuiltinMethodSplitJoinBuilderMethods<'l, 'p, 'v, 'tcx> + for MemoryBlockRangeSplitMethodBuilder<'l, 'p, 'v, 'tcx> +{ +} + +impl<'l, 'p, 'v, 'tcx> MemoryBlockRangeSplitMethodBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + kind: vir_low::MethodKind, + method_name: &'l str, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + error_kind: BuiltinMethodKind, + ) -> SpannedEncodingResult { + Ok(Self { + inner: MemoryBlockRangeSplitJoinMethodBuilder::new( + lowerer, + kind, + method_name, + ty, + type_decl, + error_kind, + )?, + }) + } + + pub(in super::super::super::super) fn build(self) -> vir_low::MethodDecl { + self.inner.build() + } + + pub(in super::super::super::super) fn create_parameters( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.create_parameters() + } + + // pub(in super::super::super::super) fn add_permission_amount_positive_precondition( + // &mut self, + // ) -> SpannedEncodingResult<()> { + // self.inner.add_permission_amount_positive_precondition() + // } + + pub(in super::super::super::super) fn add_whole_memory_block_precondition( + &mut self, + ) -> SpannedEncodingResult<()> { + let memory_block = self.inner.create_whole_block_acc()?; + self.add_precondition(memory_block); + Ok(()) + } + + pub(in super::super::super::super) fn add_memory_block_range_postcondition( + &mut self, + ) -> SpannedEncodingResult<()> { + let memory_block_range = self.inner.create_memory_block_range_acc()?; + self.add_postcondition(memory_block_range); + Ok(()) + } + + // FIXME: Code duplication. + pub(in super::super::super::super) fn add_byte_values_preserved_postcondition( + &mut self, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let element_size = self + .inner + .inner + .lowerer + .encode_type_size_expression2(self.inner.inner.ty, self.inner.inner.type_decl)?; + let length = self.inner.length()?; + let whole_size = self + .inner + .inner + .lowerer + .encode_type_size_expression_repetitions( + self.inner.inner.ty, + self.inner.inner.type_decl, + length, + self.inner.inner.position, + )?; + let size_type = self.inner.inner.lowerer.size_type_mid()?; + var_decls! { + index: Int, + byte_index: Int + } + let address: vir_low::Expression = self.inner.address.clone().into(); + let element_address = self.inner.inner.lowerer.address_offset( + element_size.clone(), + address.clone(), + index.clone().into(), + self.inner.inner.position, + )?; + // let predicate = + // self.encode_memory_block_stack_acc(element_address.clone(), size.clone(), position)?; + let start_index = self.inner.inner.lowerer.obtain_constant_value( + &size_type, + self.inner.start_index.clone().into(), + self.inner.inner.position, + )?; + let end_index = self.inner.inner.lowerer.obtain_constant_value( + &size_type, + self.inner.end_index.clone().into(), + self.inner.inner.position, + )?; + let element_bytes = self + .inner + .inner + .lowerer + .encode_memory_block_bytes_expression(element_address, element_size.clone())?; + let whole_bytes = self + .inner + .inner + .lowerer + .encode_memory_block_bytes_expression(address, whole_size)?; + let read_element_byte = self.inner.inner.lowerer.encode_read_byte_expression_int( + element_bytes, + byte_index.clone().into(), + self.inner.inner.position, + )?; + let block_size = self.inner.inner.lowerer.obtain_constant_value( + &size_type, + element_size.clone(), + self.inner.inner.position, + )?; + let block_start_index = vir_low::Expression::multiply(block_size, index.clone().into()); + let whole_byte_index = + vir_low::Expression::add(block_start_index, byte_index.clone().into()); + // let whole_byte_index = self.inner.inner.lowerer.create_domain_func_app( + // "Arithmetic", + // "mul_add", + // vec![block_size, index.clone().into(), byte_index.clone().into()], + // vir_low::Type::Int, + // self.inner.inner.position, + // )?; + let read_whole_byte = self.inner.inner.lowerer.encode_read_byte_expression_int( + vir_low::Expression::labelled_old(None, whole_bytes, self.inner.inner.position), + whole_byte_index, + self.inner.inner.position, + )?; + let element_size_int = self.inner.inner.lowerer.obtain_constant_value( + &size_type, + element_size, + self.inner.inner.position, + )?; + let body = expr!( + ((([start_index] <= index) && (index < [end_index])) && + (([0.into()] <= byte_index) && (byte_index < [element_size_int]))) ==> + ([read_element_byte.clone()] == [read_whole_byte]) + ); + let trigger = read_element_byte; + let expression = vir_low::Expression::forall( + vec![index, byte_index], + vec![vir_low::Trigger::new(vec![trigger])], + body, + ); + self.add_postcondition(expression); + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_split_join_common.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_split_join_common.rs new file mode 100644 index 00000000000..ba0065af7f9 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_range_split_join_common.rs @@ -0,0 +1,256 @@ +use super::common::{BuiltinMethodBuilder, BuiltinMethodBuilderMethods}; +use crate::encoder::{ + errors::{BuiltinMethodKind, SpannedEncodingResult}, + middle::core_proof::{ + addresses::AddressesInterface, lowerer::Lowerer, + predicates::PredicatesMemoryBlockInterface, snapshots::SnapshotValuesInterface, + type_layouts::TypeLayoutsInterface, + }, +}; +use vir_crate::{ + low::{self as vir_low}, + middle as vir_mid, +}; + +pub(in super::super) struct MemoryBlockRangeSplitJoinMethodBuilder<'l, 'p, 'v, 'tcx> { + pub(super) inner: BuiltinMethodBuilder<'l, 'p, 'v, 'tcx>, + pub(super) address: vir_low::VariableDecl, + pub(super) start_index: vir_low::VariableDecl, + pub(super) end_index: vir_low::VariableDecl, +} + +impl<'l, 'p, 'v, 'tcx> BuiltinMethodBuilderMethods<'l, 'p, 'v, 'tcx> + for MemoryBlockRangeSplitJoinMethodBuilder<'l, 'p, 'v, 'tcx> +{ + fn inner(&mut self) -> &mut BuiltinMethodBuilder<'l, 'p, 'v, 'tcx> { + &mut self.inner + } +} + +pub(in super::super) trait BuiltinMethodSplitJoinBuilderMethods<'l, 'p, 'v, 'tcx>: + Sized + BuiltinMethodBuilderMethods<'l, 'p, 'v, 'tcx> +where + 'p: 'l, + 'v: 'p, + 'tcx: 'v, +{ +} + +impl<'l, 'p, 'v, 'tcx> MemoryBlockRangeSplitJoinMethodBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + kind: vir_low::MethodKind, + method_name: &'l str, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + error_kind: BuiltinMethodKind, + ) -> SpannedEncodingResult { + let address = vir_low::VariableDecl::new("address", lowerer.address_type()?); + let size_type = lowerer.size_type()?; + let start_index = vir_low::VariableDecl::new("start_index", size_type.clone()); + let end_index = vir_low::VariableDecl::new("end_index", size_type); + let inner = + BuiltinMethodBuilder::new(lowerer, kind, method_name, ty, type_decl, error_kind)?; + Ok(Self { + inner, + address, + start_index, + end_index, + }) + } + + pub(in super::super) fn build(self) -> vir_low::MethodDecl { + self.inner.build() + } + + // pub(in super::super) fn address(&self) -> vir_low::Expression { + // self.address.clone().into() + // } + + pub(in super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.address.clone()); + self.inner.parameters.push(self.start_index.clone()); + self.inner.parameters.push(self.end_index.clone()); + Ok(()) + } + + pub(in super::super) fn length(&mut self) -> SpannedEncodingResult { + let size_type = self.inner.lowerer.size_type_mid()?; + self.inner.lowerer.construct_binary_op_snapshot( + vir_mid::BinaryOpKind::Sub, + &size_type, + &size_type, + self.end_index.clone().into(), + self.start_index.clone().into(), + self.inner.position, + ) + } + + // pub(in super::super) fn add_permission_amount_positive_precondition( + // &mut self, + // ) -> SpannedEncodingResult<()> { + // let expression = self + // .inner + // .create_permission_amount_positive(&self.permission_amount)?; + // self.add_precondition(expression); + // Ok(()) + // } + + pub(in super::super) fn create_whole_block_acc( + &mut self, + ) -> SpannedEncodingResult { + // self.create_memory_block(self.address.clone().into()) + use vir_low::macros::*; + let length = self.length()?; + let inner = self.inner(); + let size_of = inner.lowerer.encode_type_size_expression_repetitions( + inner.ty, + inner.type_decl, + length, + inner.position, + )?; + let address = &self.address; + Ok(expr! { + acc(MemoryBlock(address, [size_of])) + }) + } + + pub(in super::super) fn create_memory_block_range_acc( + &mut self, + ) -> SpannedEncodingResult { + // self.create_memory_block(self.address.clone().into()) + let size_of = self + .inner + .lowerer + .encode_type_size_expression2(self.inner.ty, self.inner.type_decl)?; + self.inner.lowerer.encode_memory_block_range_acc( + self.address.clone().into(), + size_of, + self.start_index.clone().into(), + self.end_index.clone().into(), + self.inner.position, + ) + } + + // pub(in super::super) fn padding_size(&mut self) -> SpannedEncodingResult { + // self.inner + // .lowerer + // .encode_type_padding_size_expression(self.inner.ty) + // } + + // pub(in super::super) fn create_padding_memory_block_acc( + // &mut self, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let address = self.address.clone().into(); + // let padding_size = self.padding_size()?; + // let permission_amount = self.permission_amount.clone().into(); + // let expression = expr! { + // acc(MemoryBlock([address], [padding_size]), [permission_amount]) + // }; + // Ok(expression) + // } + + // pub(in super::super) fn create_field_memory_block_acc( + // &mut self, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let field_address = self.inner.lowerer.encode_field_address( + // self.inner.ty, + // field, + // self.address.clone().into(), + // self.inner.position, + // )?; + // let field_size_of = self + // .inner + // .lowerer + // .encode_type_size_expression2(&field.ty, &field.ty)?; + // let permission_amount = self.permission_amount.clone().into(); + // let field_block = expr! { + // acc(MemoryBlock([field_address], [field_size_of]), [permission_amount]) + // }; + // Ok(field_block) + // } + + // pub(in super::super) fn create_discriminant_acc( + // &mut self, + // decl: &vir_mid::type_decl::Enum, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let discriminant_size_of = self + // .inner + // .lowerer + // .encode_type_size_expression2(&decl.discriminant_type, &decl.discriminant_type)?; + // let discriminant_field = decl.discriminant_field(); + // let discriminant_address = self.inner.lowerer.encode_field_address( + // self.inner.ty, + // &discriminant_field, + // self.address.clone().into(), + // self.inner.position, + // )?; + // let discriminant_block = expr! { + // acc(MemoryBlock([discriminant_address], [discriminant_size_of])) + // }; + // Ok(discriminant_block) + // } + + // pub(in super::super) fn create_variant_memory_block_acc( + // &mut self, + // discriminant_value: vir_mid::DiscriminantValue, + // variant: &vir_mid::type_decl::Struct, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let variant_index = variant.name.clone().into(); + // let variant_address = self.inner.lowerer.encode_enum_variant_address( + // self.inner.ty, + // &variant_index, + // self.address.clone().into(), + // Default::default(), + // )?; + // let variant_type = self.inner.ty.clone().variant(variant_index); + // let variant_size_of = self + // .inner + // .lowerer + // // .encode_type_size_expression(&variant_type)?; + // // FIXME: This is probably wrong: test enums containing arrays. + // .encode_type_size_expression2(&variant_type, &variant_type)?; + // let discriminant = self.discriminant.as_ref().unwrap().clone().into(); + // let expression = expr! { + // ([discriminant] == [discriminant_value.into()]) ==> + // (acc(MemoryBlock([variant_address], [variant_size_of]))) + // }; + // Ok(expression) + // } + + // pub(in super::super) fn create_field_to_bytes_equality( + // &mut self, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let address = self.address(); + // let inner = self.inner(); + // inner.lowerer.encode_snapshot_to_bytes_function(inner.ty)?; + // let field_address = + // inner + // .lowerer + // .encode_field_address(inner.ty, field, address, inner.position)?; + // let field_size_of = inner + // .lowerer + // .encode_type_size_expression2(&field.ty, &field.ty)?; + // let memory_block_field_bytes = inner + // .lowerer + // .encode_memory_block_bytes_expression(field_address, field_size_of)?; + // let snapshot = var! { snapshot: {inner.ty.to_snapshot(inner.lowerer)?} }.into(); + // let field_snapshot = inner.lowerer.obtain_struct_field_snapshot( + // inner.ty, + // field, + // snapshot, + // inner.position, + // )?; + // let to_bytes = ty! { Bytes }; + // Ok(expr! { + // (([memory_block_field_bytes])) == (Snap<(&field.ty)>::to_bytes([field_snapshot])) + // }) + // } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_split_join_common.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_split_join_common.rs index b3710b33b4e..922eabfd05e 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_split_join_common.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/memory_block_split_join_common.rs @@ -51,10 +51,7 @@ impl<'l, 'p, 'v, 'tcx> MemoryBlockSplitJoinMethodBuilder<'l, 'p, 'v, 'tcx> { let permission_amount = vir_low::VariableDecl::new("permission_amount", vir_low::Type::Perm); let discriminant = if ty.has_variants() { - Some(vir_low::VariableDecl::new( - "discriminant", - vir_low::Type::Int, - )) + Some(vir_low::VariableDecl::discriminant_variable()) } else { None }; diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/mod.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/mod.rs index 2dcd5ae34cc..6238132d5a6 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/mod.rs @@ -5,9 +5,13 @@ pub(super) mod duplicate_frac_ref; pub(super) mod memory_block_copy; pub(super) mod memory_block_into; pub(super) mod memory_block_join; +pub(super) mod memory_block_range_join; +pub(super) mod memory_block_range_split_join_common; pub(super) mod memory_block_split; +pub(super) mod memory_block_range_split; pub(super) mod memory_block_split_join_common; pub(super) mod move_copy_place_common; pub(super) mod move_place; +pub(super) mod restore_raw_borrowed; pub(super) mod write_address_constant; pub(super) mod write_place_constant; diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/move_copy_place_common.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/move_copy_place_common.rs index 55d21ef39af..24068b261dc 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/move_copy_place_common.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/move_copy_place_common.rs @@ -9,7 +9,7 @@ use crate::encoder::{ builtin_methods::{calls::interface::CallContext, BuiltinMethodsInterface}, lowerer::Lowerer, places::PlacesInterface, - predicates::OwnedNonAliasedUseBuilder, + predicates::PredicatesOwnedInterface, snapshots::{IntoSnapshot, SnapshotValidityInterface}, }, }; @@ -21,9 +21,9 @@ use vir_crate::{ pub(in super::super::super::super) struct MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { pub(super) inner: BuiltinMethodBuilder<'l, 'p, 'v, 'tcx>, pub(super) target_place: vir_low::VariableDecl, - pub(super) target_root_address: vir_low::VariableDecl, + pub(super) target_address: vir_low::VariableDecl, pub(super) source_place: vir_low::VariableDecl, - pub(super) source_root_address: vir_low::VariableDecl, + pub(super) source_address: vir_low::VariableDecl, pub(super) source_snapshot: vir_low::VariableDecl, } @@ -44,12 +44,10 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { type_decl: &'l vir_mid::TypeDecl, error_kind: BuiltinMethodKind, ) -> SpannedEncodingResult { - let target_place = vir_low::VariableDecl::new("target_place", lowerer.place_type()?); - let target_root_address = - vir_low::VariableDecl::new("target_root_address", lowerer.address_type()?); - let source_place = vir_low::VariableDecl::new("source_place", lowerer.place_type()?); - let source_root_address = - vir_low::VariableDecl::new("source_root_address", lowerer.address_type()?); + let target_place = vir_low::VariableDecl::new("target_place", lowerer.place_option_type()?); + let target_address = vir_low::VariableDecl::new("target_address", lowerer.address_type()?); + let source_place = vir_low::VariableDecl::new("source_place", lowerer.place_option_type()?); + let source_address = vir_low::VariableDecl::new("source_address", lowerer.address_type()?); let source_snapshot = vir_low::VariableDecl::new("source_snapshot", ty.to_snapshot(lowerer)?); let inner = @@ -57,9 +55,9 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { Ok(Self { inner, target_place, - target_root_address, + target_address, source_place, - source_root_address, + source_address, source_snapshot, }) } @@ -71,44 +69,116 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super::super::super) fn create_target_memory_block( &mut self, ) -> SpannedEncodingResult { - self.create_memory_block( - self.compute_address(&self.target_place, &self.target_root_address), - ) + self.create_memory_block(self.target_address.clone().into()) } pub(in super::super::super::super) fn create_source_owned( &mut self, + exclude_snapshot_equality: bool, + permission_amount: Option, ) -> SpannedEncodingResult { - let mut builder = OwnedNonAliasedUseBuilder::new( - self.inner.lowerer, - CallContext::BuiltinMethod, - self.inner.ty, - self.inner.type_decl, - self.source_place.clone().into(), - self.source_root_address.clone().into(), - self.source_snapshot.clone().into(), - )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - Ok(builder.build()) + if exclude_snapshot_equality { + self.inner.lowerer.owned_non_aliased( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + self.source_place.clone().into(), + self.source_address.clone().into(), + permission_amount, + self.inner.position, + ) + } else { + self.inner.lowerer.owned_non_aliased_with_snapshot( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + self.source_place.clone().into(), + self.source_address.clone().into(), + self.source_snapshot.clone().into(), + permission_amount, + self.inner.position, + ) + } + // let predicate = self.inner.lowerer.owned_non_aliased( + // CallContext::BuiltinMethod, + // self.inner.ty, + // self.inner.type_decl, + // self.source_place.clone().into(), + // self.source_address.clone().into(), + // permission_amount, + // self.inner.position, + // )?; + // let expression = if exclude_snapshot_equality { + // predicate + // } else { + // let snap_call = self.inner.lowerer.owned_non_aliased_snap( + // CallContext::BuiltinMethod, + // self.inner.ty, + // self.inner.type_decl, + // self.source_place.clone().into(), + // self.source_address.clone().into(), + // self.inner.position, + // )?; + // vir_low::Expression::and( + // predicate, + // vir_low::Expression::equals(self.source_snapshot.clone().into(), snap_call), + // ) + // }; + // Ok(expression) } // FIXME: Remove duplicates with other builders. pub(in super::super::super::super) fn create_target_owned( &mut self, + exclude_snapshot_equality: bool, ) -> SpannedEncodingResult { - let mut builder = OwnedNonAliasedUseBuilder::new( - self.inner.lowerer, - CallContext::BuiltinMethod, - self.inner.ty, - self.inner.type_decl, - self.target_place.clone().into(), - self.target_root_address.clone().into(), - self.source_snapshot.clone().into(), - )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - Ok(builder.build()) + if exclude_snapshot_equality { + self.inner.lowerer.owned_non_aliased_full_vars( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + &self.target_place, + &self.target_address, + self.inner.position, + ) + } else { + self.inner + .lowerer + .owned_non_aliased_full_vars_with_snapshot( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + &self.target_place, + &self.target_address, + &self.source_snapshot, + self.inner.position, + ) + } + // let predicate = self.inner.lowerer.owned_non_aliased_full_vars( + // CallContext::BuiltinMethod, + // self.inner.ty, + // self.inner.type_decl, + // &self.target_place, + // &self.target_address, + // self.inner.position, + // )?; + // let expression = if exclude_snapshot_equality { + // predicate + // } else { + // let snap_call = self.inner.lowerer.owned_non_aliased_snap( + // CallContext::BuiltinMethod, + // self.inner.ty, + // self.inner.type_decl, + // self.target_place.clone().into(), + // self.target_address.clone().into(), + // self.inner.position, + // )?; + // vir_low::Expression::and( + // predicate, + // vir_low::Expression::equals(self.source_snapshot.clone().into(), snap_call), + // ) + // }; + // Ok(expression) } // FIXME: Remove duplicate with add_source_validity_precondition @@ -130,8 +200,8 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .lowerer .encode_memory_block_copy_method(self.inner.ty)?; - let source_address = self.compute_address(&self.source_place, &self.source_root_address); - let target_address = self.compute_address(&self.target_place, &self.target_root_address); + // let source_address = self.compute_address(&self.source_place, &self.source_address); + // let target_address = self.compute_address(&self.target_place, &self.target_address); let mut builder = BuiltinMethodCallBuilder::new( self.inner.lowerer, CallContext::BuiltinMethod, @@ -140,8 +210,8 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.type_decl, self.inner.position, )?; - builder.add_argument(source_address); - builder.add_argument(target_address); + builder.add_argument(self.source_address.clone().into()); + builder.add_argument(self.target_address.clone().into()); if let Some(source_permission_amount) = source_permission_amount { builder.add_argument(source_permission_amount); } else { @@ -159,7 +229,7 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .lowerer .encode_memory_block_split_method(self.inner.ty)?; - let target_address = self.compute_address(&self.target_place, &self.target_root_address); + // let target_address = self.compute_address(&self.target_place, &self.target_address); let discriminant_call = self.inner.discriminant(&self.source_snapshot)?; let mut builder = BuiltinMethodCallBuilder::new( self.inner.lowerer, @@ -169,7 +239,7 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.type_decl, self.inner.position, )?; - builder.add_argument(target_address); + builder.add_argument(self.target_address.clone().into()); builder.add_full_permission_argument(); if let Some(discriminant_call) = discriminant_call { builder.add_argument(discriminant_call); @@ -185,7 +255,7 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .lowerer .encode_memory_block_join_method(self.inner.ty)?; - let source_address = self.compute_address(&self.source_place, &self.source_root_address); + // let source_address = self.compute_address(&self.source_place, &self.source_address); let discriminant_call = self.inner.discriminant(&self.source_snapshot)?; let mut builder = BuiltinMethodCallBuilder::new( self.inner.lowerer, @@ -195,7 +265,7 @@ impl<'l, 'p, 'v, 'tcx> MoveCopyPlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.type_decl, self.inner.position, )?; - builder.add_argument(source_address); + builder.add_argument(self.source_address.clone().into()); builder.add_full_permission_argument(); if let Some(discriminant_call) = discriminant_call { builder.add_argument(discriminant_call); diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/move_place.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/move_place.rs index 79778481385..559fd25db9b 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/move_place.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/move_place.rs @@ -6,15 +6,20 @@ use super::{ use crate::encoder::{ errors::{BuiltinMethodKind, SpannedEncodingResult}, middle::core_proof::{ + addresses::AddressesInterface, builtin_methods::{ calls::interface::CallContext, BuiltinMethodCallsInterface, BuiltinMethodsInterface, }, + lifetimes::LifetimesInterface, lowerer::Lowerer, places::PlacesInterface, + predicates::PredicatesOwnedInterface, + references::ReferencesInterface, snapshots::SnapshotValuesInterface, }, }; use vir_crate::{ + common::expression::UnaryOperationHelpers, low::{self as vir_low}, middle as vir_mid, }; @@ -59,7 +64,7 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .inner .parameters - .push(self.inner.target_root_address.clone()); + .push(self.inner.target_address.clone()); self.inner .inner .parameters @@ -67,7 +72,7 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .inner .parameters - .push(self.inner.source_root_address.clone()); + .push(self.inner.source_address.clone()); self.inner .inner .parameters @@ -91,20 +96,23 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { &mut self, ) -> SpannedEncodingResult { self.create_memory_block( - self.compute_address(&self.inner.source_place, &self.inner.source_root_address), + self.inner.source_address.clone().into(), // self.compute_address(&self.inner.source_place, &self.inner.source_address), ) } pub(in super::super::super::super) fn create_source_owned( &mut self, + exclude_snapshot_equality: bool, ) -> SpannedEncodingResult { - self.inner.create_source_owned() + self.inner + .create_source_owned(exclude_snapshot_equality, None) } pub(in super::super::super::super) fn create_target_owned( &mut self, + exclude_snapshot_equality: bool, ) -> SpannedEncodingResult { - self.inner.create_target_owned() + self.inner.create_target_owned(exclude_snapshot_equality) } pub(in super::super::super::super) fn add_target_validity_postcondition( @@ -130,7 +138,7 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { // .lowerer // .encode_type_size_expression2(self.inner.inner.ty, self.inner.inner.type_decl)?; // let source_address = - // self.compute_address(&self.inner.source_place, &self.inner.source_root_address); + // self.compute_address(&self.inner.source_place, &self.inner.source_address); // let bytes = self // .inner // .inner @@ -173,12 +181,24 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.source_place.clone().into(), self.inner.inner.position, )?; + let source_field_address = self.inner.inner.lowerer.encode_field_address( + self.inner.inner.ty, + field, + self.inner.source_address.clone().into(), + self.inner.inner.position, + )?; let target_field_place = self.inner.inner.lowerer.encode_field_place( self.inner.inner.ty, field, self.inner.target_place.clone().into(), self.inner.inner.position, )?; + let target_field_address = self.inner.inner.lowerer.encode_field_address( + self.inner.inner.ty, + field, + self.inner.target_address.clone().into(), + self.inner.inner.position, + )?; let source_field_snapshot = self.inner.inner.lowerer.obtain_struct_field_snapshot( self.inner.inner.ty, field, @@ -191,9 +211,9 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { &field.ty, self.inner.inner.position, target_field_place, - self.inner.target_root_address.clone().into(), + target_field_address, source_field_place, - self.inner.source_root_address.clone().into(), + source_field_address, source_field_snapshot, )?; self.add_statement(statement); @@ -221,12 +241,24 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.target_place.clone().into(), self.inner.inner.position, )?; + let target_variant_address = self.inner.inner.lowerer.encode_enum_variant_address( + self.inner.inner.ty, + &variant_index, + self.inner.target_address.clone().into(), + self.inner.inner.position, + )?; let source_variant_place = self.inner.inner.lowerer.encode_enum_variant_place( self.inner.inner.ty, &variant_index, self.inner.source_place.clone().into(), self.inner.inner.position, )?; + let source_variant_address = self.inner.inner.lowerer.encode_enum_variant_address( + self.inner.inner.ty, + &variant_index, + self.inner.source_address.clone().into(), + self.inner.inner.position, + )?; let source_variant_snapshot = self.inner.inner.lowerer.obtain_enum_variant_snapshot( self.inner.inner.ty, &variant_index, @@ -248,9 +280,9 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { )?; builder.set_guard(condition); builder.add_argument(target_variant_place); - builder.add_argument(self.inner.target_root_address.clone().into()); + builder.add_argument(target_variant_address); builder.add_argument(source_variant_place); - builder.add_argument(self.inner.source_root_address.clone().into()); + builder.add_argument(source_variant_address); builder.add_argument(source_variant_snapshot); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; @@ -273,19 +305,30 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.inner.ty, self.inner.inner.position, )?; - let target_discriminant_place = self.inner.inner.lowerer.encode_field_place( self.inner.inner.ty, &discriminant_field, self.inner.target_place.clone().into(), self.inner.inner.position, )?; + let target_discriminant_address = self.inner.inner.lowerer.encode_field_address( + self.inner.inner.ty, + &discriminant_field, + self.inner.target_address.clone().into(), + self.inner.inner.position, + )?; let source_discriminant_place = self.inner.inner.lowerer.encode_field_place( self.inner.inner.ty, &discriminant_field, self.inner.source_place.clone().into(), self.inner.inner.position, )?; + let source_discriminant_address = self.inner.inner.lowerer.encode_field_address( + self.inner.inner.ty, + &discriminant_field, + self.inner.source_address.clone().into(), + self.inner.inner.position, + )?; let source_discriminant_snashot = self.inner.inner.lowerer.construct_constant_snapshot( &decl.discriminant_type, discriminant_call, @@ -300,9 +343,9 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.inner.position, )?; builder.add_argument(target_discriminant_place); - builder.add_argument(self.inner.target_root_address.clone().into()); + builder.add_argument(target_discriminant_address); builder.add_argument(source_discriminant_place); - builder.add_argument(self.inner.source_root_address.clone().into()); + builder.add_argument(source_discriminant_address); builder.add_argument(source_discriminant_snashot); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; @@ -342,6 +385,71 @@ impl<'l, 'p, 'v, 'tcx> MovePlaceMethodBuilder<'l, 'p, 'v, 'tcx> { Ok(()) } + pub(in super::super::super::super) fn add_dead_lifetime_hack( + &mut self, + lifetime: &vir_mid::ty::LifetimeConst, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let lifetime_alive = self + .inner + .inner + .lowerer + .encode_lifetime_const_into_pure_is_alive_variable(lifetime)?; + let guard = vir_low::Expression::not(lifetime_alive.into()); + let source_current_snapshot = self.inner.inner.lowerer.reference_target_current_snapshot( + self.inner.inner.ty, + self.inner.source_snapshot.clone().into(), + self.inner.inner.position, + )?; + let source_final_snapshot = self.inner.inner.lowerer.reference_target_final_snapshot( + self.inner.inner.ty, + self.inner.source_snapshot.clone().into(), + self.inner.inner.position, + )?; + let target_snapshot = self.inner.inner.lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + self.inner.inner.ty, + self.inner.inner.type_decl, + self.inner.target_place.clone().into(), + self.inner.target_address.clone().into(), + self.inner.inner.position, + )?; + let target_current_snapshot = self.inner.inner.lowerer.reference_target_current_snapshot( + self.inner.inner.ty, + target_snapshot.clone(), + self.inner.inner.position, + )?; + let target_final_snapshot = self.inner.inner.lowerer.reference_target_final_snapshot( + self.inner.inner.ty, + target_snapshot, + self.inner.inner.position, + )?; + let body = vec![ + vir_low::Statement::comment( + "FIXME: This is a hack. Because the lifetime is dead, the reference \ + is dangling and there is no predicate that would witness that \ + the value of the dereference is the source of the dereference. \ + This is also the reason why it is sound just to assume that the \ + two are equal. A proper solution should use a custom equality function \ + that equates the targets only if lifetimes are alive." + .to_string(), + ), + stmtp! { self.inner.inner.position => + assume ([source_current_snapshot] == [target_current_snapshot]) + }, + stmtp! { self.inner.inner.position => + assume ([source_final_snapshot] == [target_final_snapshot]) + }, + // assume destructor$Snap$ref$Unique$slice$struct$m_T1$$$target_current(source_snapshot) == destructor$Snap$ref$Unique$slice$struct$m_T1$$$target_current(snap_owned_non_aliased$ref$Unique$slice$struct$m_T1$(target_place, target_address, lft_early_bound_0$alive, lft_early_bound_0)) + + // assume destructor$Snap$ref$Unique$slice$struct$m_T1$$$target_final(source_snapshot) == destructor$Snap$ref$Unique$slice$struct$m_T1$$$target_final(snap_owned_non_aliased$ref$Unique$slice$struct$m_T1$(target_place, target_address, lft_early_bound_0$alive, lft_early_bound_0)) + ]; + let statement = + vir_low::Statement::conditional(guard, body, Vec::new(), self.inner.inner.position); + self.add_statement(statement); + Ok(()) + } + pub(in super::super::super::super) fn duplicate_frac_ref( &mut self, ) -> SpannedEncodingResult<()> { diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/restore_raw_borrowed.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/restore_raw_borrowed.rs new file mode 100644 index 00000000000..7d8d52e3ffa --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/restore_raw_borrowed.rs @@ -0,0 +1,127 @@ +use super::common::{BuiltinMethodBuilder, BuiltinMethodBuilderMethods}; +use crate::encoder::{ + errors::{BuiltinMethodKind, SpannedEncodingResult}, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + lowerer::Lowerer, + places::PlacesInterface, + predicates::{PredicatesOwnedInterface, RestorationInterface}, + snapshots::IntoSnapshot, + }, +}; +use vir_crate::{ + low::{self as vir_low}, + middle as vir_mid, +}; + +pub(in super::super::super::super) struct RestoreRawBorrowedMethodBuilder<'l, 'p, 'v, 'tcx> { + inner: BuiltinMethodBuilder<'l, 'p, 'v, 'tcx>, + borrowing_address: vir_low::VariableDecl, + restored_place: vir_low::VariableDecl, + restored_root_address: vir_low::VariableDecl, + snapshot: vir_low::VariableDecl, +} + +impl<'l, 'p, 'v, 'tcx> BuiltinMethodBuilderMethods<'l, 'p, 'v, 'tcx> + for RestoreRawBorrowedMethodBuilder<'l, 'p, 'v, 'tcx> +{ + fn inner(&mut self) -> &mut BuiltinMethodBuilder<'l, 'p, 'v, 'tcx> { + &mut self.inner + } +} + +impl<'l, 'p, 'v, 'tcx> RestoreRawBorrowedMethodBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + kind: vir_low::MethodKind, + method_name: &'l str, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + error_kind: BuiltinMethodKind, + ) -> SpannedEncodingResult { + let borrowing_address = + vir_low::VariableDecl::new("borrowing_address", lowerer.address_type()?); + let restored_place = vir_low::VariableDecl::new("restored_place", lowerer.place_type()?); + let restored_root_address = + vir_low::VariableDecl::new("restored_root_address", lowerer.address_type()?); + let snapshot = vir_low::VariableDecl::new("snapshot", ty.to_snapshot(lowerer)?); + let inner = + BuiltinMethodBuilder::new(lowerer, kind, method_name, ty, type_decl, error_kind)?; + Ok(Self { + inner, + borrowing_address, + restored_place, + restored_root_address, + snapshot, + }) + } + + pub(in super::super::super::super) fn build(self) -> vir_low::MethodDecl { + self.inner.build() + } + + pub(in super::super::super::super) fn create_parameters( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.borrowing_address.clone()); + self.inner.parameters.push(self.restored_place.clone()); + self.inner + .parameters + .push(self.restored_root_address.clone()); + self.inner.parameters.push(self.snapshot.clone()); + self.create_lifetime_parameters()?; + self.create_const_parameters()?; + Ok(()) + } + + pub(in super::super::super::super) fn add_aliased_source_precondition( + &mut self, + ) -> SpannedEncodingResult<()> { + let _aliased_root_place = self + .inner + .lowerer + .encode_aliased_place_root(self.inner.position)?; + unimplemented!(); + // let aliased_predicate = self.inner.lowerer.owned_aliased( + // CallContext::BuiltinMethod, + // self.inner.ty, + // self.inner.ty, + // aliased_root_place, + // self.borrowing_address.clone().into(), + // self.snapshot.clone().into(), + // None, + // )?; + // self.add_precondition(aliased_predicate); + Ok(()) + } + + pub(in super::super::super::super) fn add_shift_precondition( + &mut self, + ) -> SpannedEncodingResult<()> { + let restore_raw_borrowed = self.inner.lowerer.restore_raw_borrowed( + self.inner.ty, + self.restored_place.clone().into(), + self.restored_root_address.clone().into(), + )?; + self.add_precondition(restore_raw_borrowed); + Ok(()) + } + + pub(crate) fn add_non_aliased_target_postcondition(&mut self) -> SpannedEncodingResult<()> { + let non_aliased_predicate = self + .inner + .lowerer + .owned_non_aliased_full_vars_with_snapshot( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.ty, + &self.restored_place, + &self.restored_root_address, + &self.snapshot, + self.inner.position, + )?; + self.add_postcondition(non_aliased_predicate); + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/write_place_constant.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/write_place_constant.rs index 7d0a79c8a42..362cfd60f46 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/write_place_constant.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/decls/write_place_constant.rs @@ -9,7 +9,7 @@ use crate::encoder::{ builtin_methods::{BuiltinMethodsInterface, CallContext}, lowerer::Lowerer, places::PlacesInterface, - predicates::{OwnedNonAliasedUseBuilder, PredicatesOwnedInterface}, + predicates::PredicatesOwnedInterface, snapshots::{IntoSnapshot, SnapshotValidityInterface, SnapshotValuesInterface}, }, }; @@ -21,7 +21,7 @@ use vir_crate::{ pub(in super::super::super::super) struct WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { inner: BuiltinMethodBuilder<'l, 'p, 'v, 'tcx>, target_place: vir_low::VariableDecl, - target_root_address: vir_low::VariableDecl, + target_address: vir_low::VariableDecl, source_snapshot: vir_low::VariableDecl, } @@ -42,9 +42,8 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { type_decl: &'l vir_mid::TypeDecl, error_kind: BuiltinMethodKind, ) -> SpannedEncodingResult { - let target_place = vir_low::VariableDecl::new("target_place", lowerer.place_type()?); - let target_root_address = - vir_low::VariableDecl::new("target_root_address", lowerer.address_type()?); + let target_place = vir_low::VariableDecl::new("target_place", lowerer.place_option_type()?); + let target_address = vir_low::VariableDecl::new("target_address", lowerer.address_type()?); let source_snapshot = vir_low::VariableDecl::new("source_snapshot", ty.to_snapshot(lowerer)?); let inner = @@ -52,7 +51,7 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { Ok(Self { inner, target_place, - target_root_address, + target_address, source_snapshot, }) } @@ -65,7 +64,7 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { &mut self, ) -> SpannedEncodingResult<()> { self.inner.parameters.push(self.target_place.clone()); - self.inner.parameters.push(self.target_root_address.clone()); + self.inner.parameters.push(self.target_address.clone()); self.inner.parameters.push(self.source_snapshot.clone()); self.create_lifetime_parameters()?; self.create_const_parameters()?; @@ -84,29 +83,49 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { &mut self, ) -> SpannedEncodingResult { self.create_memory_block( - self.compute_address(&self.target_place, &self.target_root_address), + self.target_address.clone().into(), + // self.compute_address(&self.target_place, &self.target_address), ) } // FIXME: Remove duplicates with other builders. pub(in super::super::super::super) fn create_target_owned( &mut self, + exclude_snapshot_equality: bool, ) -> SpannedEncodingResult { - self.inner - .lowerer - .mark_owned_non_aliased_as_unfolded(self.inner.ty)?; - let mut builder = OwnedNonAliasedUseBuilder::new( - self.inner.lowerer, - CallContext::BuiltinMethod, - self.inner.ty, - self.inner.type_decl, - self.target_place.clone().into(), - self.target_root_address.clone().into(), - self.source_snapshot.clone().into(), - )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - Ok(builder.build()) + if exclude_snapshot_equality { + self.inner.lowerer.owned_non_aliased_full_vars( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + &self.target_place, + &self.target_address, + self.inner.position, + ) + } else { + self.inner + .lowerer + .owned_non_aliased_full_vars_with_snapshot( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + &self.target_place, + &self.target_address, + &self.source_snapshot, + self.inner.position, + ) + } + // self.inner + // .lowerer + // .mark_owned_predicate_as_unfolded(self.inner.ty)?; + // self.inner.lowerer.owned_non_aliased_full_vars( + // CallContext::BuiltinMethod, + // self.inner.ty, + // self.inner.type_decl, + // &self.target_place, + // &self.target_address, + // self.inner.position, + // ) } pub(in super::super::super::super) fn add_source_validity_precondition( @@ -149,7 +168,7 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .lowerer .encode_memory_block_split_method(self.inner.ty)?; - let target_address = self.compute_address(&self.target_place, &self.target_root_address); + // let target_address = self.compute_address(&self.target_place, &self.target_address); let discriminant_call = self.discriminant()?; let mut builder = BuiltinMethodCallBuilder::new( self.inner.lowerer, @@ -159,7 +178,7 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.type_decl, self.inner.position, )?; - builder.add_argument(target_address); + builder.add_argument(self.target_address.clone().into()); builder.add_full_permission_argument(); if let Some(discriminant_call) = discriminant_call { builder.add_argument(discriminant_call); @@ -182,6 +201,12 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { self.target_place.clone().into(), self.inner.position, )?; + let target_field_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + field, + self.target_address.clone().into(), + self.inner.position, + )?; let source_field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( self.inner.ty, field, @@ -198,7 +223,7 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.position, )?; builder.add_argument(target_field_place); - builder.add_argument(self.target_root_address.clone().into()); + builder.add_argument(target_field_address); builder.add_argument(source_field_snapshot); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; @@ -213,7 +238,7 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner .lowerer .encode_write_address_constant_method(self.inner.ty)?; - let address = self.compute_address(&self.target_place, &self.target_root_address); + // let address = self.compute_address(&self.target_place, &self.target_address); let mut builder = BuiltinMethodCallBuilder::new( self.inner.lowerer, CallContext::BuiltinMethod, @@ -222,7 +247,7 @@ impl<'l, 'p, 'v, 'tcx> WritePlaceConstantMethodBuilder<'l, 'p, 'v, 'tcx> { self.inner.type_decl, self.inner.position, )?; - builder.add_argument(address); + builder.add_argument(self.target_address.clone().into()); builder.add_argument(self.source_snapshot.clone().into()); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/mod.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/mod.rs index 5152118b533..acde36ccc11 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/builders/mod.rs @@ -8,7 +8,10 @@ pub(in super::super) use self::decls::{ memory_block_copy::MemoryBlockCopyMethodBuilder, memory_block_into::IntoMemoryBlockMethodBuilder, memory_block_join::MemoryBlockJoinMethodBuilder, + memory_block_range_join::MemoryBlockRangeJoinMethodBuilder, + memory_block_range_split::MemoryBlockRangeSplitMethodBuilder, memory_block_split::MemoryBlockSplitMethodBuilder, move_place::MovePlaceMethodBuilder, + restore_raw_borrowed::RestoreRawBorrowedMethodBuilder, write_address_constant::WriteAddressConstantMethodBuilder, write_place_constant::WritePlaceConstantMethodBuilder, }; diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/calls/interface.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/calls/interface.rs index 7b9234efc8d..5ec75cf4135 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/calls/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/calls/interface.rs @@ -98,6 +98,21 @@ pub(in super::super::super) trait BuiltinMethodCallsInterface { ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments; + + #[allow(clippy::too_many_arguments)] + fn call_restore_raw_borrowed_method( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + position: vir_low::Position, + borrowing_address: vir_low::Expression, + restored_place: vir_low::Expression, + restored_root_address: vir_low::Expression, + snapshot: vir_low::Expression, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; } impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodCallsInterface for Lowerer<'p, 'v, 'tcx> { @@ -108,7 +123,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodCallsInterface for Lowerer<'p, 'v, 'tcx> generics: &G, position: vir_low::Position, target_place: vir_low::Expression, - target_root_address: vir_low::Expression, + target_address: vir_low::Expression, source_snapshot: vir_low::Expression, ) -> SpannedEncodingResult where @@ -123,7 +138,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodCallsInterface for Lowerer<'p, 'v, 'tcx> position, )?; builder.add_argument(target_place); - builder.add_argument(target_root_address); + builder.add_argument(target_address); builder.add_argument(source_snapshot); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; @@ -164,9 +179,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodCallsInterface for Lowerer<'p, 'v, 'tcx> generics: &G, position: vir_low::Position, target_place: vir_low::Expression, - target_root_address: vir_low::Expression, + target_address: vir_low::Expression, source_place: vir_low::Expression, - source_root_address: vir_low::Expression, + source_address: vir_low::Expression, source_snapshot: vir_low::Expression, source_permission_amount: vir_low::Expression, ) -> SpannedEncodingResult @@ -176,9 +191,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodCallsInterface for Lowerer<'p, 'v, 'tcx> let mut builder = BuiltinMethodCallBuilder::new(self, context, "copy_place", ty, generics, position)?; builder.add_argument(target_place); - builder.add_argument(target_root_address); + builder.add_argument(target_address); builder.add_argument(source_place); - builder.add_argument(source_root_address); + builder.add_argument(source_address); builder.add_argument(source_snapshot); builder.add_argument(source_permission_amount); builder.add_lifetime_arguments()?; @@ -194,7 +209,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodCallsInterface for Lowerer<'p, 'v, 'tcx> position: vir_low::Position, guard: Option, place: vir_low::Expression, - root_address: vir_low::Expression, + address: vir_low::Expression, snapshot: vir_low::Expression, ) -> SpannedEncodingResult where @@ -209,7 +224,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodCallsInterface for Lowerer<'p, 'v, 'tcx> position, )?; builder.add_argument(place); - builder.add_argument(root_address); + builder.add_argument(address); builder.add_argument(snapshot); builder.add_lifetime_arguments()?; builder.add_const_arguments()?; @@ -249,4 +264,33 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodCallsInterface for Lowerer<'p, 'v, 'tcx> builder.add_const_arguments()?; Ok(builder.build()) } + + fn call_restore_raw_borrowed_method( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + position: vir_low::Position, + borrowing_address: vir_low::Expression, + restored_place: vir_low::Expression, + restored_root_address: vir_low::Expression, + snapshot: vir_low::Expression, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let mut builder = BuiltinMethodCallBuilder::new( + self, + context, + "restore_raw_borrowed", + ty, + generics, + position, + )?; + builder.add_argument(borrowing_address); + builder.add_argument(restored_place); + builder.add_argument(restored_root_address); + builder.add_argument(snapshot); + Ok(builder.build()) + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/interface.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/interface.rs index 30f6a32d4d0..27d36e2baf4 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/interface.rs @@ -1,7 +1,7 @@ use super::{ builders::{ ChangeUniqueRefPlaceMethodBuilder, DuplicateFracRefMethodBuilder, - MemoryBlockCopyMethodBuilder, + MemoryBlockCopyMethodBuilder, RestoreRawBorrowedMethodBuilder, }, BuiltinMethodCallsInterface, CallContext, }; @@ -13,35 +13,48 @@ use crate::encoder::{ block_markers::BlockMarkersInterface, builtin_methods::builders::{ BuiltinMethodBuilderMethods, CopyPlaceMethodBuilder, IntoMemoryBlockMethodBuilder, - MemoryBlockJoinMethodBuilder, MemoryBlockSplitMethodBuilder, MovePlaceMethodBuilder, - WriteAddressConstantMethodBuilder, WritePlaceConstantMethodBuilder, + MemoryBlockJoinMethodBuilder, MemoryBlockRangeJoinMethodBuilder, + MemoryBlockRangeSplitMethodBuilder, MemoryBlockSplitMethodBuilder, + MovePlaceMethodBuilder, WriteAddressConstantMethodBuilder, + WritePlaceConstantMethodBuilder, }, compute_address::ComputeAddressInterface, + const_generics::ConstGenericsInterface, errors::ErrorsInterface, + footprint::FootprintInterface, lifetimes::LifetimesInterface, lowerer::{ DomainsLowererInterface, Lowerer, MethodsLowererInterface, PredicatesLowererInterface, VariablesLowererInterface, }, places::PlacesInterface, + pointers::PointersInterface, predicates::{ - OwnedNonAliasedUseBuilder, PredicatesMemoryBlockInterface, PredicatesOwnedInterface, + OwnedNonAliasedSnapCallBuilder, OwnedNonAliasedUseBuilder, PredicatesAliasingInterface, + PredicatesMemoryBlockInterface, PredicatesOwnedInterface, RestorationInterface, }, references::ReferencesInterface, snapshots::{ - BuiltinFunctionsInterface, IntoBuiltinMethodSnapshot, IntoProcedureFinalSnapshot, - IntoProcedureSnapshot, IntoPureSnapshot, IntoSnapshot, SnapshotBytesInterface, - SnapshotValidityInterface, SnapshotValuesInterface, SnapshotVariablesInterface, + AssertionToSnapshotConstructor, BuiltinFunctionsInterface, IntoBuiltinMethodSnapshot, + IntoProcedureFinalSnapshot, IntoProcedureSnapshot, IntoPureSnapshot, IntoSnapshot, + IntoSnapshotLowerer, PlaceToSnapshot, PredicateKind, SelfFramingAssertionToSnapshot, + SnapshotBytesInterface, SnapshotValidityInterface, SnapshotValuesInterface, + SnapshotVariablesInterface, }, type_layouts::TypeLayoutsInterface, + viewshifts::ViewShiftsInterface, }, }; use itertools::Itertools; use rustc_hash::FxHashSet; use vir_crate::{ common::{ - expression::{ExpressionIterator, UnaryOperationHelpers}, + check_mode::CheckMode, + expression::{ + BinaryOperationHelpers, ExpressionIterator, QuantifierHelpers, UnaryOperationHelpers, + }, identifier::WithIdentifier, + position::Positioned, }, low::{self as vir_low, macros::method_name}, middle::{ @@ -50,6 +63,9 @@ use vir_crate::{ }, }; +// FIXME: Move this to some proper place. It is shared with the snap function +// encoder. + #[derive(Default)] pub(in super::super) struct BuiltinMethodsState { encoded_write_place_constant_methods: FxHashSet, @@ -61,7 +77,9 @@ pub(in super::super) struct BuiltinMethodsState { encoded_owned_non_aliased_havoc_methods: FxHashSet, encoded_memory_block_copy_methods: FxHashSet, encoded_memory_block_split_methods: FxHashSet, + encoded_memory_block_range_split_methods: FxHashSet, encoded_memory_block_join_methods: FxHashSet, + encoded_memory_block_range_join_methods: FxHashSet, encoded_memory_block_havoc_methods: FxHashSet, encoded_into_memory_block_methods: FxHashSet, encoded_assign_methods: FxHashSet, @@ -73,7 +91,9 @@ pub(in super::super) struct BuiltinMethodsState { encoded_lft_tok_sep_take_methods: FxHashSet, encoded_lft_tok_sep_return_methods: FxHashSet, encoded_open_close_mut_ref_methods: FxHashSet, + encoded_restore_raw_borrowed_methods: FxHashSet, encoded_bor_shorten_methods: FxHashSet, + encoded_stashed_owned_aliased_predicates: FxHashSet, } trait Private { @@ -232,10 +252,60 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { let perm_amount = value .lifetime_token_permission .to_procedure_snapshot(self)?; - self.encode_place_arguments(arguments, &value.deref_place, false)?; - if value.uniqueness.is_unique() { - let snapshot_final = value.deref_place.to_procedure_final_snapshot(self)?; - arguments.push(snapshot_final); + arguments.push(self.encode_expression_as_place(&value.deref_place)?); + // arguments.push(self.extract_root_address(&value.deref_place)?); + arguments.push(self.encode_expression_as_place_address(&value.deref_place)?); + // self.encode_place_arguments(arguments, &value.deref_place, false)?; + if self.check_mode.unwrap() == CheckMode::PurificationFunctional { + arguments.push(value.deref_place.to_procedure_snapshot(self)?); + } else { + let place = self.encode_expression_as_place(&value.deref_place)?; + // let root_address = self.extract_root_address(&value.deref_place)?; + let address = self.encode_expression_as_place_address(&value.deref_place)?; + let ty = value.deref_place.get_type(); + let TODO_target_slice_len = None; + match value.uniqueness { + vir_mid::ty::Uniqueness::Unique => { + let snapshot_current = self.unique_ref_snap( + CallContext::Procedure, + ty, + ty, + place, + address, + deref_lifetime.clone().into(), + TODO_target_slice_len, + false, + value.deref_place.position(), + )?; + arguments.push(snapshot_current); + let mut place_encoder = + PlaceToSnapshot::for_place(PredicateKind::UniqueRef { + lifetime: deref_lifetime.clone().into(), + is_final: true, + }); + let snapshot_final = place_encoder.expression_to_snapshot( + self, + &value.deref_place, + true, + )?; + // let snapshot_final = + // value.deref_place.to_procedure_final_snapshot(self)?; + arguments.push(snapshot_final); + } + vir_mid::ty::Uniqueness::Shared => { + let snapshot_current = self.frac_ref_snap( + CallContext::Procedure, + ty, + ty, + place, + address, + deref_lifetime.clone().into(), + TODO_target_slice_len, + value.deref_place.position(), + )?; + arguments.push(snapshot_current); + } + } } arguments.extend(self.create_lifetime_arguments( CallContext::Procedure, @@ -279,6 +349,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { }; arguments.push(len); } + vir_mid::Rvalue::Cast(value) => { + self.encode_operand_arguments(arguments, &value.operand, true)?; + } vir_mid::Rvalue::UnaryOp(value) => { self.encode_operand_arguments(arguments, &value.argument, true)?; } @@ -312,6 +385,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { for operand in &aggr_value.operands { self.encode_operand_arguments(arguments, operand, false)?; } + if self.use_heap_variable()? && aggr_value.ty.is_struct() { + let heap = self.heap_variable_version_at_label(&None)?; + arguments.push(heap.into()); + } } } Ok(()) @@ -344,7 +421,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { permission: &Option, ) -> SpannedEncodingResult<()> { arguments.push(self.encode_expression_as_place(place)?); - arguments.push(self.extract_root_address(place)?); + arguments.push(self.encode_expression_as_place_address(place)?); + // arguments.push(self.extract_root_address(place)?); if let Some(variable) = permission { arguments.push(variable.to_procedure_snapshot(self)?.into()); } else { @@ -361,7 +439,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { encode_lifetime_arguments: bool, ) -> SpannedEncodingResult<()> { arguments.push(self.encode_expression_as_place(expression)?); - arguments.push(self.extract_root_address(expression)?); + // arguments.push(self.extract_root_address(expression)?); + arguments.push(self.encode_expression_as_place_address(expression)?); arguments.push(expression.to_procedure_snapshot(self)?); if encode_lifetime_arguments { arguments.extend( @@ -445,23 +524,24 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { .insert(method_name.to_string()); self.encode_compute_address(ty)?; - self.mark_owned_non_aliased_as_unfolded(ty)?; + self.mark_owned_predicate_as_unfolded(ty)?; let span = self.encoder.get_type_definition_span_mid(ty)?; let position = self.register_error( span, ErrorCtxt::UnexpectedBuiltinMethod(BuiltinMethodKind::MovePlace), ); use vir_low::macros::*; - let compute_address = ty!(Address); + // let compute_address = ty!(Address); let size_of = self.encode_type_size_expression2(ty, ty)?; var_decls! { - target_place: Place, + target_place: PlaceOption, target_address: Address }; let mut parameters = vec![target_place.clone(), target_address.clone()]; var_decls! { result_value: {ty.to_snapshot(self)?} }; let mut pres = vec![ - expr! { acc(MemoryBlock((ComputeAddress::compute_address(target_place, target_address)), [size_of])) }, + // expr! { acc(MemoryBlock((ComputeAddress::compute_address(target_place, target_address)), [size_of])) }, + expr! { acc(MemoryBlock(target_address, [size_of])) }, ]; let mut posts = Vec::new(); match value { @@ -508,13 +588,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { let snap = self.encode_lifetime_const_into_procedure_variable(lifetime)?; lifetimes_ty.push(snap.into()); } - let predicate = self.owned_non_aliased_full_vars( + let predicate = self.owned_non_aliased_full_vars_with_snapshot( CallContext::BuiltinMethod, ty, ty, &target_place, &target_address, &result_value, + position, )?; posts.push(predicate); self.encode_assign_method_rvalue( @@ -623,33 +704,87 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { vir_mid::Rvalue::AddressOf(value) => { let ty = value.place.get_type(); var_decls! { - operand_place: Place, + operand_place: PlaceOption, operand_address: Address, operand_value: { ty.to_snapshot(self)? } }; - let predicate = self.owned_non_aliased_full_vars( + let non_aliased_predicate = self.owned_non_aliased_full_vars_with_snapshot( CallContext::BuiltinMethod, ty, ty, &operand_place, &operand_address, &operand_value, + position, )?; - let compute_address = ty!(Address); - let address = - expr! { ComputeAddress::compute_address(operand_place, operand_address) }; - pres.push(predicate.clone()); - posts.push(predicate); + // let compute_address = ty!(Address); + // let address = + // expr! { ComputeAddress::compute_address(operand_place, operand_address) }; + pres.push(non_aliased_predicate); + let _aliased_root_place = self.encode_aliased_place_root(position)?; + let _aliased_predicate: vir_low::Expression = unimplemented!(); + // let aliased_predicate = self.owned_aliased( + // CallContext::BuiltinMethod, + // ty, + // ty, + // aliased_root_place, + // address.clone(), + // operand_value.clone().into(), + // None, + // )?; + let restore_raw_borrowed = self.restore_raw_borrowed( + ty, + operand_place.clone().into(), + operand_address.clone().into(), + )?; + posts.push(_aliased_predicate); + posts.push(restore_raw_borrowed); parameters.push(operand_place); parameters.push(operand_address); parameters.push(operand_value); - self.construct_constant_snapshot(result_type, address, position)? + self.construct_constant_snapshot( + result_type, + operand_address.clone().into(), + position, + )? } vir_mid::Rvalue::Len(_value) => { var_decls! { length: {self.size_type()?} }; parameters.push(length.clone()); length.into() } + vir_mid::Rvalue::Cast(value) => { + let source_type = value.operand.expression.get_type(); + let operand_value = self.encode_assign_operand( + parameters, + pres, + posts, + 1, + &value.operand, + position, + true, + )?; + match (&value.ty, source_type) { + (vir_mid::Type::Pointer(_), vir_mid::Type::Pointer(_)) => { + let address = + self.pointer_address(source_type, operand_value.into(), position)?; + self.construct_constant_snapshot(result_type, address, position)? + } + ( + vir_mid::Type::Int(vir_mid::ty::Int::Isize), + vir_mid::Type::Int(vir_mid::ty::Int::Usize), + ) => { + let number = self.obtain_constant_value( + source_type, + operand_value.into(), + position, + )?; + pres.push(expr! { [number.clone()] <= [isize::MAX.into()] }); // FIXME: use the MAX value of the target. + self.construct_constant_snapshot(result_type, number, position)? + } + (t, s) => unimplemented!("({t}) {s}"), + } + } vir_mid::Rvalue::UnaryOp(value) => { let operand_value = self.encode_assign_operand( parameters, @@ -720,12 +855,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { vir_mid::Rvalue::Discriminant(value) => { let ty = value.place.get_type(); var_decls! { - operand_place: Place, + operand_place: PlaceOption, operand_address: Address, operand_permission: Perm, operand_value: { ty.to_snapshot(self)? } }; - let predicate = self.owned_non_aliased( + let predicate = self.owned_non_aliased_with_snapshot( CallContext::BuiltinMethod, ty, ty, @@ -733,6 +868,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { operand_address.clone().into(), operand_value.clone().into(), Some(operand_permission.clone().into()), + position, )?; pres.push(expr! { [vir_low::Expression::no_permission()] < operand_permission @@ -775,7 +911,39 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { self.construct_enum_snapshot(&value.ty, variant_constructor, position)? } vir_mid::Type::Struct(_) => { - self.construct_struct_snapshot(&value.ty, arguments, position)? + let decl = self.encoder.get_type_decl_mid(&value.ty)?.unwrap_struct(); + if let Some(invariant) = decl.structural_invariant { + assert_eq!(arguments.len(), decl.fields.len()); + // Assert the invariant for the struct in the precondition. + let mut invariant_encoder = + SelfFramingAssertionToSnapshot::for_assign_precondition( + arguments.clone(), + decl.fields.clone(), + ); + for assertion in &invariant { + let encoded_assertion = invariant_encoder + .expression_to_snapshot(self, assertion, true)?; + pres.push(encoded_assertion); + } + // Create the snapshot constructor. + let deref_fields = + self.structural_invariant_to_deref_fields(&invariant)?; + let mut constructor_encoder = + AssertionToSnapshotConstructor::for_assign_aggregate_postcondition( + result_type, + arguments, + decl.fields, + deref_fields, + position, + ); + let invariant_expression = invariant.into_iter().conjoin(); + let permission_expression = + invariant_expression.convert_into_permission_expression(); + constructor_encoder + .expression_to_snapshot_constructor(self, &permission_expression)? + } else { + self.construct_struct_snapshot(&value.ty, arguments, position)? + } } vir_mid::Type::Array(value_ty) => vir_low::Expression::container_op( vir_low::ContainerOpKind::SeqConstructor, @@ -785,9 +953,6 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { ), ty => unimplemented!("{}", ty), }; - posts.push( - self.encode_snapshot_valid_call_for_type(assigned_value.clone(), result_type)?, - ); assigned_value } }; @@ -811,25 +976,26 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { // is unknown. use vir_low::macros::*; var_decls! { - target_place: Place, + target_place: PlaceOption, target_address: Address }; - let compute_address = ty!(Address); + // let compute_address = ty!(Address); let type_decl = self.encoder.get_type_decl_mid(ty)?.unwrap_struct(); let (operation_result_field, flag_field) = { let mut iter = type_decl.fields.iter(); (iter.next().unwrap(), iter.next().unwrap()) }; - let flag_place = - self.encode_field_place(ty, flag_field, target_place.clone().into(), position)?; + let flag_place = self.encode_field_place(ty, flag_field, target_place.into(), position)?; + let flag_address = + self.encode_field_address(ty, flag_field, target_address.clone().into(), position)?; let flag_value = self.obtain_struct_field_snapshot( ty, flag_field, result_value.clone().into(), position, )?; - let result_address = - expr! { (ComputeAddress::compute_address(target_place, target_address)) }; + let result_address: vir_low::Expression = target_address.into(); + // expr! { (ComputeAddress::compute_address(target_place, target_address)) }; let operation_result_address = self.encode_field_address( ty, operation_result_field, @@ -854,9 +1020,16 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { posts.push( expr! { acc(MemoryBlock([operation_result_address.clone()], [size_of_result.clone()])) }, ); - posts.push( - expr! { acc(OwnedNonAliased([flag_place], target_address, [flag_value.clone()])) }, - ); + posts.push(self.owned_non_aliased_with_snapshot( + CallContext::BuiltinMethod, + flag_type, + flag_type, + flag_place, + flag_address, + flag_value.clone(), + None, + position, + )?); let operand_left = self.encode_assign_operand(parameters, pres, posts, 1, &value.left, position, true)?; let operand_right = @@ -908,10 +1081,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { let reference_type = result_type.clone().unwrap_reference(); let ty = value.deref_place.get_type(); var_decls! { - target_place: Place, + target_place: PlaceOption, target_address: Address, - operand_place: Place, - operand_root_address: Address, + operand_place: PlaceOption, + operand_address: Address, operand_snapshot_current: { ty.to_snapshot(self)? }, operand_snapshot_final: { ty.to_snapshot(self)? }, // use only for unique references lifetime_perm: Perm @@ -921,64 +1094,103 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { let lifetime_token = self.encode_lifetime_token(new_borrow_lifetime.clone(), lifetime_perm.clone().into())?; let deref_predicate = if reference_type.uniqueness.is_unique() { - self.unique_ref_full_vars( + self.unique_ref_full_vars_with_current_snapshot( CallContext::BuiltinMethod, ty, ty, &operand_place, - &operand_root_address, + &operand_address, &operand_snapshot_current, - &operand_snapshot_final, &deref_lifetime, + None, + position, )? } else { - self.frac_ref_full_vars( + self.frac_ref_full_vars_with_current_snapshot( CallContext::BuiltinMethod, ty, ty, &operand_place, - &operand_root_address, + &operand_address, &operand_snapshot_current, &deref_lifetime, + None, + position, )? }; let valid_result = self.encode_snapshot_valid_call_for_type(result_value.clone().into(), result_type)?; let new_reference_predicate = { - self.mark_owned_non_aliased_as_unfolded(result_type)?; + self.mark_owned_predicate_as_unfolded(result_type)?; let mut builder = OwnedNonAliasedUseBuilder::new( + self, + CallContext::BuiltinMethod, + result_type, + ty, + target_place.clone().into(), + target_address.clone().into(), + position, + )?; + // builder.add_snapshot_argument(result_value.clone().into())?; + builder.add_custom_argument(true.into())?; + builder.add_custom_argument(new_borrow_lifetime.clone().into())?; + builder.add_lifetime_arguments()?; + builder.add_const_arguments()?; + let predicate = builder.build()?; + let mut builder = OwnedNonAliasedSnapCallBuilder::new( self, CallContext::BuiltinMethod, result_type, ty, target_place.into(), target_address.into(), - result_value.clone().into(), + position, )?; builder.add_custom_argument(true.into())?; builder.add_custom_argument(new_borrow_lifetime.clone().into())?; builder.add_lifetime_arguments()?; builder.add_const_arguments()?; - builder.build() + let snapshot = builder.build()?; + expr! { [predicate] && (result_value == [snapshot]) } }; let restoration = { - let final_snapshot = self.reference_target_final_snapshot( - result_type, - result_value.clone().into(), - position, - )?; - let validity = self.encode_snapshot_valid_call_for_type(final_snapshot, ty)?; if reference_type.uniqueness.is_unique() { - expr! { - wand( - (acc(DeadLifetimeToken(new_borrow_lifetime))) --* ( - [deref_predicate.clone()] && - [validity] && - // DeadLifetimeToken is duplicable and does not get consumed. - (acc(DeadLifetimeToken(new_borrow_lifetime))) - ) - ) - } + let final_snapshot = self.reference_target_final_snapshot( + result_type, + result_value.clone().into(), + position, + )?; + let validity = + self.encode_snapshot_valid_call_for_type(final_snapshot.clone(), ty)?; + self.encode_view_shift_return( + &format!("end_reborrow${}", ty.get_identifier()), + vec![ + new_borrow_lifetime.clone().into(), + operand_place.clone().into(), + operand_address.clone().into(), + operand_snapshot_current.clone().into(), + final_snapshot, + deref_lifetime.clone().into(), + ], + vec![expr! { acc(DeadLifetimeToken(new_borrow_lifetime)) }], + vec![ + deref_predicate.clone(), + validity, + // DeadLifetimeToken is duplicable and does not get consumed. + expr! { acc(DeadLifetimeToken(new_borrow_lifetime)) }, + ], + position, + )? + // expr! { + // wand( + // (acc(DeadLifetimeToken(new_borrow_lifetime))) --* ( + // [deref_predicate.clone()] && + // [validity] && + // // DeadLifetimeToken is duplicable and does not get consumed. + // (acc(DeadLifetimeToken(new_borrow_lifetime))) + // ) + // ) + // } } else { deref_predicate.clone() } @@ -986,7 +1198,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { let reference_target_address = self.reference_address(result_type, result_value.clone().into(), position)?; posts.push(expr! { - operand_root_address == [reference_target_address] + operand_address == [reference_target_address] }); let reference_target_current_snapshot = self.reference_target_current_snapshot( result_type, @@ -996,12 +1208,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { posts.push(expr! { operand_snapshot_current == [reference_target_current_snapshot] }); - let reference_target_final_snapshot = self.reference_target_final_snapshot( - result_type, - result_value.clone().into(), - position, - )?; if reference_type.uniqueness.is_unique() { + let reference_target_final_snapshot = self.reference_target_final_snapshot( + result_type, + result_value.clone().into(), + position, + )?; posts.push(expr! { operand_snapshot_final == [reference_target_final_snapshot] }); @@ -1024,7 +1236,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { posts.push(valid_result); posts.push(restoration); parameters.push(operand_place); - parameters.push(operand_root_address); + parameters.push(operand_address); parameters.push(operand_snapshot_current); if reference_type.uniqueness.is_unique() { parameters.push(operand_snapshot_final); @@ -1063,40 +1275,57 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { use vir_low::macros::*; let ty = value.place.get_type(); var_decls! { - target_place: Place, + target_place: PlaceOption, target_address: Address, - operand_place: Place, - operand_root_address: Address, + operand_place: PlaceOption, + operand_address: Address, operand_snapshot: { ty.to_snapshot(self)? }, lifetime_perm: Perm }; let new_borrow_lifetime = value.new_borrow_lifetime.to_pure_snapshot(self)?; let lifetime_token = self.encode_lifetime_token(new_borrow_lifetime.clone(), lifetime_perm.clone().into())?; - let predicate = self.owned_non_aliased_full_vars( + let predicate = self.owned_non_aliased_full_vars_with_snapshot( CallContext::BuiltinMethod, ty, ty, &operand_place, - &operand_root_address, + &operand_address, &operand_snapshot, + position, )?; let reference_predicate = { - self.mark_owned_non_aliased_as_unfolded(result_type)?; + self.mark_owned_predicate_as_unfolded(result_type)?; let mut builder = OwnedNonAliasedUseBuilder::new( + self, + CallContext::BuiltinMethod, + result_type, + ty, + target_place.clone().into(), + target_address.clone().into(), + position, + )?; + // builder.add_snapshot_argument(result_value.clone().into())?; + builder.add_custom_argument(true.into())?; + builder.add_custom_argument(new_borrow_lifetime.clone().into())?; + builder.add_lifetime_arguments()?; + builder.add_const_arguments()?; + let predicate = builder.build()?; + let mut builder = OwnedNonAliasedSnapCallBuilder::new( self, CallContext::BuiltinMethod, result_type, ty, target_place.into(), target_address.into(), - result_value.clone().into(), + position, )?; builder.add_custom_argument(true.into())?; builder.add_custom_argument(new_borrow_lifetime.clone().into())?; builder.add_lifetime_arguments()?; builder.add_const_arguments()?; - builder.build() + let snapshot = builder.build()?; + expr! { [predicate] && (result_value == [snapshot]) } }; let restoration = { let restoration_snapshot = if value.uniqueness.is_unique() { @@ -1112,31 +1341,53 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { position, )? }; - let restored_predicate = self.owned_non_aliased( + let restored_predicate = self.owned_non_aliased_with_snapshot( CallContext::BuiltinMethod, ty, ty, operand_place.clone().into(), - operand_root_address.clone().into(), + operand_address.clone().into(), restoration_snapshot.clone(), None, + position, )?; - let validity = self.encode_snapshot_valid_call_for_type(restoration_snapshot, ty)?; - expr! { - wand( - (acc(DeadLifetimeToken(new_borrow_lifetime))) --* ( - [restored_predicate] && - [validity] && - // DeadLifetimeToken is duplicable and does not get consumed. - (acc(DeadLifetimeToken(new_borrow_lifetime))) - ) - ) - } + let validity = + self.encode_snapshot_valid_call_for_type(restoration_snapshot.clone(), ty)?; + let mut arguments = vec![ + new_borrow_lifetime.clone().into(), + operand_place.clone().into(), + operand_address.clone().into(), + restoration_snapshot, + ]; + arguments.extend(self.create_lifetime_arguments(CallContext::BuiltinMethod, ty)?); + arguments.extend(self.create_const_arguments(CallContext::BuiltinMethod, ty)?); + self.encode_view_shift_return( + &format!("end_borrow${}", ty.get_identifier()), + arguments, + vec![expr! { acc(DeadLifetimeToken(new_borrow_lifetime)) }], + vec![ + restored_predicate, + validity, + // DeadLifetimeToken is duplicable and does not get consumed. + expr! { acc(DeadLifetimeToken(new_borrow_lifetime)) }, + ], + position, + )? + // expr! { + // wand( + // (acc(DeadLifetimeToken(new_borrow_lifetime))) --* ( + // [restored_predicate] && + // [validity] && + // // DeadLifetimeToken is duplicable and does not get consumed. + // (acc(DeadLifetimeToken(new_borrow_lifetime))) + // ) + // ) + // } }; let reference_target_address = self.reference_address(result_type, result_value.clone().into(), position)?; posts.push(expr! { - operand_root_address == [reference_target_address] + operand_address == [reference_target_address] }); // Note: We do not constraint the final snapshot, because it is fresh. let reference_target_current_snapshot = self.reference_target_current_snapshot( @@ -1165,7 +1416,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { posts.push(restoration); posts.push(result_validity); parameters.push(operand_place); - parameters.push(operand_root_address); + parameters.push(operand_address); parameters.push(operand_snapshot); parameters.extend(self.create_lifetime_parameters(ty)?); parameters.push(new_borrow_lifetime); @@ -1192,7 +1443,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { posts: &mut Vec, operand_counter: u32, operand: &vir_mid::Operand, - _position: vir_low::Position, + position: vir_low::Position, add_lifetime_parameters: bool, ) -> SpannedEncodingResult { use vir_low::macros::*; @@ -1201,26 +1452,28 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { match operand.kind { vir_mid::OperandKind::Copy | vir_mid::OperandKind::Move => { let place = self.encode_assign_operand_place(operand_counter)?; - let root_address = self.encode_assign_operand_address(operand_counter)?; - let predicate = self.owned_non_aliased_full_vars( + let address = self.encode_assign_operand_address(operand_counter)?; + let predicate = self.owned_non_aliased_full_vars_with_snapshot( CallContext::BuiltinMethod, ty, ty, &place, - &root_address, + &address, &snapshot, + position, )?; pres.push(predicate.clone()); let post_predicate = if operand.kind == vir_mid::OperandKind::Copy { predicate } else { - let compute_address = ty!(Address); + // let compute_address = ty!(Address); let size_of = self.encode_type_size_expression2(ty, ty)?; - expr! { acc(MemoryBlock((ComputeAddress::compute_address(place, root_address)), [size_of])) } + // expr! { acc(MemoryBlock((ComputeAddress::compute_address(place, root_address)), [size_of])) } + expr! { acc(MemoryBlock(address, [size_of])) } }; posts.push(post_predicate); parameters.push(place); - parameters.push(root_address); + parameters.push(address); parameters.push(snapshot.clone()); if add_lifetime_parameters { parameters.extend(self.create_lifetime_parameters(ty)?); @@ -1242,7 +1495,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { ) -> SpannedEncodingResult { Ok(vir_low::VariableDecl::new( format!("operand{operand_counter}_place"), - self.place_type()?, + self.place_option_type()?, )) } fn encode_assign_operand_address( @@ -1270,7 +1523,15 @@ pub(in super::super) trait BuiltinMethodsInterface { fn encode_memory_block_copy_method(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()>; fn encode_memory_block_split_method(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()>; + fn encode_memory_block_range_split_method( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()>; fn encode_memory_block_join_method(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()>; + fn encode_memory_block_range_join_method( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()>; fn encode_move_place_method(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()>; fn encode_change_unique_ref_place_method( @@ -1325,6 +1586,10 @@ pub(in super::super) trait BuiltinMethodsInterface { predicate: vir_mid::VariableDecl, position: vir_low::Position, ) -> SpannedEncodingResult<()>; + fn encode_restore_raw_borrowed_method( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()>; fn encode_open_frac_bor_atomic_method( &mut self, ty: &vir_mid::Type, @@ -1342,6 +1607,29 @@ pub(in super::super) trait BuiltinMethodsInterface { &mut self, ty_with_lifetime: &vir_mid::Type, ) -> SpannedEncodingResult<()>; + fn encode_stash_range_call( + &mut self, + statements: &mut Vec, + ty: &vir_mid::Type, + pointer_value: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + label: String, + position: vir_low::Position, + ) -> SpannedEncodingResult<()>; + + fn encode_restore_stash_range_call( + &mut self, + statements: &mut Vec, + ty: &vir_mid::Type, + old_pointer_value: vir_low::Expression, + old_start_index: vir_low::Expression, + old_end_index: vir_low::Expression, + label: String, + new_address: vir_low::Expression, + new_start_index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult<()>; } impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { @@ -1451,9 +1739,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { ); let target_memory_block = builder.create_target_memory_block()?; builder.add_precondition(target_memory_block); - let source_owned = builder.create_source_owned()?; + let source_owned = builder.create_source_owned(false)?; builder.add_precondition(source_owned); - let target_owned = builder.create_target_owned()?; + let target_owned = builder.create_target_owned(false)?; builder.add_postcondition(target_owned); let source_memory_block = builder.create_source_memory_block()?; builder.add_postcondition(source_memory_block); @@ -1461,7 +1749,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { builder.add_target_validity_postcondition()?; if has_body { builder.create_body(); - let source_owned = builder.create_source_owned()?; + let source_owned = builder.create_source_owned(true)?; builder.add_statement(vir_low::Statement::unfold_no_pos(source_owned)); } match &type_decl { @@ -1511,8 +1799,20 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { _ => unimplemented!("{type_decl:?}"), } if has_body { - let target_owned = builder.create_target_owned()?; + let target_owned = builder.create_target_owned(true)?; builder.add_statement(vir_low::Statement::fold_no_pos(target_owned)); + if let vir_mid::TypeDecl::Reference(vir_mid::type_decl::Reference { + uniqueness, + lifetimes, + .. + }) = &type_decl + { + if uniqueness.is_unique() { + // FIXME: Have a getter for the first lifetime. + let lifetime = &lifetimes[0]; + builder.add_dead_lifetime_hack(lifetime)?; + } + } } let method = builder.build(); self.declare_method(method)?; @@ -1607,7 +1907,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { "copy_place", &normalized_type, &type_decl, - BuiltinMethodKind::MovePlace, + BuiltinMethodKind::CopyPlace, )?; builder.create_parameters()?; // FIXME: To generate body for arrays, we would need to generate a @@ -1624,12 +1924,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { let source_owned = builder.create_source_owned()?; builder.add_precondition(source_owned.clone()); builder.add_postcondition(source_owned); - let target_owned = builder.create_target_owned()?; + let target_owned = builder.create_target_owned(false)?; builder.add_postcondition(target_owned); builder.add_target_validity_postcondition()?; if has_body { builder.create_body(); - let source_owned = builder.create_source_owned()?; + let source_owned = builder.create_source_owned_predicate()?; builder.add_statement(vir_low::Statement::unfold_no_pos(source_owned)); } match &type_decl { @@ -1650,9 +1950,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { _ => unimplemented!("{type_decl:?}"), } if has_body { - let target_owned = builder.create_target_owned()?; + let target_owned = builder.create_target_owned(true)?; builder.add_statement(vir_low::Statement::fold_no_pos(target_owned)); - let source_owned = builder.create_source_owned()?; + let source_owned = builder.create_source_owned_predicate()?; builder.add_statement(vir_low::Statement::fold_no_pos(source_owned)); } let method = builder.build(); @@ -1698,7 +1998,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { let target_memory_block = builder.create_target_memory_block()?; builder.add_precondition(target_memory_block); builder.add_source_validity_precondition()?; - let target_owned = builder.create_target_owned()?; + let target_owned = builder.create_target_owned(false)?; builder.add_postcondition(target_owned); if has_body { builder.create_body(); @@ -1729,7 +2029,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { _ => unimplemented!("{type_decl:?}"), } if has_body { - let target_owned = builder.create_target_owned()?; + let target_owned = builder.create_target_owned(true)?; builder.add_statement(vir_low::Statement::fold_no_pos(target_owned)); } let method = builder.build(); @@ -1754,30 +2054,33 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { let method_name = self.encode_havoc_owned_non_aliased_method_name(ty)?; let type_decl = self.encoder.get_type_decl_mid(ty)?; var_decls! { - place: Place, + place: PlaceOption, root_address: Address, old_snapshot: {ty.to_snapshot(self)?}, fresh_snapshot: {ty.to_snapshot(self)?} }; + let position = vir_low::Position::default(); let validity = self.encode_snapshot_valid_call_for_type(fresh_snapshot.clone().into(), ty)?; let mut parameters = vec![place.clone(), root_address.clone(), old_snapshot.clone()]; parameters.extend(self.create_lifetime_parameters(&type_decl)?); - let predicate_in = self.owned_non_aliased_full_vars( + let predicate_in = self.owned_non_aliased_full_vars_with_snapshot( CallContext::BuiltinMethod, ty, &type_decl, &place, &root_address, &old_snapshot, + position, )?; - let predicate_out = self.owned_non_aliased_full_vars( + let predicate_out = self.owned_non_aliased_full_vars_with_snapshot( CallContext::BuiltinMethod, ty, &type_decl, &place, &root_address, &fresh_snapshot, + position, )?; let method = vir_low::MethodDecl::new( method_name, @@ -1810,6 +2113,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { .encoded_memory_block_split_methods .insert(ty_identifier); + self.encode_compute_address(ty)?; + let type_decl = self.encoder.get_type_decl_mid(ty)?; let normalized_type = ty.normalize_type(); let mut builder = MemoryBlockSplitMethodBuilder::new( @@ -1851,6 +2156,44 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { } Ok(()) } + fn encode_memory_block_range_split_method( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()> { + let ty_identifier = ty.get_identifier(); + if !self + .builtin_methods_state + .encoded_memory_block_range_split_methods + .contains(&ty_identifier) + { + assert!( + !ty.is_trusted() && !ty.is_type_var(), + "Trying to split an abstract type." + ); + self.builtin_methods_state + .encoded_memory_block_range_split_methods + .insert(ty_identifier); + + self.encode_compute_address(ty)?; + let type_decl = self.encoder.get_type_decl_mid(ty)?; + let normalized_type = ty.normalize_type(); + let mut builder = MemoryBlockRangeSplitMethodBuilder::new( + self, + vir_low::MethodKind::LowMemoryOperation, + "memory_block_range_split", + &normalized_type, + &type_decl, + BuiltinMethodKind::JoinMemoryBlock, + )?; + builder.create_parameters()?; + builder.add_whole_memory_block_precondition()?; + builder.add_memory_block_range_postcondition()?; + builder.add_byte_values_preserved_postcondition()?; + let method = builder.build(); + self.declare_method(method)?; + } + Ok(()) + } fn encode_memory_block_join_method(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()> { let ty_identifier = ty.get_identifier(); if !self @@ -1866,6 +2209,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { .encoded_memory_block_join_methods .insert(ty_identifier); + self.encode_compute_address(ty)?; let type_decl = self.encoder.get_type_decl_mid(ty)?; let normalized_type = ty.normalize_type(); let mut builder = MemoryBlockJoinMethodBuilder::new( @@ -1919,6 +2263,44 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { } Ok(()) } + fn encode_memory_block_range_join_method( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()> { + let ty_identifier = ty.get_identifier(); + if !self + .builtin_methods_state + .encoded_memory_block_range_join_methods + .contains(&ty_identifier) + { + assert!( + !ty.is_trusted() && !ty.is_type_var(), + "Trying to join an abstract type." + ); + self.builtin_methods_state + .encoded_memory_block_range_join_methods + .insert(ty_identifier); + + self.encode_compute_address(ty)?; + let type_decl = self.encoder.get_type_decl_mid(ty)?; + let normalized_type = ty.normalize_type(); + let mut builder = MemoryBlockRangeJoinMethodBuilder::new( + self, + vir_low::MethodKind::LowMemoryOperation, + "memory_block_range_join", + &normalized_type, + &type_decl, + BuiltinMethodKind::JoinMemoryBlock, + )?; + builder.create_parameters()?; + builder.add_memory_block_range_precondition()?; + builder.add_whole_memory_block_postcondition()?; + builder.add_byte_values_preserved_postcondition()?; + let method = builder.build(); + self.declare_method(method)?; + } + Ok(()) + } fn encode_havoc_memory_block_method_name( &mut self, ty: &vir_mid::Type, @@ -1982,7 +2364,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { .encoded_into_memory_block_methods .insert(ty_identifier); - self.mark_owned_non_aliased_as_unfolded(ty)?; + self.mark_owned_predicate_as_unfolded(ty)?; let type_decl = self.encoder.get_type_decl_mid(ty)?; let normalized_type = ty.normalize_type(); @@ -1996,7 +2378,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { )?; builder.create_parameters()?; builder.add_const_parameters_validity_precondition()?; - let predicate = builder.create_owned()?; + let predicate = builder.create_owned(false)?; builder.add_precondition(predicate); let memory_block = builder.create_target_memory_block()?; builder.add_postcondition(memory_block); @@ -2011,7 +2393,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { ); if has_body { builder.create_body(); - let predicate = builder.create_owned()?; + let predicate = builder.create_owned(true)?; builder.add_statement(vir_low::Statement::unfold_no_pos(predicate)); } match &type_decl { @@ -2066,7 +2448,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { let method_name = self.encode_assign_method_name(target.get_type(), &value)?; self.encode_assign_method(&method_name, target.get_type(), &value)?; let target_place = self.encode_expression_as_place(&target)?; - let target_address = self.extract_root_address(&target)?; + let target_address = self.encode_expression_as_place_address(&target)?; + // let target_address = self.extract_root_address(&target)?; let mut arguments = vec![target_place.clone(), target_address.clone()]; self.encode_rvalue_arguments(&target, &mut arguments, &value)?; let target_value_type = target.get_type().to_snapshot(self)?; @@ -2087,7 +2470,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { vir_mid::Expression::field(value.place.clone(), discriminant_field, position) }; let source_place = self.encode_expression_as_place(&source)?; - let source_root_address = self.extract_root_address(&source)?; + // let source_root_address = self.extract_root_address(&source)?; + let source_address = self.encode_expression_as_place_address(&source)?; let source_permission_amount = if let Some(source_permission) = &value.source_permission { source_permission.to_procedure_snapshot(self)?.into() @@ -2104,17 +2488,16 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { target_place, target_address, source_place, - source_root_address, + source_address, source_snapshot.clone(), source_permission_amount, )?]; - let new_snapshot = self.new_snapshot_variable_version(&target.get_base(), position)?; - self.encode_snapshot_update_with_new_snapshot( + let new_snapshot = self.encode_snapshot_update_with_new_snapshot( &mut copy_place_statements, &target, source_snapshot, position, - Some(new_snapshot.clone()), + // Some(new_snapshot.clone()), )?; if let Some(conditions) = value.use_field { let mut disjuncts = Vec::new(); @@ -2122,12 +2505,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { disjuncts.push(self.lower_block_marker_condition(condition)?); } let mut else_branch = vec![assign_statement]; - self.encode_snapshot_update_with_new_snapshot( + let new_snapshot_else_branch = self.encode_snapshot_update_with_new_snapshot( &mut else_branch, &target, result_value.into(), position, - Some(new_snapshot), + // Some(new_snapshot), )?; statements.push(vir_low::Statement::conditional( disjuncts.into_iter().disjoin(), @@ -2135,6 +2518,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { else_branch, position, )); + statements.push(vir_low::Statement::assume( + vir_low::Expression::equals(new_snapshot, new_snapshot_else_branch), + position, + )); } else { // Use field unconditionally. statements.extend(copy_place_statements); @@ -2147,21 +2534,69 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { result_value.clone().into(), position, )?; - if let vir_mid::Rvalue::Ref(value) = value { - let snapshot = if value.uniqueness.is_unique() { - self.reference_target_final_snapshot( - target.get_type(), - result_value.into(), - position, - )? - } else { - self.reference_target_current_snapshot( - target.get_type(), - result_value.into(), - position, - )? - }; - self.encode_snapshot_update(statements, &value.place, snapshot, position)?; + // if let vir_mid::Rvalue::Ref(value) = value { + // let snapshot = if value.uniqueness.is_unique() { + // self.reference_target_final_snapshot( + // target.get_type(), + // result_value.into(), + // position, + // )? + // } else { + // self.reference_target_current_snapshot( + // target.get_type(), + // result_value.into(), + // position, + // )? + // }; + // self.encode_snapshot_update(statements, &value.place, snapshot, position)?; + // } + match value { + vir_mid::Rvalue::Ref(value) => { + let snapshot = if value.uniqueness.is_unique() { + self.reference_target_final_snapshot( + target.get_type(), + result_value.into(), + position, + )? + } else { + self.reference_target_current_snapshot( + target.get_type(), + result_value.into(), + position, + )? + }; + self.encode_snapshot_update(statements, &value.place, snapshot, position)?; + } + // vir_mid::Rvalue::AddressOf(value) => { + // let address = self.pointer_address( + // target.get_type(), + // result_value.clone().into(), + // position, + // )?; + // let heap = self.heap_variable_version_at_label(&None)?; + // // statements.push(vir_low::Statement::assume( + // // vir_low::Expression::container_op_no_pos( + // // vir_low::ContainerOpKind::MapContains, + // // heap.ty.clone(), + // // vec![heap.into(), address], + // // ), + // // position, + // // )); + // let heap_chunk = self.pointer_target_snapshot( + // target.get_type(), + // &None, + // result_value.into(), + // position, + // )?; + // statements.push(vir_low::Statement::assume( + // vir_low::Expression::equals( + // heap_chunk, + // value.place.to_procedure_snapshot(self)?, + // ), + // position, + // )); + // } + _ => {} } } Ok(()) @@ -2176,6 +2611,27 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { self.encode_consume_operand_method(&method_name, &operand, position)?; let mut arguments = Vec::new(); self.encode_operand_arguments(&mut arguments, &operand, true)?; + { + // Mark the produced MemoryBlock as non-aliasable instance. + assert!( + matches!(operand.kind, vir_mid::OperandKind::Move | vir_mid::OperandKind::Constant), + "Our assumption that all consume operands are either moves or constants is wrong: {operand}." + ); + if vir_mid::OperandKind::Move == operand.kind { + assert!( + operand.expression.is_local(), + "Our assumption that all consume operands are moves of locals is wrong: {operand}." + ); + self.mark_place_as_used_in_memory_block(&operand.expression)?; + } + // let ty = operand.expression.get_type(); + // let address = self.encode_expression_as_place_address(&operand.expression)?; + // let size = self.encode_type_size_expression2(ty, ty)?; + // let predicate_acc = self + // .encode_memory_block_stack_acc(address, size, position)? + // .unwrap_predicate_access_predicate(); + // self.mark_predicate_as_non_aliased(predicate_acc)?; + } statements.push(vir_low::Statement::method_call( method_name, arguments, @@ -2194,9 +2650,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { vir_mid::Predicate::OwnedNonAliased(predicate) => { let ty = predicate.place.get_type(); self.encode_havoc_owned_non_aliased_method(ty)?; - self.mark_owned_non_aliased_as_unfolded(ty)?; + self.mark_owned_predicate_as_unfolded(ty)?; let place = self.encode_expression_as_place(&predicate.place)?; - let address = self.extract_root_address(&predicate.place)?; + // let address = self.extract_root_address(&predicate.place)?; + let address = self.encode_expression_as_place_address(&predicate.place)?; let old_snapshot = predicate.place.to_procedure_snapshot(self)?; let snapshot_type = ty.to_snapshot(self)?; let fresh_snapshot = self.create_new_temporary_variable(snapshot_type)?; @@ -2262,63 +2719,179 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { lifetime: Lifetime, lifetime_perm: Perm, owned_perm: Perm, - place: Place, - root_address: Address, + place: PlaceOption, + address: Address, current_snapshot: {ty.to_snapshot(self)?} }; + let position = vir_low::Position::default(); let lifetime_access = expr! { acc(LifetimeToken(lifetime), lifetime_perm) }; - let frac_ref_access = self.frac_ref_full_vars( + let TODO_target_slice_len = None; + let frac_ref_access = self.frac_ref_full_vars_with_current_snapshot( CallContext::BuiltinMethod, ty, ty, &place, - &root_address, + &address, ¤t_snapshot, &lifetime, + TODO_target_slice_len.clone(), + position, )?; - let owned_access = self.owned_non_aliased( + let prestate_snapshot = vir_low::Expression::labelled_old_no_pos( + None, + self.frac_ref_snap( + CallContext::BuiltinMethod, + ty, + ty, + place.clone().into(), + address.clone().into(), + lifetime.clone().into(), + TODO_target_slice_len, + position, + )?, + ); + let owned_access = self.owned_non_aliased_with_snapshot( CallContext::BuiltinMethod, ty, &type_decl, place.clone().into(), - root_address.clone().into(), - current_snapshot.clone().into(), + address.clone().into(), + prestate_snapshot, Some(owned_perm.clone().into()), + position, )?; - let method = vir_low::MethodDecl::new( - self.encode_open_frac_bor_atomic_method_name(ty)?, - vir_low::MethodKind::MirOperation, - vec![ + let owned_access_viewshift = self.owned_non_aliased( + CallContext::BuiltinMethod, + ty, + &type_decl, + place.clone().into(), + address.clone().into(), + Some(owned_perm.clone().into()), + position, + )?; + let parameters = vec![ + lifetime.clone(), + lifetime_perm.clone(), + place.clone(), + address.clone(), + current_snapshot.clone(), + ]; + let close_frac_ref_predicate = expr! { + acc(CloseFracRef( lifetime, - lifetime_perm.clone(), + lifetime_perm, place, - root_address, + address, current_snapshot, - ], + owned_perm + )) + }; + let lifetime_perm_bounds = expr! { + ([vir_low::Expression::no_permission()] < lifetime_perm) && + (lifetime_perm < [vir_low::Expression::full_permission()]) + }; + let owned_perm_bounds = expr! { + ([vir_low::Expression::no_permission()] < owned_perm) && + (owned_perm < [vir_low::Expression::full_permission()]) + }; + let method = vir_low::MethodDecl::new( + self.encode_open_frac_bor_atomic_method_name(ty)?, + vir_low::MethodKind::MirOperation, + parameters, vec![owned_perm.clone()], vec![ - expr! { - [vir_low::Expression::no_permission()] < lifetime_perm - }, + lifetime_perm_bounds.clone(), lifetime_access.clone(), frac_ref_access.clone(), ], vec![ - expr! { - owned_perm < [vir_low::Expression::full_permission()] - }, - expr! { - [vir_low::Expression::no_permission()] < owned_perm - }, - owned_access.clone(), - vir_low::Expression::magic_wand_no_pos( - owned_access, - expr! { [lifetime_access] && [frac_ref_access] }, - ), + owned_perm_bounds.clone(), + owned_access, + close_frac_ref_predicate.clone(), + // vir_low::Expression::magic_wand_no_pos( + // owned_access_viewshift, + // expr! { [lifetime_access] && [frac_ref_access] }, + // ), ], None, ); self.declare_method(method)?; + { + let close_frac_ref_predicate_decl = vir_low::PredicateDecl::new( + predicate_name! { CloseFracRef }, + vir_low::PredicateKind::WithoutSnapshotWhole, + vec![ + lifetime.clone(), + lifetime_perm.clone(), + place.clone(), + address.clone(), + current_snapshot.clone(), + owned_perm.clone(), + ], + None, + ); + self.declare_predicate(close_frac_ref_predicate_decl)?; + // Apply the viewshift encoded in the `CloseFracRef` predicate. + let close_method = vir_low::MethodDecl::new( + method_name! { close_frac_ref }, + vir_low::MethodKind::MirOperation, + vec![ + lifetime.clone(), + lifetime_perm, + place.clone(), + address.clone(), + current_snapshot.clone(), + owned_perm, + ], + Vec::new(), + vec![ + lifetime_perm_bounds, + owned_perm_bounds, + close_frac_ref_predicate, + owned_access_viewshift, + ], + vec![lifetime_access, frac_ref_access], + None, + ); + self.declare_method(close_method)?; + } + } + Ok(()) + } + + fn encode_restore_raw_borrowed_method( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()> { + let ty_identifier = ty.get_identifier(); + if !self + .builtin_methods_state + .encoded_restore_raw_borrowed_methods + .contains(&ty_identifier) + { + self.builtin_methods_state + .encoded_restore_raw_borrowed_methods + .insert(ty_identifier); + + self.encode_restore_raw_borrowed_transition_predicate(ty)?; + + let type_decl = self.encoder.get_type_decl_mid(ty)?; + let normalized_type = ty.normalize_type(); + + let mut builder = RestoreRawBorrowedMethodBuilder::new( + self, + vir_low::MethodKind::LowMemoryOperation, + "restore_raw_borrowed", + &normalized_type, + &type_decl, + BuiltinMethodKind::RestoreRawBorrowed, + )?; + builder.create_parameters()?; + builder.add_aliased_source_precondition()?; + builder.add_shift_precondition()?; + builder.add_non_aliased_target_postcondition()?; + let method = builder.build(); + self.declare_method(method)?; } Ok(()) } @@ -2554,28 +3127,31 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { var_decls! { lifetime: Lifetime, lifetime_perm: Perm, - place: Place, - root_address: Address, + place: PlaceOption, + address: Address, current_snapshot: {ty.to_snapshot(self)?}, final_snapshot: {ty.to_snapshot(self)?} }; - let owned_predicate = self.owned_non_aliased_full_vars( + let position = vir_low::Position::default(); + let owned_predicate = self.owned_non_aliased_full_vars_with_snapshot( CallContext::BuiltinMethod, ty, &type_decl, &place, - &root_address, + &address, ¤t_snapshot, + position, )?; - let unique_ref_predicate = self.unique_ref_full_vars( + let unique_ref_predicate = self.unique_ref_full_vars_with_current_snapshot( CallContext::BuiltinMethod, ty, &type_decl, &place, - &root_address, + &address, ¤t_snapshot, - &final_snapshot, &lifetime, + None, + position, )?; let open_method = vir_low::MethodDecl::new( method_name! { open_mut_ref }, @@ -2584,7 +3160,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { lifetime.clone(), lifetime_perm.clone(), place.clone(), - root_address.clone(), + address.clone(), current_snapshot.clone(), final_snapshot.clone(), ], @@ -2618,7 +3194,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { lifetime, lifetime_perm, place, - root_address, + address, final_snapshot ))}, ], @@ -2629,11 +3205,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { { let close_mut_ref_predicate = vir_low::PredicateDecl::new( predicate_name! { CloseMutRef }, + vir_low::PredicateKind::WithoutSnapshotWhole, vec![ lifetime.clone(), lifetime_perm.clone(), place.clone(), - root_address.clone(), + address.clone(), final_snapshot.clone(), ], None, @@ -2647,7 +3224,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { lifetime.clone(), lifetime_perm.clone(), place.clone(), - root_address.clone(), + address.clone(), current_snapshot.clone(), final_snapshot.clone(), ], @@ -2658,7 +3235,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { lifetime, lifetime_perm, place, - root_address, + address, final_snapshot ))}, owned_predicate, @@ -2693,8 +3270,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { lft: Lifetime, old_lft: Lifetime, lifetime_perm: Perm, - place: Place, - root_address: Address, + place: PlaceOption, + address: Address, current_snapshot: {target_type.to_snapshot(self)?}, final_snapshot: {target_type.to_snapshot(self)?} } @@ -2703,12 +3280,13 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { old_lft.clone(), lifetime_perm.clone(), place.clone(), - root_address.clone(), + address.clone(), current_snapshot.clone(), ]; if reference_type.uniqueness.is_unique() { - parameters.push(final_snapshot.clone()); + parameters.push(final_snapshot); } + let position = vir_low::Position::default(); let mut pres = vec![ expr! { [vir_low::Expression::no_permission()] < lifetime_perm }, expr! { lifetime_perm < [vir_low::Expression::full_permission()] }, @@ -2717,44 +3295,50 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { ]; let mut posts = vec![expr! { acc(LifetimeToken(lft), lifetime_perm)}]; if reference_type.uniqueness.is_unique() { - pres.push(self.unique_ref_full_vars( + pres.push(self.unique_ref_full_vars_with_current_snapshot( CallContext::BuiltinMethod, target_type, target_type, &place, - &root_address, + &address, ¤t_snapshot, - &final_snapshot, &old_lft, + None, + position, )?); - posts.push(self.unique_ref_full_vars( + posts.push(self.unique_ref_full_vars_with_current_snapshot( CallContext::BuiltinMethod, target_type, target_type, &place, - &root_address, + &address, ¤t_snapshot, - &final_snapshot, &lft, + None, + position, )?); } else { - pres.push(self.frac_ref_full_vars( + pres.push(self.frac_ref_full_vars_with_current_snapshot( CallContext::BuiltinMethod, target_type, target_type, &place, - &root_address, + &address, ¤t_snapshot, &old_lft, + None, + position, )?); - posts.push(self.frac_ref_full_vars( + posts.push(self.frac_ref_full_vars_with_current_snapshot( CallContext::BuiltinMethod, target_type, target_type, &place, - &root_address, + &address, ¤t_snapshot, &lft, + None, + position, )?); } parameters.extend(self.create_lifetime_parameters(target_type)?); @@ -2771,4 +3355,416 @@ impl<'p, 'v: 'p, 'tcx: 'v> BuiltinMethodsInterface for Lowerer<'p, 'v, 'tcx> { } Ok(()) } + + fn encode_stash_range_call( + &mut self, + statements: &mut Vec, + ty: &vir_mid::Type, + pointer_value: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + label: String, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + statements.push(vir_low::Statement::comment(format!( + "Stash range call for {label}" + ))); + // statements.push(vir_low::Statement::label(label.clone(), position)); + let exhale_owned = vir_low::Statement::exhale( + self.owned_aliased_range( + CallContext::Procedure, + ty, + ty, + pointer_value.clone(), + start_index.clone(), + end_index.clone(), + None, + position, + )?, + position, + ); + statements.push(exhale_owned); + let vir_mid::Type::Pointer(pointer_type) = ty else { + unreachable!("ty: {}", ty); + }; + let target_type = &*pointer_type.target_type; + let ty_identifier = target_type.get_identifier(); + if !self + .builtin_methods_state + .encoded_stashed_owned_aliased_predicates + .contains(&ty_identifier) + { + self.builtin_methods_state + .encoded_stashed_owned_aliased_predicates + .insert(ty_identifier); + let predicate = vir_low::PredicateDecl::new( + predicate_name! { StashedOwnedAliased }, + vir_low::PredicateKind::WithoutSnapshotWhole, + vec![ + var! { index: Int }, + var! { bytes: Bytes }, + var! { snapshot: { target_type.to_snapshot(self)? } }, + ], + None, + ); + self.declare_predicate(predicate)?; + } + let start_address = self.pointer_address(ty, pointer_value, position)?; + let size = self.encode_type_size_expression2(target_type, target_type)?; + let inhale_raw = vir_low::Statement::inhale( + self.encode_memory_block_range_acc( + start_address.clone(), + size.clone(), + start_index.clone(), + end_index.clone(), + position, + )?, + position, + ); + statements.push(inhale_raw); + let inhale_stash = { + let size_type = self.size_type_mid()?; + var_decls! { + index: Int + } + // let start_address = self.pointer_address( + // ty, + // pointer_value, + // position, + // )?; + let element_address = + self.address_offset(size.clone(), start_address, index.clone().into(), position)?; + let start_index = self.obtain_constant_value(&size_type, start_index, position)?; + let end_index = self.obtain_constant_value(&size_type, end_index, position)?; + let bytes = self.encode_memory_block_bytes_expression(element_address.clone(), size)?; + let snapshot = vir_low::Expression::labelled_old( + Some(label), + self.owned_aliased_snap( + CallContext::Procedure, + target_type, + target_type, + element_address.clone(), + position, + )?, + position, + ); + let stash_predicate = expr! { + acc(StashedOwnedAliased( + index, + [bytes], + [snapshot] + )) + }; + let body = expr!( + (([start_index] <= index) && (index < [end_index])) ==> + [stash_predicate] + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![element_address])], + body, + ); + vir_low::Statement::inhale(expression, position) + }; + statements.push(inhale_stash); + // statements.push(vir_low::Statement::label( + // format!("{}$post", label), + // position, + // )); + Ok(()) + } + + fn encode_restore_stash_range_call( + &mut self, + statements: &mut Vec, + ty: &vir_mid::Type, + old_pointer_value: vir_low::Expression, + old_start_index: vir_low::Expression, + old_end_index: vir_low::Expression, + label: String, + new_pointer_value: vir_low::Expression, + new_start_index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + statements.push(vir_low::Statement::comment(format!( + "Restore stash for {label}" + ))); + let label_post = format!("{label}$post"); + use vir_low::macros::*; + let vir_mid::Type::Pointer(pointer_type) = ty else { + unreachable!("ty: {}", ty); + }; + let size_type = self.size_type_mid()?; + let target_type = &*pointer_type.target_type; + let size = self.encode_type_size_expression2(target_type, target_type)?; + let old_start_address = self.pointer_address(ty, old_pointer_value, position)?; + let new_start_address = self.pointer_address(ty, new_pointer_value, position)?; + let new_start_index = self.obtain_constant_value(&size_type, new_start_index, position)?; + let new_end_index = vir_low::Expression::add( + new_start_index.clone(), + vir_low::Expression::labelled_old( + Some(label.clone()), + vir_low::Expression::subtract( + self.obtain_constant_value(&size_type, old_end_index, position)?, + self.obtain_constant_value(&size_type, old_start_index.clone(), position)?, + ), + position, + ), + ); + let assume_extensionality = { + // For performance reasons, we do not have global extensionality + // assumptions, but assume them when needed. + + // FIXME: Instead of having the assumption as a quantifier, assert + // that bytes are equal for the entire range and then assume that + // the byte blocks are equal. + var_decls! { + index: Int, + byte_index: Int + } + let new_element_address = self.address_offset( + size.clone(), + new_start_address.clone(), + index.clone().into(), + position, + )?; + let old_index = vir_low::Expression::add( + vir_low::Expression::labelled_old( + Some(label.clone()), + self.obtain_constant_value(&size_type, old_start_index.clone(), position)?, + position, + ), + vir_low::Expression::subtract(index.clone().into(), new_start_index.clone()), + ); + let old_element_address = + self.address_offset(size.clone(), old_start_address.clone(), old_index, position)?; + let new_block_bytes = self + .encode_memory_block_bytes_expression(new_element_address.clone(), size.clone())?; + let old_block_bytes = + self.encode_memory_block_bytes_expression(old_element_address, size.clone())?; + let old_block_bytes = + vir_low::Expression::labelled_old(Some(label_post), old_block_bytes, position); + let element_size_int = + self.obtain_constant_value(&size_type, size.clone(), position)?; + let new_read_element_byte = self.encode_read_byte_expression_int( + new_block_bytes.clone(), + byte_index.clone().into(), + position, + )?; + let old_read_element_byte = self.encode_read_byte_expression_int( + old_block_bytes.clone(), + byte_index.clone().into(), + position, + )?; + let bytes_equal_body = expr!( + ((([new_start_index.clone()] <= index) && (index < [new_end_index.clone()])) && + (([0.into()] <= byte_index) && (byte_index < [element_size_int]))) ==> + ([new_read_element_byte.clone()] == [old_read_element_byte]) + ); + let bytes_equal = vir_low::Expression::forall( + vec![index.clone(), byte_index], + vec![vir_low::Trigger::new(vec![new_read_element_byte])], + bytes_equal_body, + ); + let assert_byte_equality = vir_low::Statement::assert(bytes_equal, position); + statements.push(assert_byte_equality); + let body = expr!( + (([new_start_index.clone()] <= index) && (index < [new_end_index.clone()])) ==> + // ( + ([new_block_bytes] == [old_block_bytes]) + // == [bytes_equal]) + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![new_element_address])], + body, + ); + vir_low::Statement::assume(expression, position) + }; + statements.push(assume_extensionality); + let exhale_stash = { + var_decls! { + index: Int + } + // let start_address = self.pointer_address( + // ty, + // pointer_value, + // position, + // )?; + let old_index = vir_low::Expression::add( + vir_low::Expression::labelled_old( + Some(label.clone()), + self.obtain_constant_value(&size_type, old_start_index.clone(), position)?, + position, + ), + vir_low::Expression::subtract(index.clone().into(), new_start_index.clone()), + ); + let old_element_address = self.address_offset( + size.clone(), + old_start_address.clone(), + old_index.clone(), + position, + )?; + let new_element_address = self.address_offset( + size.clone(), + new_start_address.clone(), + index.clone().into(), + position, + )?; + // let start_index = self.obtain_constant_value(&size_type, start_index, position)?; + // let end_index = self.obtain_constant_value(&size_type, end_index, position)?; + let bytes = self + .encode_memory_block_bytes_expression(new_element_address.clone(), size.clone())?; + let snapshot = vir_low::Expression::labelled_old( + Some(label.clone()), + self.owned_aliased_snap( + CallContext::Procedure, + target_type, + target_type, + old_element_address, + position, + )?, + position, + ); + let stash_predicate = expr! { + acc(StashedOwnedAliased( + [old_index], + [bytes], + [snapshot] + )) + }; + let body = expr!( + (([new_start_index.clone()] <= index) && (index < [new_end_index.clone()])) ==> + [stash_predicate] + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![new_element_address])], + body, + ); + vir_low::Statement::exhale(expression, position) + }; + statements.push(exhale_stash); + // FIXME: Code duplication with encode_memory_block_range_acc. + let exhale_raw = { + var_decls! { + index: Int + } + let element_address = self.address_offset( + size.clone(), + new_start_address.clone(), + index.clone().into(), + position, + )?; + let predicate = self.encode_memory_block_stack_acc( + element_address.clone(), + size.clone(), + position, + )?; + // let new_start_index = self.obtain_constant_value(&size_type, new_start_address.clone(), position)?; + // let end_index = self.obtain_constant_value(&size_type, end_index, position)?; + let body = expr!( + (([new_start_index.clone()] <= index) && (index < [new_end_index.clone()])) ==> [predicate] + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![element_address])], + body, + ); + vir_low::Statement::exhale( + expression, + // self.encode_memory_block_range_acc( + // new_start_address.clone(), + // size, + // new_start_index.clone(), + // new_end_index.clone(), + // position, + // )?, + position, + ) + }; + statements.push(exhale_raw); + let inhale_owned = { + var_decls! { + index: Int + } + let element_address = self.address_offset( + size.clone(), + new_start_address.clone(), + index.clone().into(), + position, + )?; + let predicate = self.owned_aliased( + CallContext::Procedure, + target_type, + target_type, + element_address.clone(), + None, + position, + )?; + // let new_start_index = self.obtain_constant_value(&size_type, new_start_address.clone(), position)?; + // let end_index = self.obtain_constant_value(&size_type, end_index, position)?; + let body = expr!( + (([new_start_index.clone()] <= index) && (index < [new_end_index.clone()])) ==> + [predicate] + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![element_address])], + body, + ); + vir_low::Statement::inhale(expression, position) + }; + statements.push(inhale_owned); + let inhale_snapshot_preserved = { + var_decls! { + index: Int + } + let new_element_address = self.address_offset( + size.clone(), + new_start_address, + index.clone().into(), + position, + )?; + let old_index = vir_low::Expression::add( + vir_low::Expression::labelled_old( + Some(label.clone()), + self.obtain_constant_value(&size_type, old_start_index, position)?, + position, + ), + vir_low::Expression::subtract(index.clone().into(), new_start_index.clone()), + ); + let old_element_address = + self.address_offset(size, old_start_address, old_index, position)?; + let new_snapshot = self.owned_aliased_snap( + CallContext::Procedure, + target_type, + target_type, + new_element_address.clone(), + position, + )?; + let old_snapshot = self.owned_aliased_snap( + CallContext::Procedure, + target_type, + target_type, + old_element_address, + position, + )?; + let old_snapshot = + vir_low::Expression::labelled_old(Some(label), old_snapshot, position); + let body = expr!( + (([new_start_index] <= index) && (index < [new_end_index])) ==> + ([new_snapshot] == [old_snapshot]) + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![new_element_address])], + body, + ); + vir_low::Statement::inhale(expression, position) + }; + statements.push(inhale_snapshot_preserved); + Ok(()) + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/mod.rs b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/mod.rs index a04aa21f1cd..7f84d0111ec 100644 --- a/prusti-viper/src/encoder/middle/core_proof/builtin_methods/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/builtin_methods/mod.rs @@ -1,3 +1,4 @@ +mod assertion_encoder; mod builders; mod calls; mod interface; diff --git a/prusti-viper/src/encoder/middle/core_proof/compute_address/interface.rs b/prusti-viper/src/encoder/middle/core_proof/compute_address/interface.rs index 24fa0641fc7..c40abb6b992 100644 --- a/prusti-viper/src/encoder/middle/core_proof/compute_address/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/compute_address/interface.rs @@ -1,89 +1,120 @@ use crate::encoder::{ errors::SpannedEncodingResult, high::types::HighTypeEncoderInterface, - middle::core_proof::{ - addresses::AddressesInterface, lowerer::Lowerer, places::PlacesInterface, - references::ReferencesInterface, snapshots::IntoSnapshot, - }, + middle::core_proof::{addresses::AddressesInterface, lowerer::Lowerer}, }; use rustc_hash::FxHashSet; -use vir_crate::{common::identifier::WithIdentifier, low as vir_low, middle as vir_mid}; +use vir_crate::{ + common::identifier::WithIdentifier, + low::{self as vir_low}, + middle as vir_mid, +}; #[derive(Default)] pub(in super::super) struct ComputeAddressState { - pub(super) encoded_types: FxHashSet, + pub(super) encoded_types: FxHashSet, pub(super) encoded_roots: FxHashSet, - pub(super) axioms: Vec, + pub(super) rewrite_rules: Vec, } impl ComputeAddressState { /// Construct the final domain. pub(in super::super) fn destruct(self) -> Option { + // None if self.encoded_types.is_empty() && self.encoded_roots.is_empty() { None } else { Some(vir_low::DomainDecl { name: "ComputeAddress".to_string(), - functions: vec![vir_low::DomainFunctionDecl { - name: "compute_address".to_string(), - is_unique: false, - parameters: vir_low::macros::vars! { - place: Place, - address: Address + functions: vec![ + // vir_low::DomainFunctionDecl { + // name: "compute_address".to_string(), + // is_unique: false, + // parameters: vir_low::macros::vars! { + // place: PlaceOption, + // address: Address + // }, + // return_type: vir_low::macros::ty! { Address }, + // }, + vir_low::DomainFunctionDecl { + name: "address_is_non_aliased".to_string(), + is_unique: false, + parameters: vir_low::macros::vars! { + address: Address + }, + return_type: vir_low::Type::Bool, }, - return_type: vir_low::macros::ty! { Address }, - }], - axioms: self.axioms, + ], + axioms: Vec::new(), + rewrite_rules: self.rewrite_rules, }) } } } -trait Private { - fn encode_compute_address_axiom_for_field( - &mut self, - ty: &vir_mid::Type, - field: &vir_mid::FieldDecl, - ) -> SpannedEncodingResult; -} +impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { + // fn encode_compute_address_axiom_for_field( + // &mut self, + // ty: &vir_mid::Type, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let compute_address = ty!(Address); + // var_decls! { + // place: Place, + // address: Address + // }; + // let place_option = self.place_option_some_constructor(place.clone().into())?; + // let field_place = + // self.encode_field_place(ty, field, place_option.clone(), Default::default())?; + // let field_address = self.encode_field_address( + // ty, + // field, + // expr! { ComputeAddress::compute_address([place_option], address) }, + // Default::default(), + // )?; + // let source = expr! { (ComputeAddress::compute_address([field_place], address)) }; + // Ok(vir_low::DomainRewriteRuleDecl { + // comment: None, + // name: format!( + // "{}${}$compute_address_axiom", + // ty.get_identifier(), + // field.name + // ), + // egg_only: false, + // variables: vec![place, address], + // triggers: None, + // source, + // target: field_address, + // }) + // } -impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { - fn encode_compute_address_axiom_for_field( + fn encode_propagate_address_non_aliased_axiom_for_field( &mut self, ty: &vir_mid::Type, field: &vir_mid::FieldDecl, - ) -> SpannedEncodingResult { + ) -> SpannedEncodingResult { use vir_low::macros::*; - let compute_address = ty!(Address); - let body = expr! { - forall( - place: Place, address: Address :: - raw_code { - let field_place = self.encode_field_place( - ty, - field, - place.clone().into(), - Default::default() - )?; - let field_address = self.encode_field_address( - ty, - field, - expr! { ComputeAddress::compute_address(place, address) }, - Default::default(), - )?; - } - [ { (ComputeAddress::compute_address([field_place.clone()], address)) } ] - (ComputeAddress::compute_address([field_place], address)) == [field_address] - ) + let address_is_non_aliased = ty!(Bool); + var_decls! { + address: Address }; - Ok(vir_low::DomainAxiomDecl { + let field_address = + self.encode_field_address(ty, field, address.clone().into(), Default::default())?; + let source = expr! { (ComputeAddress::address_is_non_aliased([field_address])) }; + let target = expr! { (ComputeAddress::address_is_non_aliased(address)) }; + Ok(vir_low::DomainRewriteRuleDecl { comment: None, name: format!( - "{}${}$compute_address_axiom", + "{}${}$address_is_non_aliased_axiom", ty.get_identifier(), field.name ), - body, + egg_only: true, + variables: vec![address], + triggers: None, + source, + target, }) } } @@ -98,15 +129,15 @@ pub(in super::super) trait ComputeAddressInterface { impl<'p, 'v: 'p, 'tcx: 'v> ComputeAddressInterface for Lowerer<'p, 'v, 'tcx> { fn encode_compute_address(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()> { - let ty_without_lifetime = ty.normalize_type(); + let ty_identifier = ty.get_identifier(); if !self .compute_address_state .encoded_types - .contains(&ty_without_lifetime) + .contains(&ty_identifier) { self.compute_address_state .encoded_types - .insert(ty_without_lifetime); + .insert(ty_identifier); let type_decl = self.encoder.get_type_decl_mid(ty)?; match type_decl { @@ -122,89 +153,151 @@ impl<'p, 'v: 'p, 'tcx: 'v> ComputeAddressInterface for Lowerer<'p, 'v, 'tcx> { } vir_mid::TypeDecl::Struct(decl) => { for field in &decl.fields { - let axiom = self.encode_compute_address_axiom_for_field(ty, field)?; - self.compute_address_state.axioms.push(axiom); + // let axiom = self.encode_compute_address_axiom_for_field(ty, field)?; + // self.compute_address_state.rewrite_rules.push(axiom); + let axiom = + self.encode_propagate_address_non_aliased_axiom_for_field(ty, field)?; + self.compute_address_state.rewrite_rules.push(axiom); self.encode_compute_address(&field.ty)?; } } vir_mid::TypeDecl::Enum(decl) => { + // FIXME: Encode address_is_non_aliased axioms for enum variants. if decl.safety.is_enum() { let discriminant_field = decl.discriminant_field(); - let axiom = - self.encode_compute_address_axiom_for_field(ty, &discriminant_field)?; - self.compute_address_state.axioms.push(axiom); + // let axiom = + // self.encode_compute_address_axiom_for_field(ty, &discriminant_field)?; + // self.compute_address_state.rewrite_rules.push(axiom); + let axiom = self.encode_propagate_address_non_aliased_axiom_for_field( + ty, + &discriminant_field, + )?; + self.compute_address_state.rewrite_rules.push(axiom); } self.encode_compute_address(&decl.discriminant_type)?; for variant in &decl.variants { use vir_low::macros::*; - let compute_address = ty!(Address); - let body = expr! { - forall( - place: Place, address: Address :: - raw_code { - let variant_index = variant.name.clone().into(); - let variant_place = self.encode_enum_variant_place( - ty, - &variant_index, - place.clone().into(), - Default::default() - )?; - let variant_address = self.encode_enum_variant_address( - ty, - &variant_index, - expr! { ComputeAddress::compute_address(place, address) }, - Default::default(), - )?; - } - [ { (ComputeAddress::compute_address([variant_place.clone()], address)) } ] - (ComputeAddress::compute_address([variant_place], address)) == [variant_address] - ) + let address_is_non_aliased = ty!(Bool); + var_decls! { + address: Address + } + let variant_index = variant.name.clone().into(); + let variant_address = self.encode_enum_variant_address( + ty, + &variant_index, + address.clone().into(), + Default::default(), + )?; + let source = expr! { + (ComputeAddress::address_is_non_aliased([variant_address])) }; - let axiom = vir_low::DomainAxiomDecl { + let target = expr! { + (ComputeAddress::address_is_non_aliased(address)) + }; + let axiom = vir_low::DomainRewriteRuleDecl { comment: None, name: format!( "{}${}$compute_address_axiom", ty.get_identifier(), variant.name ), - body, + egg_only: true, + variables: vec![address], + triggers: None, + source, + target, }; - self.compute_address_state.axioms.push(axiom); + self.compute_address_state.rewrite_rules.push(axiom); let variant_ty = ty.clone().variant(variant.name.clone().into()); self.encode_compute_address(&variant_ty)?; + // self.encode_compute_address(&decl.discriminant_type)?; + // for variant in &decl.variants { + // use vir_low::macros::*; + // let compute_address = ty!(Address); + // var_decls! { + // place: Place, + // address: Address + // } + // let variant_index = variant.name.clone().into(); + // let variant_place = self.encode_enum_variant_place( + // ty, + // &variant_index, + // place.clone().into(), + // Default::default(), + // )?; + // let variant_address = self.encode_enum_variant_address( + // ty, + // &variant_index, + // expr! { ComputeAddress::compute_address(place, address) }, + // Default::default(), + // )?; + // let source = expr! { + // (ComputeAddress::compute_address([variant_place], address)) + // }; + // let axiom = vir_low::DomainRewriteRuleDecl { + // comment: None, + // name: format!( + // "{}${}$compute_address_axiom", + // ty.get_identifier(), + // variant.name + // ), + // egg_only: false, + // variables: vec![place, address], + // triggers: None, + // source, + // target: variant_address, + // }; + // self.compute_address_state.rewrite_rules.push(axiom); + // let variant_ty = ty.clone().variant(variant.name.clone().into()); + // self.encode_compute_address(&variant_ty)?; } } vir_mid::TypeDecl::Array(_decl) => { // FIXME: Doing nothing is probably wrong. } vir_mid::TypeDecl::Reference(_reference) => { - use vir_low::macros::*; - let compute_address = ty!(Address); - let body = expr! { - forall( - place: Place, snapshot: {ty.to_snapshot(self)?} :: - raw_code { - let position = vir_low::Position::default(); - let deref_place = self.reference_deref_place( - place.clone().into(), position)?; - let address = self.reference_address( - ty, - snapshot.clone().into(), - position, - )?; - } - [ { (ComputeAddress::compute_address( - [deref_place.clone()], [address.clone()])) } ] - (ComputeAddress::compute_address( - [deref_place], [address.clone()])) == [address] - ) - }; - let axiom = vir_low::DomainAxiomDecl { - comment: None, - name: format!("{}$compute_address_axiom", ty.get_identifier(),), - body, - }; - self.compute_address_state.axioms.push(axiom); + // use vir_low::macros::*; + // let compute_address = ty!(Address); + // let _body = expr! { + // forall( + // place: Place, snapshot: {ty.to_snapshot(self)?} :: + // raw_code { + // let position = vir_low::Position::default(); + // let deref_place = self.reference_deref_place( + // place.clone().into(), position)?; + // let address = self.reference_address( + // ty, + // snapshot.clone().into(), + // position, + // )?; + // } + // [ { (ComputeAddress::compute_address( + // [deref_place.clone()], [address.clone()])) } ] + // (ComputeAddress::compute_address( + // [deref_place], [address.clone()])) == [address] + // ) + // }; + // var_decls! { + // place: Place, + // snapshot: {ty.to_snapshot(self)?} + // } + // let position = vir_low::Position::default(); + // let deref_place = self.reference_deref_place(place.clone().into(), position)?; + // let address = self.reference_address(ty, snapshot.clone().into(), position)?; + // let source = expr! { + // (ComputeAddress::compute_address( + // [deref_place], [address.clone()])) + // }; + // let axiom = vir_low::DomainRewriteRuleDecl { + // comment: None, + // name: format!("{}$compute_address_axiom", ty.get_identifier(),), + // egg_only: false, + // variables: vec![place, snapshot], + // triggers: None, + // source, + // target: address, + // }; + // self.compute_address_state.rewrite_rules.push(axiom); } // vir_mid::TypeDecl::Never => {}, // vir_mid::TypeDecl::Closure(Closure) => {}, @@ -216,36 +309,39 @@ impl<'p, 'v: 'p, 'tcx: 'v> ComputeAddressInterface for Lowerer<'p, 'v, 'tcx> { } fn encode_compute_address_for_place_root( &mut self, - place_root: &vir_low::Expression, + _place_root: &vir_low::Expression, ) -> SpannedEncodingResult<()> { - if self - .compute_address_state - .encoded_roots - .contains(place_root) - { - return Ok(()); - } - self.compute_address_state - .encoded_roots - .insert(place_root.clone()); - use vir_low::macros::*; - let compute_address = ty! { Address }; - let body = expr! { - forall( - address: Address :: - [ { (ComputeAddress::compute_address([place_root.clone()], address)) } ] - (ComputeAddress::compute_address([place_root.clone()], address)) == address - ) - }; - let axiom = vir_low::DomainAxiomDecl { - comment: None, - name: format!( - "root${}$compute_address_axiom", - self.compute_address_state.encoded_roots.len() - ), - body, - }; - self.compute_address_state.axioms.push(axiom); + // debug_assert_eq!(place_root.get_type(), &self.place_type()?); + // if self + // .compute_address_state + // .encoded_roots + // .contains(place_root) + // { + // return Ok(()); + // } + // self.compute_address_state + // .encoded_roots + // .insert(place_root.clone()); + // use vir_low::macros::*; + // let compute_address = ty! { Address }; + // var_decls! { + // address: Address + // } + // let place_option_root = self.place_option_some_constructor(place_root.clone())?; + // let source = expr! { (ComputeAddress::compute_address([place_option_root], address)) }; + // let axiom = vir_low::DomainRewriteRuleDecl { + // comment: None, + // name: format!( + // "root${}$compute_address_axiom", + // self.compute_address_state.encoded_roots.len() + // ), + // egg_only: false, + // variables: vec![address.clone()], + // triggers: None, + // source, + // target: address.into(), + // }; + // self.compute_address_state.rewrite_rules.push(axiom); Ok(()) } } diff --git a/prusti-viper/src/encoder/middle/core_proof/footprint/interface.rs b/prusti-viper/src/encoder/middle/core_proof/footprint/interface.rs new file mode 100644 index 00000000000..7b15d770762 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/footprint/interface.rs @@ -0,0 +1,341 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{lowerer::Lowerer, snapshots::IntoSnapshot}, +}; +use std::collections::{BTreeMap, BTreeSet}; +use vir_crate::{ + common::position::Positioned, + low as vir_low, + middle::{self as vir_mid, operations::ty::Typed, visitors::ExpressionFolder}, +}; + +struct FootprintComputation<'l, 'p, 'v, 'tcx> { + _lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + // parameters: &'l BTreeMap, + deref_field_fresh_index_counters: BTreeMap, + deref_field_indices: BTreeMap, + derefs: Vec, +} + +// FIXME: Delete. +impl<'l, 'p, 'v, 'tcx> FootprintComputation<'l, 'p, 'v, 'tcx> { + fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + parameters: &'l BTreeMap, + ) -> Self { + let deref_field_fresh_index_counters = parameters + .iter() + .map(|(parameter, decl)| (parameter.clone(), decl.fields.len())) + .collect(); + Self { + _lowerer: lowerer, + // parameters, + deref_field_fresh_index_counters, + deref_field_indices: Default::default(), + derefs: Default::default(), + } + } + + fn extract_base_variable<'a>( + &self, + place: &'a vir_mid::Expression, + ) -> &'a vir_mid::VariableDecl { + match place { + vir_mid::Expression::Local(expression) => &expression.variable, + _ => unimplemented!(), + } + } + + // FIXME: This should be using `own` places. + fn create_deref_field(&mut self, deref: &vir_mid::Deref) -> vir_mid::Expression { + match &*deref.base { + vir_mid::Expression::Field(expression) => { + let variable = self.extract_base_variable(&expression.base); + let deref_field_name = format!("{}$deref", expression.field.name); + let deref_variable = vir_mid::VariableDecl::new(deref_field_name, deref.ty.clone()); + let field_index = self.compute_deref_field_index(deref, variable, &deref_variable); + vir_mid::Expression::field( + (*expression.base).clone(), + vir_mid::FieldDecl { + name: deref_variable.name, + index: field_index, + ty: deref_variable.ty, + }, + expression.position, + ) + } + _ => unimplemented!(), + } + } + + fn compute_deref_field_index( + &mut self, + deref: &vir_mid::Deref, + variable: &vir_mid::VariableDecl, + deref_variable: &vir_mid::VariableDecl, + ) -> usize { + if let Some(index) = self.deref_field_indices.get(deref_variable) { + *index + } else { + let counter = self + .deref_field_fresh_index_counters + .get_mut(variable) + .unwrap(); + let index = *counter; + *counter += 1; + self.deref_field_indices + .insert(deref_variable.clone(), index); + self.derefs.push(deref.clone()); + index + } + } + + fn into_deref_fields(self) -> Vec<(vir_mid::VariableDecl, usize)> { + let mut deref_fields: Vec<_> = self.deref_field_indices.into_iter().collect(); + deref_fields.sort_by_key(|(_, index)| *index); + deref_fields + } + + fn into_derefs(self) -> Vec { + self.derefs + } +} + +impl<'l, 'p, 'v, 'tcx> vir_mid::visitors::ExpressionFolder + for FootprintComputation<'l, 'p, 'v, 'tcx> +{ + fn fold_acc_predicate_enum( + &mut self, + acc_predicate: vir_mid::AccPredicate, + ) -> vir_mid::Expression { + match *acc_predicate.predicate { + vir_mid::Predicate::LifetimeToken(_) => { + unimplemented!() + } + vir_mid::Predicate::MemoryBlockStack(_) + | vir_mid::Predicate::MemoryBlockStackDrop(_) + | vir_mid::Predicate::MemoryBlockHeap(_) + | vir_mid::Predicate::MemoryBlockHeapRange(_) + | vir_mid::Predicate::MemoryBlockHeapDrop(_) => true.into(), + vir_mid::Predicate::OwnedNonAliased(predicate) => { + let position = predicate.place.position(); + let place = self.fold_expression(predicate.place); + vir_mid::Expression::builtin_func_app( + vir_mid::BuiltinFunc::IsValid, + Vec::new(), + vec![place], + vir_mid::Type::Bool, + position, + ) + // match predicate.place { + // vir_mid::Expression::Deref(deref) => { + // let deref_field = self.create_deref_field(&deref); + // let app = vir_mid::Expression::builtin_func_app( + // vir_mid::BuiltinFunc::IsValid, + // Vec::new(), + // vec![deref_field], + // vir_mid::Type::Bool, + // deref.position, + // ); + // app + // }} + // _ => unimplemented!(), + } + vir_mid::Predicate::OwnedRange(predicate) => { + unimplemented!("predicate: {}", predicate); + } + vir_mid::Predicate::OwnedSet(predicate) => { + unimplemented!("predicate: {}", predicate); + } + } + } + + fn fold_deref_enum(&mut self, deref: vir_mid::Deref) -> vir_mid::Expression { + if deref.base.get_type().is_pointer() { + self.create_deref_field(&deref) + } else { + vir_mid::Expression::Deref(self.fold_deref(deref)) + } + } +} + +struct Predicate<'l, 'p, 'v, 'tcx> { + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, +} + +impl<'l, 'p, 'v, 'tcx> Predicate<'l, 'p, 'v, 'tcx> { + fn new(lowerer: &'l mut Lowerer<'p, 'v, 'tcx>) -> Self { + Self { lowerer } + } + + // FIXME: Code duplication. + fn extract_base_variable<'a>( + &self, + place: &'a vir_mid::Expression, + ) -> &'a vir_mid::VariableDecl { + match place { + vir_mid::Expression::Local(expression) => &expression.variable, + _ => unimplemented!(), + } + } +} + +impl<'l, 'p, 'v, 'tcx> vir_mid::visitors::ExpressionFolder for Predicate<'l, 'p, 'v, 'tcx> { + // fn fold_field_enum(&mut self, field: vir_mid::Field) -> vir_mid::Expression { + // match &*field.base { + // vir_mid::Expression::Local(local) => { + // assert!(local.variable.is_self_variable()); + // let position = field.position; + // let app = vir_mid::Expression::builtin_func_app( + // vir_mid::BuiltinFunc::GetSnapshot, + // Vec::new(), + // vec![deref_field], + // vir_mid::Type::Bool, + // position, + // ); + // app + // } + // _ => vir_mid::visitors::default_fold_field(self, field), + // } + // } + // fn fold_acc_predicate_enum( + // &mut self, + // acc_predicate: vir_mid::AccPredicate, + // ) -> vir_mid::Expression { + // match *acc_predicate.predicate { + // vir_mid::Predicate::LifetimeToken(_) => { + // unimplemented!() + // } + // vir_mid::Predicate::MemoryBlockStack(_) + // | vir_mid::Predicate::MemoryBlockStackDrop(_) + // | vir_mid::Predicate::MemoryBlockHeap(_) + // | vir_mid::Predicate::MemoryBlockHeapDrop(_) => true.into(), + // vir_mid::Predicate::OwnedNonAliased(predicate) => match predicate.place { + // vir_mid::Expression::Deref(deref) => { + // let deref_field = self.create_deref_field(&deref); + // let app = vir_mid::Expression::builtin_func_app( + // vir_mid::BuiltinFunc::IsValid, + // Vec::new(), + // vec![deref_field], + // vir_mid::Type::Bool, + // deref.position, + // ); + // app + // } + // _ => unimplemented!(), + // }, + // } + // } +} + +fn compute_parameter_name( + place: &vir_mid::Expression, + parameter_name: &mut String, +) -> SpannedEncodingResult<()> { + match place { + vir_mid::Expression::Deref(expression) => { + compute_parameter_name(&expression.base, parameter_name)?; + parameter_name.push_str("$deref"); + } + vir_mid::Expression::Field(expression) => { + compute_parameter_name(&expression.base, parameter_name)?; + parameter_name.push('$'); + parameter_name.push_str(&expression.field.name); + } + vir_mid::Expression::Local(expression) => { + assert!(expression.variable.is_self_variable()); + } + _ => { + unimplemented!("{place}"); + } + } + Ok(()) +} + +impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { + /// Computes the parameter that corresponds to the value of + /// the dereferenced place. + fn compute_deref_field_from_place( + &mut self, + place: &vir_mid::Expression, + ) -> SpannedEncodingResult<(String, vir_low::Type)> { + let mut parameter_name = String::new(); + compute_parameter_name(place, &mut parameter_name)?; + Ok((parameter_name, place.get_type().to_snapshot(self)?)) + } + + /// Computes the parameter that corresponds to the value of + /// the dereferenced place. + fn compute_deref_field_from_range_address( + &mut self, + address: &vir_mid::Expression, + ) -> SpannedEncodingResult<(String, vir_low::Type)> { + let mut parameter_name = String::new(); + compute_parameter_name(address, &mut parameter_name)?; + let vir_mid::Type::Pointer(ty) = address.get_type() else { + unreachable!(); + }; + let element_type = ty.target_type.to_snapshot(self)?; + Ok((parameter_name, vir_low::Type::seq(element_type))) + } +} + +pub(in super::super) struct DerefOwned { + pub(in super::super) place: vir_mid::Expression, + pub(in super::super) field_name: String, + pub(in super::super) field_type: vir_low::Type, +} + +pub(in super::super) struct DerefOwnedRange { + pub(in super::super) address: vir_mid::Expression, + pub(in super::super) field_name: String, + pub(in super::super) field_type: vir_low::Type, +} + +pub(in super::super) type DerefFields = (Vec, Vec); + +pub(in super::super) trait FootprintInterface { + fn structural_invariant_to_deref_fields( + &mut self, + invariant: &[vir_mid::Expression], + ) -> SpannedEncodingResult; +} + +impl<'p, 'v: 'p, 'tcx: 'v> FootprintInterface for Lowerer<'p, 'v, 'tcx> { + /// For the given invariant, compute the deref fields. This is done by + /// finding all `own` predicates and creating variables for them. + /// + /// The order of the returned fields is guaranteed to be the same for the + /// same invariant. + fn structural_invariant_to_deref_fields( + &mut self, + invariant: &[vir_mid::Expression], + ) -> SpannedEncodingResult { + let mut owned_places = BTreeSet::default(); + let mut owned_range_addresses = BTreeSet::default(); + for expression in invariant { + let (new_owned_places, new_owned_range_addresses) = expression.collect_owned_places(); + owned_places.extend(new_owned_places); + owned_range_addresses.extend(new_owned_range_addresses); + } + let mut owned_fields = Vec::new(); + for owned_place in owned_places { + let (name, ty) = self.compute_deref_field_from_place(&owned_place)?; + owned_fields.push(DerefOwned { + place: owned_place, + field_name: name, + field_type: ty, + }); + } + let mut owned_range_fields = Vec::new(); + for owned_range_address in owned_range_addresses { + let (name, ty) = self.compute_deref_field_from_range_address(&owned_range_address)?; + owned_range_fields.push(DerefOwnedRange { + address: owned_range_address, + field_name: name, + field_type: ty, + }); + } + Ok((owned_fields, owned_range_fields)) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/footprint/mod.rs b/prusti-viper/src/encoder/middle/core_proof/footprint/mod.rs new file mode 100644 index 00000000000..19b038bcf5c --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/footprint/mod.rs @@ -0,0 +1,3 @@ +mod interface; + +pub(super) use self::interface::{DerefFields, DerefOwned, DerefOwnedRange, FootprintInterface}; diff --git a/prusti-viper/src/encoder/middle/core_proof/heap/interface.rs b/prusti-viper/src/encoder/middle/core_proof/heap/interface.rs new file mode 100644 index 00000000000..b24b43dbe04 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/heap/interface.rs @@ -0,0 +1,164 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::lowerer::{DomainsLowererInterface, Lowerer}, +}; +use vir_crate::{common::expression::QuantifierHelpers, low as vir_low}; + +const HEAP_DOMAIN_NAME: &str = "Heap$"; +const HEAP_LOOKUP_FUNCTION_NAME: &str = "heap$lookup"; +const HEAP_UPDATE_FUNCTION_NAME: &str = "heap$update"; +const HEAP_CHUNK_TYPE_NAME: &str = "HeapChunk$"; + +pub(in super::super) trait Private { + fn encode_heap_axioms(&mut self) -> SpannedEncodingResult<()>; +} + +impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { + fn encode_heap_axioms(&mut self) -> SpannedEncodingResult<()> { + if !self.heap_state.is_heap_encoded { + self.heap_state.is_heap_encoded = true; + + let position = vir_low::Position::default(); + use vir_low::macros::*; + let heap_type = self.heap_type()?; + let heap_chunk_type = self.heap_chunk_type()?; + var_decls!( + heap: {heap_type.clone()}, + address: Address, + chunk: {heap_chunk_type.clone()} + ); + let update_call = self.create_domain_func_app( + HEAP_DOMAIN_NAME, + HEAP_UPDATE_FUNCTION_NAME, + vec![ + heap.clone().into(), + address.clone().into(), + chunk.clone().into(), + ], + heap_type, + position, + )?; + { + let lookup_call = self.create_domain_func_app( + HEAP_DOMAIN_NAME, + HEAP_LOOKUP_FUNCTION_NAME, + vec![update_call.clone(), address.clone().into()], + heap_chunk_type.clone(), + position, + )?; + + // forall heap: Heap$, addr: Address, chunk: HeapChunk$ :: + // { heap$lookup(heap$update(heap, addr, chunk), addr) } + // heap$lookup(heap$update(heap, addr, chunk), addr) == chunk + let axiom_update_value = vir_low::DomainAxiomDecl { + comment: None, + name: "heap$update_value$axiom".to_string(), + body: QuantifierHelpers::forall( + vec![heap.clone(), address.clone(), chunk.clone()], + vec![vir_low::Trigger::new(vec![lookup_call.clone()])], + expr! { + [lookup_call] == chunk + }, + ), + }; + self.declare_axiom(HEAP_DOMAIN_NAME, axiom_update_value)?; + } + { + var_decls!(address2: Address); + let lookup_call_original = self.create_domain_func_app( + HEAP_DOMAIN_NAME, + HEAP_LOOKUP_FUNCTION_NAME, + vec![heap.clone().into(), address2.clone().into()], + heap_chunk_type.clone(), + position, + )?; + let lookup_call_updated = self.create_domain_func_app( + HEAP_DOMAIN_NAME, + HEAP_LOOKUP_FUNCTION_NAME, + vec![update_call, address2.clone().into()], + heap_chunk_type, + position, + )?; + // forall heap: Heap$, addr1: Address, addr2: Address, chunk: HeapChunk$ :: + // { heap$lookup(heap$update(heap, addr1, chunk), addr2) } + // addr1 != addr2 ==> + // heap$lookup(heap$update(heap, addr1, chunk), addr2) == heap$lookup(heap, addr2) + let axiom_preserve_value = vir_low::DomainAxiomDecl { + name: "heap$update_preserve_value$axiom".to_string(), + comment: None, + body: QuantifierHelpers::forall( + vec![heap, address.clone(), address2.clone(), chunk], + vec![vir_low::Trigger::new(vec![lookup_call_updated.clone()])], + expr! { + (address != address2) ==> + ([lookup_call_updated] == [lookup_call_original]) + }, + ), + }; + self.declare_axiom(HEAP_DOMAIN_NAME, axiom_preserve_value)?; + } + } + Ok(()) + } +} + +pub(in super::super) trait HeapInterface { + fn heap_lookup( + &mut self, + heap: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn heap_update( + &mut self, + heap: vir_low::Expression, + address: vir_low::Expression, + value: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn heap_chunk_type(&mut self) -> SpannedEncodingResult; + fn heap_type(&mut self) -> SpannedEncodingResult; +} + +impl<'p, 'v: 'p, 'tcx: 'v> HeapInterface for Lowerer<'p, 'v, 'tcx> { + fn heap_lookup( + &mut self, + _heap: vir_low::Expression, + _address: vir_low::Expression, + _position: vir_low::Position, + ) -> SpannedEncodingResult { + unimplemented!("outdated-code"); + // self.encode_heap_axioms()?; + // let return_type = self.heap_chunk_type()?; + // self.create_domain_func_app( + // HEAP_DOMAIN_NAME, + // HEAP_LOOKUP_FUNCTION_NAME, + // vec![heap, address], + // return_type, + // position, + // ) + } + fn heap_update( + &mut self, + heap: vir_low::Expression, + address: vir_low::Expression, + value: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + self.encode_heap_axioms()?; + let return_type = self.heap_type()?; + self.create_domain_func_app( + HEAP_DOMAIN_NAME, + HEAP_UPDATE_FUNCTION_NAME, + vec![heap, address, value], + return_type, + position, + ) + } + fn heap_chunk_type(&mut self) -> SpannedEncodingResult { + self.domain_type(HEAP_CHUNK_TYPE_NAME) + } + fn heap_type(&mut self) -> SpannedEncodingResult { + self.domain_type(HEAP_DOMAIN_NAME) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/heap/mod.rs b/prusti-viper/src/encoder/middle/core_proof/heap/mod.rs new file mode 100644 index 00000000000..0548285daf0 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/heap/mod.rs @@ -0,0 +1,4 @@ +mod interface; +mod state; + +pub(super) use self::{interface::HeapInterface, state::HeapState}; diff --git a/prusti-viper/src/encoder/middle/core_proof/heap/state.rs b/prusti-viper/src/encoder/middle/core_proof/heap/state.rs new file mode 100644 index 00000000000..2faba742830 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/heap/state.rs @@ -0,0 +1,4 @@ +#[derive(Default)] +pub(in super::super) struct HeapState { + pub(super) is_heap_encoded: bool, +} diff --git a/prusti-viper/src/encoder/middle/core_proof/interface.rs b/prusti-viper/src/encoder/middle/core_proof/interface.rs index 1821c52762d..60bc2fa2b4a 100644 --- a/prusti-viper/src/encoder/middle/core_proof/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/interface.rs @@ -4,6 +4,7 @@ use crate::encoder::{ }; use log::debug; use prusti_common::config; +use prusti_interface::data::ProcedureDefId; use prusti_rustc_interface::{hir::def_id::DefId, middle::ty}; use vir_crate::{ common::{check_mode::CheckMode, identifier::WithIdentifier}, @@ -12,7 +13,7 @@ use vir_crate::{ #[derive(Default)] pub(crate) struct MidCoreProofEncoderState { - encoded_programs: Vec, + encoded_programs: Vec<(Option, vir_low::Program)>, } pub(crate) trait MidCoreProofEncoderInterface<'tcx> { @@ -26,7 +27,7 @@ pub(crate) trait MidCoreProofEncoderInterface<'tcx> { ty: ty::Ty<'tcx>, check_mode: CheckMode, ) -> SpannedEncodingResult<()>; - fn take_core_proof_programs(&mut self) -> Vec; + fn take_core_proof_programs(&mut self) -> Vec<(Option, vir_low::Program)>; } impl<'v, 'tcx: 'v> MidCoreProofEncoderInterface<'tcx> for super::super::super::Encoder<'v, 'tcx> { @@ -42,29 +43,94 @@ impl<'v, 'tcx: 'v> MidCoreProofEncoderInterface<'tcx> for super::super::super::E ); return Ok(()); } - let procedure = self.encode_procedure_core_proof(proc_def_id, check_mode)?; - let super::lowerer::LoweringResult { - procedures, - domains, - functions, - predicates, - methods, - } = super::lowerer::lower_procedure(self, proc_def_id, procedure)?; - let mut program = vir_low::Program { - name: self.env().name.get_absolute_item_name(proc_def_id), - check_mode, - procedures, - domains, - predicates, - functions, - methods, - }; - if config::inline_caller_for() { - super::transformations::inline_functions::inline_caller_for(&mut program); + for procedure in self.encode_procedure_core_proof(proc_def_id, check_mode)? { + let name = procedure.name.clone(); + let super::lowerer::LoweringResult { + procedures, + domains, + functions, + predicates, + methods, + domains_info: _, + predicates_info, + snapshot_domains_info, + } = super::lowerer::lower_procedure(self, proc_def_id, procedure)?; + let mut program = vir_low::Program { + name, + // name: self.env().name.get_absolute_item_name(proc_def_id), + check_mode, + procedures, + domains, + predicates, + functions, + methods, + }; + let source_filename = self.env().name.source_file_name(); + if config::trace_with_symbolic_execution() || config::custom_heap_encoding() { + program = super::transformations::desugar_method_calls::desugar_method_calls( + &source_filename, + program, + ); + program = super::transformations::desugar_fold_unfold::desugar_fold_unfold( + &source_filename, + program, + &predicates_info.owned_predicates_info, + ); + program = super::transformations::desugar_implications::desugar_implications( + &source_filename, + program, + ); + program = super::transformations::desugar_conditionals::desugar_conditionals( + &source_filename, + program, + ); + } + if config::inline_caller_for() + || config::trace_with_symbolic_execution() + || config::custom_heap_encoding() + { + super::transformations::inline_functions::inline_caller_for( + &source_filename, + &mut program, + ); + } + if config::expand_quantifiers() { + program = super::transformations::expand_quantifiers::expand_quantifiers( + &source_filename, + program, + ); + } + if config::trace_with_symbolic_execution() { + program = + super::transformations::symbolic_execution::purify_with_symbolic_execution( + &source_filename, + program, + predicates_info.non_aliased_memory_block_addresses.clone(), + &snapshot_domains_info, + predicates_info.owned_predicates_info.clone(), + 1, + )?; + // program = + // super::transformations::symbolic_execution::purify_with_symbolic_execution( + // &source_filename, + // program, + // predicates_info.non_aliased_memory_block_addresses, + // &snapshot_domains_info, + // predicates_info.owned_predicates_info.clone(), + // 2, + // )?; + } + if config::custom_heap_encoding() { + super::transformations::custom_heap_encoding::custom_heap_encoding( + self, + &mut program, + predicates_info.owned_predicates_info, + )?; + } + self.mid_core_proof_encoder_state + .encoded_programs + .push((Some(proc_def_id), program)); } - self.mid_core_proof_encoder_state - .encoded_programs - .push(program); Ok(()) } @@ -99,6 +165,9 @@ impl<'v, 'tcx: 'v> MidCoreProofEncoderInterface<'tcx> for super::super::super::E functions, predicates, methods, + domains_info: _, + snapshot_domains_info: _, + predicates_info: _, } = super::lowerer::lower_type(self, def_id, ty, check_copy)?; assert!(procedures.is_empty()); let mut program = vir_low::Program { @@ -111,15 +180,19 @@ impl<'v, 'tcx: 'v> MidCoreProofEncoderInterface<'tcx> for super::super::super::E methods, }; if config::inline_caller_for() { - super::transformations::inline_functions::inline_caller_for(&mut program); + let source_filename = self.env().name.source_file_name(); + super::transformations::inline_functions::inline_caller_for( + &source_filename, + &mut program, + ); } self.mid_core_proof_encoder_state .encoded_programs - .push(program); + .push((None, program)); Ok(()) } - fn take_core_proof_programs(&mut self) -> Vec { + fn take_core_proof_programs(&mut self) -> Vec<(Option, vir_low::Program)> { std::mem::take(&mut self.mid_core_proof_encoder_state.encoded_programs) } } diff --git a/prusti-viper/src/encoder/middle/core_proof/into_low/cfg.rs b/prusti-viper/src/encoder/middle/core_proof/into_low/cfg.rs index e413408a0be..e8a18443ccb 100644 --- a/prusti-viper/src/encoder/middle/core_proof/into_low/cfg.rs +++ b/prusti-viper/src/encoder/middle/core_proof/into_low/cfg.rs @@ -6,18 +6,27 @@ use crate::encoder::{ addresses::AddressesInterface, block_markers::BlockMarkersInterface, builtin_methods::{BuiltinMethodCallsInterface, BuiltinMethodsInterface, CallContext}, + const_generics::ConstGenericsInterface, + labels::LabelsInterface, lifetimes::LifetimesInterface, lowerer::{Lowerer, VariablesLowererInterface}, places::PlacesInterface, - predicates::{PredicatesMemoryBlockInterface, PredicatesOwnedInterface}, + pointers::PointersInterface, + predicates::{ + PredicatesAliasingInterface, PredicatesMemoryBlockInterface, PredicatesOwnedInterface, + }, references::ReferencesInterface, snapshots::{ - IntoProcedureBoolExpression, IntoProcedureFinalSnapshot, IntoProcedureSnapshot, - SnapshotValidityInterface, SnapshotValuesInterface, SnapshotVariablesInterface, + IntoProcedureAssertion, IntoProcedureBoolExpression, IntoProcedureFinalSnapshot, + IntoProcedureSnapshot, IntoSnapshotLowerer, PlaceToSnapshot, PredicateKind, + SelfFramingAssertionToSnapshot, SnapshotValidityInterface, SnapshotValuesInterface, + SnapshotVariablesInterface, }, + viewshifts::ViewShiftsInterface, }, }; use vir_crate::{ + common::{expression::BinaryOperationHelpers, identifier::WithIdentifier}, low::{self as vir_low}, middle::{self as vir_mid, operations::ty::Typed}, }; @@ -43,22 +52,44 @@ impl IntoLow for vir_mid::Statement { use vir_low::{macros::*, Statement}; match self { Self::Comment(statement) => Ok(vec![Statement::comment(statement.comment)]), - Self::OldLabel(label) => { - lowerer.save_old_label(label.name)?; - Ok(Vec::new()) + Self::OldLabel(statement) => { + lowerer.save_old_label(statement.name.clone())?; + lowerer.save_custom_label(statement.name.clone())?; + Ok(vec![vir_low::Statement::label( + statement.name, + statement.position, + )]) } - Self::Inhale(statement) => { - if let vir_mid::Predicate::OwnedNonAliased(owned) = &statement.predicate { - lowerer.mark_owned_non_aliased_as_unfolded(owned.place.get_type())?; + Self::InhalePredicate(statement) => { + let mut statements = Vec::new(); + match &statement.predicate { + vir_mid::Predicate::OwnedNonAliased(predicate) => { + lowerer.mark_owned_predicate_as_unfolded(predicate.place.get_type())?; + } + // vir_mid::Predicate::MemoryBlockStack(predicate) => { + // // let predicate_acc = predicate + // // .clone() + // // .into_low(lowerer)? + // // .unwrap_predicate_access_predicate(); + // lowerer.mark_place_as_used_in_memory_block(&predicate.place)?; + // } + _ => (), } - Ok(vec![Statement::inhale( + statements.push(Statement::inhale( statement.predicate.into_low(lowerer)?, statement.position, - )]) + )); + Ok(statements) } - Self::Exhale(statement) => { - if let vir_mid::Predicate::OwnedNonAliased(owned) = &statement.predicate { - lowerer.mark_owned_non_aliased_as_unfolded(owned.place.get_type())?; + Self::ExhalePredicate(statement) => { + match &statement.predicate { + vir_mid::Predicate::OwnedNonAliased(owned) => { + lowerer.mark_owned_predicate_as_unfolded(owned.place.get_type())?; + } + // vir_mid::Predicate::MemoryBlockStack(predicate) => { + // lowerer.mark_place_as_used_in_memory_block(&predicate.place)?; + // } + _ => (), } Ok(vec![Statement::exhale( statement.predicate.into_low(lowerer)?, @@ -92,13 +123,57 @@ impl IntoLow for vir_mid::Statement { )?; Ok(statements) } - Self::Assume(statement) => Ok(vec![Statement::assume( - statement.expression.to_procedure_bool_expression(lowerer)?, - statement.position, - )]), + Self::HeapHavoc(statement) => { + let new_version = lowerer.new_heap_variable_version(statement.position)?; + Ok(vec![vir_low::Statement::comment(format!( + "new heap version: {new_version}" + ))]) + } + Self::InhaleExpression(statement) => { + let mut assertion_encoder = + SelfFramingAssertionToSnapshot::for_inhale_exhale_expression(statement.label); + let assertion = assertion_encoder.expression_to_snapshot( + lowerer, + &statement.expression, + true, + )?; + Ok(vec![Statement::inhale( + assertion, + // statement.expression.to_procedure_assertion(lowerer)?, + statement.position, + )]) + } + Self::ExhaleExpression(statement) => { + let mut assertion_encoder = + SelfFramingAssertionToSnapshot::for_inhale_exhale_expression(statement.label); + let assertion = assertion_encoder.expression_to_snapshot( + lowerer, + &statement.expression, + true, + )?; + // let assertion = statement.expression.to_procedure_assertion(lowerer)?; + let exhale = Statement::exhale(assertion, statement.position); + Ok(vec![exhale]) + } + Self::Assume(statement) => { + assert!( + statement.expression.is_pure(), + "must be pure: {}", + statement.expression + ); + Ok(vec![Statement::assume( + statement.expression.to_procedure_assertion(lowerer)?, + statement.position, + )]) + } Self::Assert(statement) => { + assert!( + statement.expression.is_pure(), + "must be pure: {}", + statement.expression + ); let assert = Statement::assert( - statement.expression.to_procedure_bool_expression(lowerer)?, + statement.expression.to_procedure_assertion(lowerer)?, statement.position, ); let low_statement = if let Some(condition) = statement.condition { @@ -119,19 +194,30 @@ impl IntoLow for vir_mid::Statement { // argument. Self::FoldOwned(statement) => { let ty = statement.place.get_type(); - lowerer.mark_owned_non_aliased_as_unfolded(ty)?; + lowerer.mark_owned_predicate_as_unfolded(ty)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let root_address = lowerer.extract_root_address(&statement.place)?; - let snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + let permission_amount = if let Some(permission) = statement.permission { + permission.to_procedure_snapshot(lowerer)?.into() + } else { + vir_low::Expression::full_permission() + }; + // let root_address = lowerer.extract_root_address(&statement.place)?; + // let mut place_encoder = + // PlaceToSnapshot::for_place(PredicateKind::Owned); + // let snapshot = + // place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; + // let snapshot = statement.place.to_procedure_snapshot(lowerer)?; let predicate = lowerer.owned_non_aliased( CallContext::Procedure, ty, ty, place, - root_address, - snapshot, - None, + address, + Some(permission_amount), + statement.position, )?; + assert!(predicate.is_predicate_access_predicate()); let mut low_statement = vir_low::Statement::fold_no_pos(predicate); if let Some(condition) = statement.condition { let low_condition = lowerer.lower_block_marker_condition(condition)?; @@ -145,19 +231,30 @@ impl IntoLow for vir_mid::Statement { } Self::UnfoldOwned(statement) => { let ty = statement.place.get_type(); - lowerer.mark_owned_non_aliased_as_unfolded(ty)?; + lowerer.mark_owned_predicate_as_unfolded(ty)?; + // let root_address = lowerer.extract_root_address(&statement.place)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let root_address = lowerer.extract_root_address(&statement.place)?; - let snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + let permission_amount = if let Some(permission) = statement.permission { + permission.to_procedure_snapshot(lowerer)?.into() + } else { + vir_low::Expression::full_permission() + }; + // let mut place_encoder = + // PlaceToSnapshot::for_place(PredicateKind::Owned); + // let snapshot = + // place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; + // let snapshot = statement.place.to_procedure_snapshot(lowerer)?; let predicate = lowerer.owned_non_aliased( CallContext::Procedure, ty, ty, place, - root_address, - snapshot, - None, + address, + Some(permission_amount), + statement.position, )?; + assert!(predicate.is_predicate_access_predicate()); let mut low_statement = vir_low::Statement::unfold_no_pos(predicate); if let Some(condition) = statement.condition { let low_condition = lowerer.lower_block_marker_condition(condition)?; @@ -171,33 +268,57 @@ impl IntoLow for vir_mid::Statement { } Self::FoldRef(statement) => { let ty = statement.place.get_type(); - lowerer.mark_owned_non_aliased_as_unfolded(ty)?; + lowerer.mark_owned_predicate_as_unfolded(ty)?; let lifetime = lowerer.encode_lifetime_const_into_procedure_variable(statement.lifetime)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let root_address = lowerer.extract_root_address(&statement.place)?; - let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + // let root_address = lowerer.extract_root_address(&statement.place)?; + // let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; let predicate = if statement.uniqueness.is_shared() { + // let mut place_encoder = + // PlaceToSnapshot::for_place(PredicateKind::FracRef { + // lifetime: lifetime.clone().into(), + // }); + // let current_snapshot = + // place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; lowerer.frac_ref( CallContext::Procedure, ty, ty, place, - root_address, - current_snapshot, + address, lifetime.into(), + None, + None, + statement.position, )? } else { - let final_snapshot = statement.place.to_procedure_final_snapshot(lowerer)?; + // let mut place_encoder = + // PlaceToSnapshot::for_place(PredicateKind::UniqueRef { + // lifetime: lifetime.clone().into(), + // is_final: false, + // }); + // let current_snapshot = + // place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; + // let mut place_encoder = + // PlaceToSnapshot::for_place(PredicateKind::UniqueRef { + // lifetime: lifetime.clone().into(), + // is_final: false, + // }); + // let final_snapshot = + // place_encoder.expression_to_snapshot(lowerer, &statement.place, true)?; + // let final_snapshot = statement.place.to_procedure_final_snapshot(lowerer)?; lowerer.unique_ref( CallContext::Procedure, ty, ty, place, - root_address, - current_snapshot, - final_snapshot, + address, lifetime.into(), + None, + None, + statement.position, )? }; let mut low_statement = vir_low::Statement::fold_no_pos(predicate); @@ -213,33 +334,57 @@ impl IntoLow for vir_mid::Statement { } Self::UnfoldRef(statement) => { let ty = statement.place.get_type(); - lowerer.mark_owned_non_aliased_as_unfolded(ty)?; + lowerer.mark_owned_predicate_as_unfolded(ty)?; let lifetime = lowerer.encode_lifetime_const_into_procedure_variable(statement.lifetime)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let root_address = lowerer.extract_root_address(&statement.place)?; - let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + // let root_address = lowerer.extract_root_address(&statement.place)?; + // let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; let predicate = if statement.uniqueness.is_shared() { + // let mut place_encoder = + // PlaceToSnapshot::for_place(PredicateKind::FracRef { + // lifetime: lifetime.clone().into(), + // }); + // let current_snapshot = + // place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; lowerer.frac_ref( CallContext::Procedure, ty, ty, place, - root_address, - current_snapshot, + address, lifetime.into(), + None, + None, + statement.position, )? } else { - let final_snapshot = statement.place.to_procedure_final_snapshot(lowerer)?; + // let mut place_encoder = + // PlaceToSnapshot::for_place(PredicateKind::UniqueRef { + // lifetime: lifetime.clone().into(), + // is_final: false, + // }); + // let current_snapshot = + // place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; + // let mut place_encoder = + // PlaceToSnapshot::for_place(PredicateKind::UniqueRef { + // lifetime: lifetime.clone().into(), + // is_final: false, + // }); + // let final_snapshot = + // place_encoder.expression_to_snapshot(lowerer, &statement.place, true)?; + // let final_snapshot = statement.place.to_procedure_final_snapshot(lowerer)?; lowerer.unique_ref( CallContext::Procedure, ty, ty, place, - root_address, - current_snapshot, - final_snapshot, + address, lifetime.into(), + None, + None, + statement.position, )? }; let mut low_statement = vir_low::Statement::unfold_no_pos(predicate); @@ -303,6 +448,26 @@ impl IntoLow for vir_mid::Statement { }; Ok(vec![low_statement]) } + Self::JoinRange(statement) => { + let ty = statement.address.get_type(); + let vir_mid::Type::Pointer(pointer_type) = ty else { + unreachable!() + }; + let target_type = &*pointer_type.target_type; + lowerer.encode_memory_block_range_join_method(target_type)?; + let pointer_value = statement.address.to_procedure_snapshot(lowerer)?; + let start_address = + lowerer.pointer_address(ty, pointer_value, statement.position)?; + let start_index = statement.start_index.to_procedure_snapshot(lowerer)?; + let end_index = statement.end_index.to_procedure_snapshot(lowerer)?; + let low_statement = stmtp! { + statement.position => + call memory_block_range_join( + [start_address], [start_index], [end_index] + ) + }; + Ok(vec![low_statement]) + } Self::SplitBlock(statement) => { let ty = statement.place.get_type(); lowerer.encode_memory_block_split_method(ty)?; @@ -353,12 +518,37 @@ impl IntoLow for vir_mid::Statement { }; Ok(vec![low_statement]) } + Self::SplitRange(statement) => { + let ty = statement.address.get_type(); + let vir_mid::Type::Pointer(pointer_type) = ty else { + unreachable!() + }; + let target_type = &*pointer_type.target_type; + lowerer.encode_memory_block_range_split_method(target_type)?; + let pointer_value = statement.address.to_procedure_snapshot(lowerer)?; + let start_address = + lowerer.pointer_address(ty, pointer_value, statement.position)?; + let start_index = statement.start_index.to_procedure_snapshot(lowerer)?; + let end_index = statement.end_index.to_procedure_snapshot(lowerer)?; + let low_statement = stmtp! { + statement.position => + call memory_block_range_split( + [start_address], [start_index], [end_index] + ) + }; + Ok(vec![low_statement]) + } Self::ConvertOwnedIntoMemoryBlock(statement) => { let ty = statement.place.get_type(); lowerer.encode_into_memory_block_method(ty)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let root_address = lowerer.extract_root_address(&statement.place)?; - let snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + // let root_address = lowerer.extract_root_address(&statement.place)?; + // let snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::Owned); + // SelfFramingAssertionToSnapshot::for_place_expression(); + let snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; let low_condition = statement .condition .map(|condition| lowerer.lower_block_marker_condition(condition)) @@ -370,7 +560,7 @@ impl IntoLow for vir_mid::Statement { statement.position, low_condition, place, - root_address, + address, snapshot, )?; Ok(vec![low_statement.set_default_position(statement.position)]) @@ -379,75 +569,127 @@ impl IntoLow for vir_mid::Statement { let ty = statement.place.get_type(); lowerer.encode_into_memory_block_method(ty)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let root_address = lowerer.extract_root_address(&statement.place)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + // let root_address = lowerer.extract_root_address(&statement.place)?; let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; let lifetime = lowerer.encode_lifetime_const_into_procedure_variable(statement.lifetime)?; - let validity = - lowerer.encode_snapshot_valid_call_for_type(current_snapshot.clone(), ty)?; - let restored_predicate = if let Some((deref_lifetime, uniqueness)) = - statement.place.get_dereference_kind() - { - let deref_lifetime = lowerer - .encode_lifetime_const_into_procedure_variable(deref_lifetime)? - .into(); - if uniqueness.is_unique() { - let final_snapshot = - statement.place.to_procedure_final_snapshot(lowerer)?; - lowerer.unique_ref( - CallContext::Procedure, - ty, - ty, - place, - root_address, - current_snapshot, - final_snapshot, - deref_lifetime, - )? - } else { - lowerer.frac_ref( - CallContext::Procedure, - ty, - ty, - place, - root_address, - current_snapshot, - deref_lifetime, - )? - } - } else { - lowerer.owned_non_aliased( - CallContext::Procedure, - ty, - ty, - place, - root_address, - current_snapshot, - None, - )? - }; - let low_statement = if let Some(condition) = statement.condition { - let low_condition = lowerer.lower_block_marker_condition(condition)?; - stmtp! { - statement.position => - apply (acc(DeadLifetimeToken(lifetime))) --* ( - [restored_predicate] && - [validity] && - (acc(DeadLifetimeToken(lifetime))) - ) - } + // let validity = + // lowerer.encode_snapshot_valid_call_for_type(current_snapshot.clone(), ty)?; + // let restored_predicate = if let Some((deref_lifetime, uniqueness)) = + // statement.place.get_dereference_kind() + // { + // let deref_lifetime = lowerer + // .encode_lifetime_const_into_procedure_variable(deref_lifetime)? + // .into(); + // if uniqueness.is_unique() { + // let final_snapshot = + // statement.place.to_procedure_final_snapshot(lowerer)?; + // lowerer.unique_ref_with_current_snapshot( + // CallContext::Procedure, + // ty, + // ty, + // place.clone(), + // address.clone(), + // current_snapshot.clone(), + // deref_lifetime, + // None, // FIXME + // None, + // statement.position, + // )? + // } else { + // lowerer.frac_ref_with_current_snapshot( + // CallContext::Procedure, + // ty, + // ty, + // place.clone(), + // address.clone(), + // current_snapshot.clone(), + // deref_lifetime, + // None, // FIXME + // None, + // statement.position, + // )? + // } + // } else { + // lowerer.owned_non_aliased_with_snapshot( + // CallContext::Procedure, + // ty, + // ty, + // place.clone(), + // address.clone(), + // current_snapshot.clone(), + // None, + // statement.position, + // )? + // }; + let low_condition = if let Some(condition) = statement.condition { + Some(lowerer.lower_block_marker_condition(condition)?) + // stmtp! { + // statement.position => + // apply (acc(DeadLifetimeToken(lifetime))) --* ( + // [restored_predicate] && + // [validity] && + // (acc(DeadLifetimeToken(lifetime))) + // ) + // } } else { - stmtp! { - statement.position => - apply (acc(DeadLifetimeToken(lifetime))) --* ( - [restored_predicate] && - [validity] && - (acc(DeadLifetimeToken(lifetime))) - ) - } + // stmtp! { + // statement.position => + // apply (acc(DeadLifetimeToken(lifetime))) --* ( + // [restored_predicate] && + // [validity] && + // (acc(DeadLifetimeToken(lifetime))) + // ) + // } + None }; + let mut arguments = vec![lifetime.into(), place, address, current_snapshot]; + arguments.extend(lowerer.create_lifetime_arguments(CallContext::Procedure, ty)?); + arguments.extend(lowerer.create_const_arguments(CallContext::Procedure, ty)?); + let low_statement = lowerer.encode_apply_view_shift( + &format!("end_borrow${}", ty.get_identifier()), + low_condition, + arguments, + statement.position, + )?; Ok(vec![low_statement]) } + Self::RestoreRawBorrowed(statement) => { + let ty = statement.restored_place.get_type(); + lowerer.encode_restore_raw_borrowed_method(ty)?; + let borrowing_place_parent = statement.borrowing_place.get_parent_ref().unwrap(); + let borrowing_snapshot = borrowing_place_parent.to_procedure_snapshot(lowerer)?; + let borrowing_address = lowerer.pointer_address( + borrowing_place_parent.get_type(), + borrowing_snapshot, + statement.position, + )?; + let restored_place = + lowerer.encode_expression_as_place(&statement.restored_place)?; + let restored_address = + lowerer.encode_expression_as_place_address(&statement.restored_place)?; + // let restored_root_address = + // lowerer.extract_root_address(&statement.restored_place)?; + let snapshot = statement.borrowing_place.to_procedure_snapshot(lowerer)?; + let mut statements = vec![lowerer.call_restore_raw_borrowed_method( + CallContext::Procedure, + ty, + ty, + statement.position, + borrowing_address, + restored_place, + restored_address, + snapshot.clone(), + )?]; + lowerer.encode_snapshot_update( + &mut statements, + &statement.restored_place, + snapshot, + statement.position, + )?; + Ok(statements) + } Self::MovePlace(statement) => { // TODO: Remove code duplication with Self::CopyPlace let target_ty = statement.target.get_type(); @@ -457,27 +699,36 @@ impl IntoLow for vir_mid::Statement { assert_eq!(target_ty_without_lifetime, source_ty_without_lifetime); lowerer.encode_move_place_method(target_ty)?; let target_place = lowerer.encode_expression_as_place(&statement.target)?; - let target_root_address = lowerer.extract_root_address(&statement.target)?; + let target_address = + lowerer.encode_expression_as_place_address(&statement.target)?; + // let target_root_address = lowerer.extract_root_address(&statement.target)?; let source_place = lowerer.encode_expression_as_place(&statement.source)?; - let source_root_address = lowerer.extract_root_address(&statement.source)?; - let source_snapshot = statement.source.to_procedure_snapshot(lowerer)?; - let mut statements = vec![lowerer.call_move_place_method( + let source_address = + lowerer.encode_expression_as_place_address(&statement.source)?; + // let source_root_address = lowerer.extract_root_address(&statement.source)?; + // let source_snapshot = statement.source.to_procedure_snapshot(lowerer)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::Owned); + // SelfFramingAssertionToSnapshot::for_place_expression(); + let source_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.source, false)?; + let mut statements = Vec::new(); + lowerer.encode_snapshot_update( + &mut statements, + &statement.target, + source_snapshot.clone(), + statement.position, + )?; + statements.push(lowerer.call_move_place_method( CallContext::Procedure, target_ty, target_ty, statement.position, target_place, - target_root_address, + target_address, source_place, - source_root_address, - source_snapshot.clone(), - )?]; - lowerer.encode_snapshot_update( - &mut statements, - &statement.target, + source_address, source_snapshot, - statement.position, - )?; + )?); Ok(statements) } Self::CopyPlace(statement) => { @@ -487,34 +738,46 @@ impl IntoLow for vir_mid::Statement { assert_eq!(target_ty, source_ty); lowerer.encode_copy_place_method(target_ty)?; let target_place = lowerer.encode_expression_as_place(&statement.target)?; - let target_root_address = lowerer.extract_root_address(&statement.target)?; + // let target_root_address = lowerer.extract_root_address(&statement.target)?; + let target_address = + lowerer.encode_expression_as_place_address(&statement.target)?; let source_place = lowerer.encode_expression_as_place(&statement.source)?; - let source_root_address = lowerer.extract_root_address(&statement.source)?; + // let source_root_address = lowerer.extract_root_address(&statement.source)?; + let source_address = + lowerer.encode_expression_as_place_address(&statement.source)?; let source_permission_amount = if let Some(source_permission) = statement.source_permission { source_permission.to_procedure_snapshot(lowerer)?.into() } else { vir_low::Expression::full_permission() }; - let source_snapshot = statement.source.to_procedure_snapshot(lowerer)?; - let mut statements = vec![lowerer.call_copy_place_method( + // let source_snapshot = statement.source.to_procedure_snapshot(lowerer)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::Owned); + // SelfFramingAssertionToSnapshot::for_place_expression(); + let _source_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.source, false)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::Owned); + let source_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.source, false)?; + let mut statements = Vec::new(); + lowerer.encode_snapshot_update( + &mut statements, + &statement.target, + source_snapshot.clone(), + statement.position, + )?; + statements.push(lowerer.call_copy_place_method( CallContext::Procedure, target_ty, target_ty, statement.position, target_place, - target_root_address, + target_address, source_place, - source_root_address, - source_snapshot.clone(), - source_permission_amount, - )?]; - lowerer.encode_snapshot_update( - &mut statements, - &statement.target, + source_address, source_snapshot, - statement.position, - )?; + source_permission_amount, + )?); Ok(statements) } Self::WritePlace(statement) => { @@ -523,7 +786,9 @@ impl IntoLow for vir_mid::Statement { assert_eq!(target_ty, source_ty); lowerer.encode_write_place_constant_method(target_ty)?; let target_place = lowerer.encode_expression_as_place(&statement.target)?; - let target_root_address = lowerer.extract_root_address(&statement.target)?; + let target_address = + lowerer.encode_expression_as_place_address(&statement.target)?; + // let target_address = lowerer.extract_root_address(&statement.target)?; let source_snapshot = statement.value.to_procedure_snapshot(lowerer)?; let mut statements = vec![lowerer.call_write_place_constant_method( CallContext::Procedure, @@ -531,7 +796,7 @@ impl IntoLow for vir_mid::Statement { target_ty, statement.position, target_place, - target_root_address, + target_address, source_snapshot.clone(), )?]; lowerer.encode_snapshot_update( @@ -574,12 +839,7 @@ impl IntoLow for vir_mid::Statement { let variant_index = variant_place.clone().unwrap_variant().variant_index; let union_place = variant_place.get_parent_ref().unwrap(); let mut statements = Vec::new(); - lowerer.encode_snapshot_havoc( - &mut statements, - union_place, - statement.position, - None, - )?; + lowerer.encode_snapshot_havoc(&mut statements, union_place, statement.position)?; let snapshot = union_place.to_procedure_snapshot(lowerer)?; let discriminant = lowerer.obtain_enum_discriminant( snapshot, @@ -606,6 +866,48 @@ impl IntoLow for vir_mid::Statement { )?; Ok(stmts) } + Self::StashRange(statement) => { + let ty = statement.address.get_type(); + let pointer_value = statement.address.to_procedure_snapshot(lowerer)?; + let start_index = statement.start_index.to_procedure_snapshot(lowerer)?; + let end_index = statement.end_index.to_procedure_snapshot(lowerer)?; + let mut statements = Vec::new(); + lowerer.encode_stash_range_call( + &mut statements, + ty, + pointer_value, + start_index, + end_index, + statement.label, + statement.position, + )?; + Ok(statements) + } + Self::StashRangeRestore(statement) => { + assert_eq!( + statement.old_address.get_type(), + statement.new_address.get_type() + ); + let ty = statement.old_address.get_type(); + let old_pointer_value = statement.old_address.to_procedure_snapshot(lowerer)?; + let old_start_index = statement.old_start_index.to_procedure_snapshot(lowerer)?; + let old_end_index = statement.old_end_index.to_procedure_snapshot(lowerer)?; + let new_address = statement.new_address.to_procedure_snapshot(lowerer)?; + let new_start_index = statement.new_start_index.to_procedure_snapshot(lowerer)?; + let mut statements = Vec::new(); + lowerer.encode_restore_stash_range_call( + &mut statements, + ty, + old_pointer_value, + old_start_index, + old_end_index, + statement.old_label, + new_address, + new_start_index, + statement.position, + )?; + Ok(statements) + } Self::NewLft(statement) => { let targets = vec![vir_low::Expression::local_no_pos( statement.target.to_procedure_snapshot(lowerer)?, @@ -635,29 +937,62 @@ impl IntoLow for vir_mid::Statement { let (lifetime, uniqueness) = statement.target.get_dereference_kind().unwrap(); let lifetime = lowerer.encode_lifetime_const_into_procedure_variable(lifetime)?; let place = lowerer.encode_expression_as_place(&statement.target)?; - let root_address = lowerer.extract_root_address(&statement.target)?; - let current_snapshot = statement.target.to_procedure_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.target)?; + // let root_address = lowerer.extract_root_address(&statement.target)?; + // let current_snapshot = statement.target.to_procedure_snapshot(lowerer)?; // TODO: These should be method calls. let statements = match uniqueness { vir_mid::ty::Uniqueness::Unique => { let final_snapshot = statement.target.to_procedure_final_snapshot(lowerer)?; let predicate = lowerer.unique_ref( + CallContext::Procedure, + ty, + ty, + place.clone(), + address.clone(), + lifetime.clone().into(), + None, // FIXME: This should be a proper value + None, + statement.position, + )?; + let current_snapshot = lowerer.unique_ref_snap( CallContext::Procedure, ty, ty, place, - root_address, - current_snapshot.clone(), - final_snapshot.clone(), + address, lifetime.into(), + None, // FIXME: This should be a proper value + false, + statement.position, )?; + let label = lowerer.fresh_label("dead_reference_label")?; + lowerer.save_old_label(label.clone())?; + let current_snapshot = vir_low::Expression::labelled_old_no_pos( + Some(label.clone()), + current_snapshot, + ); + + // let predicate = lowerer.unique_ref_with_current_snapshot( + // CallContext::Procedure, + // ty, + // ty, + // place, + // address, + // current_snapshot.clone(), + // lifetime.into(), + // None, // FIXME: This should be a proper value + // None, + // statement.position, + // )?; lowerer.mark_unique_ref_as_used(ty)?; let mut statements = vec![ vir_low::Statement::comment(format!( "dead reference: {}", statement.target )), + vir_low::Statement::label(label, statement.position), vir_low::Statement::exhale_no_pos(predicate) .set_default_position(statement.position), stmtp! { @@ -682,9 +1017,11 @@ impl IntoLow for vir_mid::Statement { ty, ty, place, - root_address, - current_snapshot, + address, lifetime.into(), + None, // FIXME: This should be a proper value + None, + statement.position, )?; let low_statement = vir_low::Statement::exhale_no_pos(predicate) .set_default_position(statement.position); @@ -731,10 +1068,16 @@ impl IntoLow for vir_mid::Statement { .unwrap() .to_procedure_snapshot(lowerer)?, ); - let statements = vec![Statement::assign( - lowerer - .new_snapshot_variable_version(&statement.target, statement.position)?, - value, + let statements = vec![Statement::assume( + vir_low::Expression::equals( + lowerer + .new_snapshot_variable_version( + &statement.target, + statement.position, + )? + .into(), + value, + ), statement.position, )]; Ok(statements) @@ -796,21 +1139,26 @@ impl IntoLow for vir_mid::Statement { .lifetime_token_permission .to_procedure_snapshot(lowerer)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let address = lowerer.extract_root_address(&statement.place)?; - let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + // let address = lowerer.extract_root_address(&statement.place)?; let targets = vec![statement .predicate_permission_amount .to_procedure_snapshot(lowerer)? .into()]; + let mut arguments = vec![lifetime.clone().into(), perm_amount, place, address]; + // if lowerer.check_mode.unwrap() == CheckMode::PurificationSoudness { + // let mut assertion_encoder = SelfFramingAssertionToSnapshot::for_place_expression(); + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::FracRef { + lifetime: lifetime.into(), + }); + // let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let current_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.place, true)?; + arguments.push(current_snapshot); + // } Ok(vec![Statement::method_call( method_name!(frac_bor_atomic_acc), - vec![ - lifetime.into(), - perm_amount, - place, - address, - current_snapshot, - ], + arguments, targets, statement.position, )]) @@ -823,38 +1171,57 @@ impl IntoLow for vir_mid::Statement { .lifetime_token_permission .to_procedure_snapshot(lowerer)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let root_address = lowerer.extract_root_address(&statement.place)?; - let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::Owned); + // let mut place_encoder = SelfFramingAssertionToSnapshot::for_place_expression(); + let current_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.place, true)?; + // let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; + // let root_address = lowerer.extract_root_address(&statement.place)?; let tmp_frac_ref_perm = statement .predicate_permission_amount .to_procedure_snapshot(lowerer)?; - let owned_predicate = lowerer.owned_non_aliased( - CallContext::Procedure, - ty, - ty, - place.clone(), - root_address.clone(), - current_snapshot.clone(), - Some(tmp_frac_ref_perm.into()), - )?; - let frac_predicate = lowerer.frac_ref( - CallContext::Procedure, - ty, - ty, - place, - root_address, - current_snapshot, - lifetime.clone().into(), - )?; - Ok(vec![stmtp! { - statement.position => - apply ( - [owned_predicate] - ) --* ( - (acc(LifetimeToken(lifetime), [perm_amount])) && - [frac_predicate] + // let owned_predicate = lowerer.owned_non_aliased( + // CallContext::Procedure, + // ty, + // ty, + // place.clone(), + // address.clone(), + // Some(tmp_frac_ref_perm.into()), + // statement.position, + // )?; + // let frac_predicate = lowerer.frac_ref_with_current_snapshot( + // CallContext::Procedure, + // ty, + // ty, + // place, + // address, + // current_snapshot, + // lifetime.clone().into(), + // None, + // None, + // statement.position, + // )?; + let statements = vec![stmtp! { statement.position => + call close_frac_ref( + lifetime, + [perm_amount], + [place], + [address], + [current_snapshot], + tmp_frac_ref_perm ) - }]) + }]; + Ok(statements) + // Ok(vec![stmtp! { + // statement.position => + // apply ( + // [owned_predicate] + // ) --* ( + // (acc(LifetimeToken(lifetime), [perm_amount])) && + // [frac_predicate] + // ) + // }]) } Self::OpenMutRef(statement) => { let ty = statement.place.get_type(); @@ -865,9 +1232,24 @@ impl IntoLow for vir_mid::Statement { .lifetime_token_permission .to_procedure_snapshot(lowerer)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let address = lowerer.extract_root_address(&statement.place)?; - let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; - let final_snapshot = statement.place.to_procedure_final_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + // let address = lowerer.extract_root_address(&statement.place)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::UniqueRef { + lifetime: lifetime.clone().into(), + is_final: false, + }); + // SelfFramingAssertionToSnapshot::for_place_expression(); + let current_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; + // let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; + // let final_snapshot = statement.place.to_procedure_final_snapshot(lowerer)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::UniqueRef { + lifetime: lifetime.clone().into(), + is_final: true, + }); + // SelfFramingAssertionToSnapshot::for_place_expression(); + let final_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; let statements = vec![stmtp! { statement.position => call open_mut_ref( lifetime, @@ -889,9 +1271,21 @@ impl IntoLow for vir_mid::Statement { .lifetime_token_permission .to_procedure_snapshot(lowerer)?; let place = lowerer.encode_expression_as_place(&statement.place)?; - let address = lowerer.extract_root_address(&statement.place)?; - let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; - let final_snapshot = statement.place.to_procedure_final_snapshot(lowerer)?; + let address = lowerer.encode_expression_as_place_address(&statement.place)?; + // let address = lowerer.extract_root_address(&statement.place)?; + // let current_snapshot = statement.place.to_procedure_snapshot(lowerer)?; + // let final_snapshot = statement.place.to_procedure_final_snapshot(lowerer)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::Owned); + // SelfFramingAssertionToSnapshot::for_place_expression(); + let current_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; + let mut place_encoder = PlaceToSnapshot::for_place(PredicateKind::UniqueRef { + lifetime: lifetime.clone().into(), + is_final: true, + }); + // SelfFramingAssertionToSnapshot::for_place_expression(); + let final_snapshot = + place_encoder.expression_to_snapshot(lowerer, &statement.place, false)?; let statements = vec![stmtp! { statement.position => call close_mut_ref( lifetime, @@ -973,37 +1367,34 @@ impl IntoLow for vir_mid::Predicate { expr! { acc(LifetimeToken([lifetime.into()]), [permission])} .set_default_position(predicate.position) } - Predicate::MemoryBlockStack(predicate) => { - lowerer.encode_memory_block_predicate()?; - let place = lowerer.encode_expression_as_place_address(&predicate.place)?; - let size = predicate.size.to_procedure_snapshot(lowerer)?; - expr! { acc(MemoryBlock([place], [size]))}.set_default_position(predicate.position) - } - Predicate::MemoryBlockStackDrop(predicate) => { - let place = lowerer.encode_expression_as_place_address(&predicate.place)?; - let size = predicate.size.to_procedure_snapshot(lowerer)?; - lowerer.encode_memory_block_stack_drop_acc(place, size, predicate.position)? - } + Predicate::MemoryBlockStack(predicate) => predicate.into_low(lowerer)?, + Predicate::MemoryBlockStackDrop(predicate) => predicate.into_low(lowerer)?, Predicate::MemoryBlockHeap(predicate) => { unimplemented!("predicate: {}", predicate); } + Predicate::MemoryBlockHeapRange(predicate) => { + unimplemented!("predicate: {}", predicate); + } Predicate::MemoryBlockHeapDrop(predicate) => { unimplemented!("predicate: {}", predicate); } Predicate::OwnedNonAliased(predicate) => { + lowerer.mark_place_as_used_in_memory_block(&predicate.place)?; let place = lowerer.encode_expression_as_place(&predicate.place)?; - let root_address = lowerer.extract_root_address(&predicate.place)?; + let address = lowerer.encode_expression_as_place_address(&predicate.place)?; + // let root_address = lowerer.extract_root_address(&predicate.place)?; let snapshot = predicate.place.to_procedure_snapshot(lowerer)?; let ty = predicate.place.get_type(); let valid = lowerer.encode_snapshot_valid_call_for_type(snapshot.clone(), ty)?; - let low_predicate = lowerer.owned_non_aliased( + let low_predicate = lowerer.owned_non_aliased_with_snapshot( CallContext::Procedure, ty, ty, place, - root_address, + address, snapshot, None, + predicate.position, )?; exprp! { predicate.position => @@ -1011,11 +1402,41 @@ impl IntoLow for vir_mid::Predicate { [valid] } } + Predicate::OwnedRange(_) => todo!(), + Predicate::OwnedSet(_) => todo!(), }; Ok(result) } } +impl IntoLow for vir_mid::ast::predicate::MemoryBlockStack { + type Target = vir_low::Expression; + + fn into_low<'p, 'v: 'p, 'tcx: 'v>( + self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult { + lowerer.encode_memory_block_predicate()?; + lowerer.mark_place_as_used_in_memory_block(&self.place)?; + let place = lowerer.encode_expression_as_place_address(&self.place)?; + let size = self.size.to_procedure_snapshot(lowerer)?; + lowerer.encode_memory_block_stack_acc(place, size, self.position) + } +} + +impl IntoLow for vir_mid::ast::predicate::MemoryBlockStackDrop { + type Target = vir_low::Expression; + + fn into_low<'p, 'v: 'p, 'tcx: 'v>( + self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult { + let place = lowerer.encode_expression_as_place_address(&self.place)?; + let size = self.size.to_procedure_snapshot(lowerer)?; + lowerer.encode_memory_block_stack_drop_acc(place, size, self.position) + } +} + impl IntoLow for vir_mid::BasicBlockId { type Target = vir_low::Label; fn into_low<'p, 'v: 'p, 'tcx: 'v>( diff --git a/prusti-viper/src/encoder/middle/core_proof/labels/interface.rs b/prusti-viper/src/encoder/middle/core_proof/labels/interface.rs new file mode 100644 index 00000000000..5a452e928cf --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/labels/interface.rs @@ -0,0 +1,22 @@ +use crate::encoder::{errors::SpannedEncodingResult, middle::core_proof::lowerer::Lowerer}; +use vir_crate::low as vir_low; + +pub(in super::super) trait LabelsInterface { + fn fresh_label(&mut self, prefix: &str) -> SpannedEncodingResult; + fn save_custom_label(&mut self, label: String) -> SpannedEncodingResult<()>; +} + +impl<'p, 'v: 'p, 'tcx: 'v> LabelsInterface for Lowerer<'p, 'v, 'tcx> { + fn fresh_label(&mut self, prefix: &str) -> SpannedEncodingResult { + let label = format!("{}{}", prefix, self.labels_state.counter); + self.save_custom_label(label.clone())?; + self.labels_state.counter += 1; + Ok(label) + } + + fn save_custom_label(&mut self, label: String) -> SpannedEncodingResult<()> { + let label = vir_low::Label::new(label); + assert!(self.labels_state.labels.insert(label)); + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/labels/mod.rs b/prusti-viper/src/encoder/middle/core_proof/labels/mod.rs new file mode 100644 index 00000000000..381f69bf617 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/labels/mod.rs @@ -0,0 +1,4 @@ +mod interface; +mod state; + +pub(super) use self::{interface::LabelsInterface, state::LabelsState}; diff --git a/prusti-viper/src/encoder/middle/core_proof/labels/state.rs b/prusti-viper/src/encoder/middle/core_proof/labels/state.rs new file mode 100644 index 00000000000..fb9247ef7e3 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/labels/state.rs @@ -0,0 +1,14 @@ +use std::collections::BTreeSet; +use vir_crate::low as vir_low; + +#[derive(Default)] +pub(in super::super) struct LabelsState { + pub(super) counter: u64, + pub(super) labels: BTreeSet, +} + +impl LabelsState { + pub(crate) fn destruct(self) -> Vec { + self.labels.into_iter().collect() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/lifetimes/interface.rs b/prusti-viper/src/encoder/middle/core_proof/lifetimes/interface.rs index c4c01f4bf16..70c2d941c58 100644 --- a/prusti-viper/src/encoder/middle/core_proof/lifetimes/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/lifetimes/interface.rs @@ -339,6 +339,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesInterface for Lowerer<'p, 'v, 'tcx> { self.lifetimes_state.is_lifetime_token_encoded = true; let predicate = vir_low::PredicateDecl::new( "LifetimeToken", + vir_low::PredicateKind::LifetimeToken, vec![vir_low::VariableDecl::new( "lifetime", self.lifetime_type()?, @@ -348,6 +349,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesInterface for Lowerer<'p, 'v, 'tcx> { self.declare_predicate(predicate)?; let predicate = vir_low::PredicateDecl::new( "DeadLifetimeToken", + vir_low::PredicateKind::WithoutSnapshotWhole, vec![vir_low::VariableDecl::new( "lifetime", self.lifetime_type()?, @@ -388,11 +390,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesInterface for Lowerer<'p, 'v, 'tcx> { .contains_key(&is_alive_variable) { let variable = self.initial_snapshot_variable_version(&is_alive_variable)?; + let position = self.procedure_position.unwrap(); self.lifetimes_state .lifetime_is_alive_initialization .insert( is_alive_variable.clone(), - vir_low::Statement::assign_no_pos(variable, true.into()), + vir_low::Statement::assume(variable.into(), position), ); } is_alive_variable.to_procedure_snapshot(self) diff --git a/prusti-viper/src/encoder/middle/core_proof/lowerer/domains/interface.rs b/prusti-viper/src/encoder/middle/core_proof/lowerer/domains/interface.rs index dc5f061e9f8..75bf65227f5 100644 --- a/prusti-viper/src/encoder/middle/core_proof/lowerer/domains/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/lowerer/domains/interface.rs @@ -6,36 +6,40 @@ use vir_crate::{ middle as vir_mid, }; +#[derive(Default)] +pub(in super::super::super) struct DomainsInfo {} + #[derive(Default)] pub(in super::super) struct DomainsLowererState { functions: BTreeSet, domains: BTreeMap, + domains_info: DomainsInfo, } impl DomainsLowererState { - pub fn destruct(self) -> Vec { - self.domains.into_values().collect() + pub fn destruct(self) -> (Vec, DomainsInfo) { + (self.domains.into_values().collect(), self.domains_info) } } -trait DomainsLowererInterfacePrivate { - /// Returns a borrow of a domain. Creates the domain if it does not exist. - fn borrow_domain( - &mut self, - domain_name: String, - ) -> SpannedEncodingResult<&mut vir_low::DomainDecl>; - fn create_domain_func_app_custom( - &mut self, - domain_name: String, - function_name: String, - arguments: Vec, - return_type: vir_low::Type, - is_unique: bool, - position: vir_low::Position, - ) -> SpannedEncodingResult; -} +// trait DomainsLowererInterfacePrivate { +// /// Returns a borrow of a domain. Creates the domain if it does not exist. +// fn borrow_domain( +// &mut self, +// domain_name: String, +// ) -> SpannedEncodingResult<&mut vir_low::DomainDecl>; +// fn create_domain_func_app_custom( +// &mut self, +// domain_name: String, +// function_name: String, +// arguments: Vec, +// return_type: vir_low::Type, +// is_unique: bool, +// position: vir_low::Position, +// ) -> SpannedEncodingResult; +// } -impl<'p, 'v: 'p, 'tcx: 'v> DomainsLowererInterfacePrivate for Lowerer<'p, 'v, 'tcx> { +impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { fn borrow_domain( &mut self, domain_name: String, @@ -44,7 +48,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> DomainsLowererInterfacePrivate for Lowerer<'p, 'v, 't .domains_state .domains .entry(domain_name.clone()) - .or_insert_with(|| vir_low::DomainDecl::new(domain_name, Vec::new(), Vec::new())); + .or_insert_with(|| { + vir_low::DomainDecl::new(domain_name, Vec::new(), Vec::new(), Vec::new()) + }); Ok(domain) } @@ -84,6 +90,11 @@ pub(in super::super::super) trait DomainsLowererInterface { domain_name: &str, axiom: vir_low::DomainAxiomDecl, ) -> SpannedEncodingResult<()>; + fn declare_rewrite_rule( + &mut self, + domain_name: &str, + axiom: vir_low::DomainRewriteRuleDecl, + ) -> SpannedEncodingResult<()>; fn insert_domain_function( &mut self, domain_name: &str, @@ -97,6 +108,15 @@ pub(in super::super::super) trait DomainsLowererInterface { parameters: std::borrow::Cow<'_, Vec>, return_type: std::borrow::Cow<'_, vir_low::Type>, ) -> SpannedEncodingResult<()>; + // /// Declare a domain function that is a binary operator. + // fn declare_domain_function_maybe_binary_op( + // &mut self, + // domain_name: &str, + // function_name: std::borrow::Cow<'_, String>, + // operation: Option<(vir_mid::BinaryOpKind, vir_mid::Type)>, + // parameters: std::borrow::Cow<'_, Vec>, + // return_type: std::borrow::Cow<'_, vir_low::Type>, + // ) -> SpannedEncodingResult<()>; fn create_domain_func_app( &mut self, domain_name: impl ToString, @@ -157,6 +177,15 @@ impl<'p, 'v: 'p, 'tcx: 'v> DomainsLowererInterface for Lowerer<'p, 'v, 'tcx> { domain.axioms.push(axiom); Ok(()) } + fn declare_rewrite_rule( + &mut self, + domain_name: &str, + axiom: vir_low::DomainRewriteRuleDecl, + ) -> SpannedEncodingResult<()> { + let domain = self.domains_state.domains.get_mut(domain_name).unwrap(); + domain.rewrite_rules.push(axiom); + Ok(()) + } fn insert_domain_function( &mut self, domain_name: &str, @@ -194,6 +223,33 @@ impl<'p, 'v: 'p, 'tcx: 'v> DomainsLowererInterface for Lowerer<'p, 'v, 'tcx> { } Ok(()) } + // fn declare_domain_function_maybe_binary_op( + // &mut self, + // domain_name: &str, + // function_name: std::borrow::Cow<'_, String>, + // operation: Option<(vir_mid::BinaryOpKind, vir_mid::Type)>, + // parameters: std::borrow::Cow<'_, Vec>, + // return_type: std::borrow::Cow<'_, vir_low::Type>, + // ) -> SpannedEncodingResult<()> { + // if !self.domains_state.functions.contains(&*function_name) { + // if let Some((op, ty)) = operation { + // assert!(self + // .domains_state + // .domains_info + // .snapshot_binary_operators + // .insert(function_name.to_string(), (op, ty),) + // .is_none()); + // } + // self.declare_domain_function( + // domain_name, + // function_name, + // false, + // parameters, + // return_type, + // )?; + // } + // Ok(()) + // } /// Note: You are likely to want to call one of this function's wrappers. fn create_domain_func_app( &mut self, diff --git a/prusti-viper/src/encoder/middle/core_proof/lowerer/domains/mod.rs b/prusti-viper/src/encoder/middle/core_proof/lowerer/domains/mod.rs index 3dffb1d6e1a..f40ce218924 100644 --- a/prusti-viper/src/encoder/middle/core_proof/lowerer/domains/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/lowerer/domains/mod.rs @@ -1,4 +1,4 @@ mod interface; -pub(in super::super) use self::interface::DomainsLowererInterface; pub(super) use self::interface::DomainsLowererState; +pub(in super::super) use self::interface::{DomainsInfo, DomainsLowererInterface}; diff --git a/prusti-viper/src/encoder/middle/core_proof/lowerer/functions/interface.rs b/prusti-viper/src/encoder/middle/core_proof/lowerer/functions/interface.rs index 3c428d40d8a..b35742de603 100644 --- a/prusti-viper/src/encoder/middle/core_proof/lowerer/functions/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/lowerer/functions/interface.rs @@ -5,7 +5,8 @@ use crate::encoder::{ function_gas::FunctionGasInterface, lowerer::{DomainsLowererInterface, Lowerer}, snapshots::{ - IntoPureBoolExpression, IntoPureSnapshot, IntoSnapshot, SnapshotValidityInterface, + FramedExpressionToSnapshot, IntoPureBoolExpression, IntoPureSnapshot, IntoSnapshot, + IntoSnapshotLowerer, SnapshotValidityInterface, }, types::TypesInterface, }, @@ -28,16 +29,7 @@ impl FunctionsLowererState { } } -trait Private { - fn caller_function_name(&mut self, function_name: &str) -> String; - fn ensure_pure_function_lowered(&mut self, function_name: String) -> SpannedEncodingResult<()>; - fn ensure_all_types_lowered( - &mut self, - function_decl: &vir_mid::FunctionDecl, - ) -> SpannedEncodingResult<()>; -} - -impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { +impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { fn caller_function_name(&mut self, function_name: &str) -> String { format!("caller_for${function_name}") } @@ -124,7 +116,19 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { return_type, ); let body = if let Some(body) = function_decl.body { - expr! { ([call.clone()] == [body.to_pure_snapshot(self)?]) } + // eprintln!("body: {body}"); + let framing_variables = &function_decl.parameters; + // for variable in framing_variables { + // eprintln!("variable: {variable}"); + // } + // let deref_fields = self.framing_variable_deref_fields(framing_variables)?; + // for (e, name, ty) in &deref_fields { + // eprintln!("field: {} {} {}", e, name, ty); + // } + let mut body_encoder = + FramedExpressionToSnapshot::for_function_body(framing_variables); + let encoded_body = body_encoder.expression_to_snapshot(self, &body, false)?; + expr! { ([call.clone()] == [encoded_body]) } } else { true.into() }; diff --git a/prusti-viper/src/encoder/middle/core_proof/lowerer/mod.rs b/prusti-viper/src/encoder/middle/core_proof/lowerer/mod.rs index 341045a7d4f..bb63e489717 100644 --- a/prusti-viper/src/encoder/middle/core_proof/lowerer/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/lowerer/mod.rs @@ -3,25 +3,38 @@ use self::{ predicates::PredicatesLowererState, variables::VariablesLowererState, }; use super::{ + addresses::AddressState, adts::AdtsState, + block_markers::BlockMarkersInterface, builtin_methods::BuiltinMethodsState, compute_address::ComputeAddressState, + heap::HeapState, into_low::IntoLow, + labels::LabelsState, lifetimes::LifetimesState, places::PlacesState, predicates::{PredicatesMemoryBlockInterface, PredicatesOwnedInterface, PredicatesState}, - snapshots::{SnapshotVariablesInterface, SnapshotsState}, + snapshots::{SnapshotDomainsInfo, SnapshotVariablesInterface, SnapshotsState}, types::TypesState, + viewshifts::ViewShiftsState, }; use crate::encoder::{ - errors::SpannedEncodingResult, middle::core_proof::builtin_methods::BuiltinMethodsInterface, + errors::{ErrorCtxt, SpannedEncodingResult}, + middle::core_proof::{ + builtin_methods::BuiltinMethodsInterface, + predicates::{PredicateInfo, PredicatesAliasingInterface}, + }, + mir::errors::ErrorInterface, Encoder, }; +use log::info; use prusti_rustc_interface::hir::def_id::DefId; use rustc_hash::{FxHashMap, FxHashSet}; use std::collections::BTreeMap; use vir_crate::{ - common::{cfg::Cfg, check_mode::CheckMode, graphviz::ToGraphviz}, + common::{ + cfg::Cfg, check_mode::CheckMode, expression::UnaryOperationHelpers, graphviz::ToGraphviz, + }, low::{self as vir_low, operations::ty::Typed}, middle as vir_mid, }; @@ -33,8 +46,10 @@ mod predicates; mod variables; pub(super) use self::{ - domains::DomainsLowererInterface, functions::FunctionsLowererInterface, - methods::MethodsLowererInterface, predicates::PredicatesLowererInterface, + domains::{DomainsInfo, DomainsLowererInterface}, + functions::FunctionsLowererInterface, + methods::MethodsLowererInterface, + predicates::PredicatesLowererInterface, variables::VariablesLowererInterface, }; @@ -44,6 +59,9 @@ pub(super) struct LoweringResult { pub(super) functions: Vec, pub(super) predicates: Vec, pub(super) methods: Vec, + pub(super) snapshot_domains_info: SnapshotDomainsInfo, + pub(super) domains_info: DomainsInfo, + pub(super) predicates_info: PredicateInfo, } pub(super) fn lower_procedure<'p, 'v: 'p, 'tcx: 'v>( @@ -51,6 +69,7 @@ pub(super) fn lower_procedure<'p, 'v: 'p, 'tcx: 'v>( def_id: DefId, procedure: vir_mid::ProcedureDecl, ) -> SpannedEncodingResult { + info!("Lowering procedure {} ({def_id:?})", procedure.name); let lowerer = self::Lowerer::new(encoder); let mut result = lowerer.lower_procedure(def_id, procedure)?; if let Some(path) = prusti_common::config::execute_only_failing_trace() { @@ -88,6 +107,7 @@ pub(super) fn lower_type<'p, 'v: 'p, 'tcx: 'v>( pub(super) struct Lowerer<'p, 'v: 'p, 'tcx: 'v> { pub(super) encoder: &'p mut Encoder<'v, 'tcx>, pub(super) def_id: Option, + pub(super) procedure_position: Option, pub(super) check_mode: Option, variables_state: VariablesLowererState, functions_state: FunctionsLowererState, @@ -102,6 +122,10 @@ pub(super) struct Lowerer<'p, 'v: 'p, 'tcx: 'v> { pub(super) adts_state: AdtsState, pub(super) lifetimes_state: LifetimesState, pub(super) places_state: PlacesState, + pub(super) heap_state: HeapState, + pub(super) address_state: AddressState, + pub(super) labels_state: LabelsState, + pub(super) view_shifts_state: ViewShiftsState, } impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { @@ -109,6 +133,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { Self { encoder, def_id: None, + procedure_position: None, check_mode: None, variables_state: Default::default(), functions_state: Default::default(), @@ -123,6 +148,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { adts_state: Default::default(), lifetimes_state: Default::default(), places_state: Default::default(), + heap_state: Default::default(), + address_state: Default::default(), + labels_state: Default::default(), + view_shifts_state: Default::default(), } } @@ -131,7 +160,13 @@ impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { def_id: DefId, mut procedure: vir_mid::ProcedureDecl, ) -> SpannedEncodingResult { + assert!( + !procedure.position.is_default(), + "procedure {def_id:?} without position" + ); self.def_id = Some(def_id); + self.procedure_position = Some(procedure.position); + self.set_non_aliased_places(std::mem::take(&mut procedure.non_aliased_places))?; let mut basic_blocks_map = BTreeMap::new(); let mut basic_block_edges = BTreeMap::new(); let predecessors = procedure.predecessors_owned(); @@ -142,22 +177,30 @@ impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { self.set_current_block_for_snapshots(label, &predecessors, &mut basic_block_edges)?; let basic_block = procedure.basic_blocks.remove(label).unwrap(); let marker = self.create_block_marker(label)?; - marker_initialisation.push(vir_low::Statement::assign_no_pos( - marker.clone(), - false.into(), + marker_initialisation.push(vir_low::Statement::assume( + vir_low::Expression::not(self.initial_snapshot_variable_version(&marker)?.into()), + procedure.position, )); let mut statements = vec![ - vir_low::Statement::assign_no_pos(marker.clone(), true.into()), + // vir_low::Statement::assign(marker.clone(), true.into(), procedure.position), + vir_low::Statement::assume( + self.new_snapshot_variable_version(&marker, procedure.position)? + .into(), + procedure.position, + ), // We need to use a function call here because Silicon optimizes // out assignments to pure variables and our Z3 wrapper does not // see them. - vir_low::Statement::log_event(self.create_domain_func_app( - "MarkerCalls", - format!("basic_block_marker${}", marker.name), - vec![], - vir_low::Type::Bool, - Default::default(), - )?), + vir_low::Statement::log_event( + self.create_domain_func_app( + "MarkerCalls", + format!("basic_block_marker${}", marker.name), + vec![], + vir_low::Type::Bool, + procedure.position, + )?, + procedure.position, + ), ]; for statement in basic_block.statements { statements.extend(statement.into_low(&mut self)?); @@ -170,52 +213,103 @@ impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { std::mem::swap(entry_block_statements, &mut marker_initialisation); entry_block_statements.extend(marker_initialisation); - let mut basic_blocks = Vec::new(); + let mut basic_blocks = BTreeMap::new(); for basic_block_id in traversal_order { let (statements, mut successor) = basic_blocks_map.remove(&basic_block_id).unwrap(); let label = basic_block_id.clone().into_low(&mut self)?; if let Some(intermediate_blocks) = basic_block_edges.remove(&basic_block_id) { - for (successor_label, successor_statements) in intermediate_blocks { + for (successor_label, equalities) in intermediate_blocks { let successor_label = successor_label.into_low(&mut self)?; let intermediate_block_label = vir_low::Label::new(format!( "label__from__{}__to__{}", label.name, successor_label.name )); successor.replace_label(&successor_label, intermediate_block_label.clone()); - basic_blocks.push(vir_low::BasicBlock { - label: intermediate_block_label, - statements: successor_statements, - successor: vir_low::Successor::Goto(successor_label), - }); + let mut successor_statements = Vec::new(); + for (variable_name, ty, position, old_version, new_version) in equalities { + let new_variable = self.create_snapshot_variable_low( + &variable_name, + ty.clone(), + new_version, + )?; + let old_variable = self.create_snapshot_variable_low( + &variable_name, + ty.clone(), + old_version, + )?; + let position = self.encoder.change_error_context( + // FIXME: Get a more precise span. + position, + ErrorCtxt::Unexpected, + ); + let statement = vir_low::macros::stmtp! { + position => assume (new_variable == old_variable) + }; + successor_statements.push(statement); + } + basic_blocks.insert( + intermediate_block_label, + vir_low::BasicBlock { + statements: successor_statements, + successor: vir_low::Successor::Goto(successor_label), + }, + ); } } - basic_blocks.push(vir_low::BasicBlock { + basic_blocks.insert( label, - statements, - successor, - }); + vir_low::BasicBlock { + statements, + successor, + }, + ); } + let entry = procedure.entry.clone().into_low(&mut self)?; + let exit = procedure.exit.clone().into_low(&mut self)?; let mut removed_functions = FxHashSet::default(); - if procedure.check_mode == CheckMode::Specifications { + if procedure.check_mode == CheckMode::PurificationFunctional { removed_functions.insert(self.encode_memory_block_bytes_function_name()?); } - let mut predicates = self.collect_owned_predicate_decls()?; - basic_blocks[0].statements.splice( + let (mut predicates, owned_predicates_info) = self.collect_owned_predicate_decls()?; + let predicates_info = PredicateInfo { + non_aliased_memory_block_addresses: self.take_non_aliased_memory_block_addresses()?, + owned_predicates_info, + }; + basic_blocks.get_mut(&entry).unwrap().statements.splice( 0..0, self.lifetimes_state.lifetime_is_alive_initialization(), ); - let mut domains = self.domains_state.destruct(); + if prusti_common::config::dump_debug_info() { + let source_filename = self.encoder.env().name.source_file_name(); + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_low_before_perm_desugaring", + format!("{}.{}.dot", source_filename, procedure.name), + |writer| basic_blocks.to_graphviz(writer).unwrap(), + ); + } + let snapshot_domains_info = self.snapshots_state.destruct(); + let (mut domains, domains_info) = self.domains_state.destruct(); domains.extend(self.compute_address_state.destruct()); predicates.extend(self.predicates_state.destruct()); let mut lowered_procedure = vir_low::ProcedureDecl { name: procedure.name, + position: procedure.position, locals: self.variables_state.destruct(), + custom_labels: self.labels_state.destruct(), basic_blocks, + entry, + exit, }; let mut methods = self.methods_state.destruct(); let mut functions = self.functions_state.destruct(); - if procedure.check_mode == CheckMode::Specifications { + if procedure.check_mode == CheckMode::PurificationFunctional { + removed_functions.extend( + functions + .iter() + .filter(|function| function.kind == vir_low::FunctionKind::Snap) + .map(|function| function.name.clone()), + ); super::transformations::remove_predicates::remove_predicates( &mut lowered_procedure, &mut methods, @@ -224,13 +318,19 @@ impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { ); functions.retain(|function| !removed_functions.contains(&function.name)); }; - Ok(LoweringResult { + let result = LoweringResult { procedures: vec![lowered_procedure], domains, functions, predicates, methods, - }) + domains_info, + snapshot_domains_info, + predicates_info, + }; + self.def_id = None; + self.procedure_position = None; + Ok(result) } fn create_parameters(&self, arguments: &[vir_low::Expression]) -> Vec { @@ -243,12 +343,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { .collect() } - fn create_block_marker( - &mut self, - label: &vir_mid::BasicBlockId, - ) -> SpannedEncodingResult { - self.create_variable(format!("{label}$marker"), vir_low::Type::Bool) - } + // fn create_block_marker( + // &mut self, + // label: &vir_mid::BasicBlockId, + // ) -> SpannedEncodingResult { + // self.create_variable(format!("{label}$marker"), vir_low::Type::Bool) + // } /// If `check_copy` is true, encode `copy` builtin method. fn lower_type( @@ -258,21 +358,29 @@ impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { check_copy: bool, ) -> SpannedEncodingResult { self.def_id = def_id; - self.mark_owned_non_aliased_as_unfolded(&ty)?; + self.mark_owned_predicate_as_unfolded(&ty)?; self.encode_move_place_method(&ty)?; if check_copy { self.encode_copy_place_method(&ty)?; } - let mut predicates = self.collect_owned_predicate_decls()?; - let mut domains = self.domains_state.destruct(); + let (mut predicates, owned_predicates_info) = self.collect_owned_predicate_decls()?; + let snapshot_domains_info = self.snapshots_state.destruct(); + let (mut domains, domains_info) = self.domains_state.destruct(); domains.extend(self.compute_address_state.destruct()); predicates.extend(self.predicates_state.destruct()); + let predicates_info = PredicateInfo { + owned_predicates_info, + non_aliased_memory_block_addresses: Default::default(), + }; Ok(LoweringResult { procedures: Vec::new(), domains, functions: self.functions_state.destruct(), predicates, methods: self.methods_state.destruct(), + predicates_info, + snapshot_domains_info, + domains_info, }) } } diff --git a/prusti-viper/src/encoder/middle/core_proof/lowerer/variables/interface.rs b/prusti-viper/src/encoder/middle/core_proof/lowerer/variables/interface.rs index 12374a09824..5b594acd781 100644 --- a/prusti-viper/src/encoder/middle/core_proof/lowerer/variables/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/lowerer/variables/interface.rs @@ -24,6 +24,7 @@ pub(in super::super::super) trait VariablesLowererInterface { name: String, ty: vir_low::Type, ) -> SpannedEncodingResult; + fn register_variable(&mut self, variable: &vir_low::VariableDecl) -> SpannedEncodingResult<()>; fn create_new_temporary_variable( &mut self, ty: vir_low::Type, @@ -43,6 +44,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> VariablesLowererInterface for Lowerer<'p, 'v, 'tcx> { } Ok(vir_low::VariableDecl::new(name, ty)) } + fn register_variable(&mut self, variable: &vir_low::VariableDecl) -> SpannedEncodingResult<()> { + if !self.variables_state.variables.contains_key(&variable.name) { + self.variables_state + .variables + .insert(variable.name.clone(), variable.ty.clone()); + } + Ok(()) + } fn create_new_temporary_variable( &mut self, ty: vir_low::Type, diff --git a/prusti-viper/src/encoder/middle/core_proof/mod.rs b/prusti-viper/src/encoder/middle/core_proof/mod.rs index 2a36c53a2d3..55ea4643ccc 100644 --- a/prusti-viper/src/encoder/middle/core_proof/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/mod.rs @@ -5,12 +5,14 @@ mod builtin_methods; mod compute_address; mod const_generics; mod errors; +mod footprint; mod function_gas; mod interface; mod into_low; mod lifetimes; mod lowerer; mod places; +mod pointers; mod predicates; mod references; mod snapshots; @@ -18,5 +20,8 @@ mod transformations; mod type_layouts; mod types; mod utils; +mod heap; +mod labels; +mod viewshifts; pub(crate) use self::interface::{MidCoreProofEncoderInterface, MidCoreProofEncoderState}; diff --git a/prusti-viper/src/encoder/middle/core_proof/places/encoder.rs b/prusti-viper/src/encoder/middle/core_proof/places/encoder.rs index 4204479138d..33e21f467ed 100644 --- a/prusti-viper/src/encoder/middle/core_proof/places/encoder.rs +++ b/prusti-viper/src/encoder/middle/core_proof/places/encoder.rs @@ -8,14 +8,14 @@ use crate::encoder::{ }; use vir_crate::{ low as vir_low, - middle::{self as vir_mid}, + middle::{self as vir_mid, operations::ty::Typed}, }; pub(super) struct PlaceEncoder {} impl PlaceExpressionDomainEncoder for PlaceEncoder { fn domain_name(&mut self, _lowerer: &mut Lowerer) -> &str { - "Place" + "PlaceOption" } fn encode_local( @@ -33,7 +33,8 @@ impl PlaceExpressionDomainEncoder for PlaceEncoder { local.position, )?; lowerer.encode_compute_address_for_place_root(&place_root)?; - Ok(place_root) + let place_option_root = lowerer.place_option_some_constructor(place_root.clone())?; + Ok(place_option_root) } fn encode_deref( @@ -42,7 +43,13 @@ impl PlaceExpressionDomainEncoder for PlaceEncoder { lowerer: &mut Lowerer, arg: vir_low::Expression, ) -> SpannedEncodingResult { - lowerer.encode_deref_place(arg, deref.position) + if deref.base.get_type().is_reference() { + lowerer.encode_deref_place(arg, deref.position) + } else { + assert!(deref.base.get_type().is_pointer()); + lowerer.place_option_none_constructor(deref.position) + // lowerer.encode_aliased_place_root(deref.position) + } } fn encode_array_index_axioms( @@ -52,4 +59,12 @@ impl PlaceExpressionDomainEncoder for PlaceEncoder { ) -> SpannedEncodingResult<()> { lowerer.encode_place_array_index_axioms(ty) } + + fn encode_labelled_old( + &mut self, + expression: &vir_mid::expression::LabelledOld, + lowerer: &mut Lowerer, + ) -> SpannedEncodingResult { + self.encode_expression(&expression.base, lowerer) + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/places/interface.rs b/prusti-viper/src/encoder/middle/core_proof/places/interface.rs index 66796753217..406763bf93f 100644 --- a/prusti-viper/src/encoder/middle/core_proof/places/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/places/interface.rs @@ -10,8 +10,8 @@ use crate::encoder::{ }; use prusti_rustc_interface::data_structures::fx::FxHashSet; use vir_crate::{ - common::{expression::QuantifierHelpers, identifier::WithIdentifier}, - low::{self as vir_low, macros::var_decls}, + common::{expression::QuantifierHelpers, identifier::WithIdentifier, position::Positioned}, + low::{self as vir_low, macros::var_decls, operations::ty::Typed}, middle::{self as vir_mid}, }; @@ -23,6 +23,15 @@ pub(in super::super) struct PlacesState { pub(in super::super) trait PlacesInterface { fn place_type(&mut self) -> SpannedEncodingResult; + fn place_option_type(&mut self) -> SpannedEncodingResult; + fn place_option_some_constructor( + &mut self, + place: vir_low::Expression, + ) -> SpannedEncodingResult; + fn place_option_none_constructor( + &mut self, + position: vir_mid::Position, + ) -> SpannedEncodingResult; fn encode_expression_as_place( &mut self, place: &vir_mid::Expression, @@ -54,12 +63,47 @@ pub(in super::super) trait PlacesInterface { position: vir_mid::Position, ) -> SpannedEncodingResult; fn encode_place_array_index_axioms(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()>; + fn encode_aliased_place_root( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult; } impl<'p, 'v: 'p, 'tcx: 'v> PlacesInterface for Lowerer<'p, 'v, 'tcx> { fn place_type(&mut self) -> SpannedEncodingResult { self.domain_type("Place") } + fn place_option_type(&mut self) -> SpannedEncodingResult { + self.domain_type("PlaceOption") + } + fn place_option_some_constructor( + &mut self, + place: vir_low::Expression, + ) -> SpannedEncodingResult { + debug_assert_eq!(place.get_type(), &self.place_type()?); + let place_option_type = self.place_option_type()?; + let position = place.position(); + self.create_domain_func_app( + "PlaceOption", + "place_option_some", + vec![place], + place_option_type, + position, + ) + } + fn place_option_none_constructor( + &mut self, + position: vir_mid::Position, + ) -> SpannedEncodingResult { + let place_option_type = self.place_option_type()?; + self.create_domain_func_app( + "PlaceOption", + "place_option_none", + Vec::new(), + place_option_type, + position, + ) + } /// Emits code that represents the place. fn encode_expression_as_place( &mut self, @@ -75,7 +119,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> PlacesInterface for Lowerer<'p, 'v, 'tcx> { base_place: vir_low::Expression, position: vir_mid::Position, ) -> SpannedEncodingResult { - self.encode_field_access_function_app("Place", base_place, base_type, field, position) + debug_assert_eq!(base_place.get_type(), &self.place_option_type()?); + self.encode_field_access_function_app("PlaceOption", base_place, base_type, field, position) } fn encode_enum_variant_place( &mut self, @@ -84,16 +129,23 @@ impl<'p, 'v: 'p, 'tcx: 'v> PlacesInterface for Lowerer<'p, 'v, 'tcx> { base_place: vir_low::Expression, position: vir_mid::Position, ) -> SpannedEncodingResult { - self.encode_variant_access_function_app("Place", base_place, base_type, variant, position) + self.encode_variant_access_function_app( + "PlaceOption", + base_place, + base_type, + variant, + position, + ) } fn encode_deref_place( &mut self, base_place: vir_low::Expression, position: vir_mid::Position, ) -> SpannedEncodingResult { - let return_type = self.place_type()?; + debug_assert_eq!(base_place.get_type(), &self.place_option_type()?); + let return_type = self.place_option_type()?; self.create_domain_func_app( - "Place", + "PlaceOption", "deref_reference_place", vec![base_place], return_type, @@ -107,7 +159,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> PlacesInterface for Lowerer<'p, 'v, 'tcx> { index: vir_low::Expression, position: vir_mid::Position, ) -> SpannedEncodingResult { - self.encode_index_access_function_app("Place", base_place, base_type, index, position) + self.encode_index_access_function_app("PlaceOption", base_place, base_type, index, position) } fn encode_place_array_index_axioms(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()> { let identifier = ty.get_identifier(); @@ -159,4 +211,20 @@ impl<'p, 'v: 'p, 'tcx: 'v> PlacesInterface for Lowerer<'p, 'v, 'tcx> { } Ok(()) } + fn encode_aliased_place_root( + &mut self, + _position: vir_low::Position, + ) -> SpannedEncodingResult { + unimplemented!(); + // let return_type = self.place_type()?; + // let place_root = self.create_domain_func_app( + // "PlaceOption", + // "aliased_place_root", + // vec![], + // return_type, + // position, + // )?; + // self.encode_compute_address_for_place_root(&place_root)?; + // Ok(place_root) + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/pointers/interface.rs b/prusti-viper/src/encoder/middle/core_proof/pointers/interface.rs new file mode 100644 index 00000000000..b7626db8365 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/pointers/interface.rs @@ -0,0 +1,199 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + heap::HeapInterface, + lowerer::{DomainsLowererInterface, Lowerer}, + snapshots::{IntoSnapshot, SnapshotValuesInterface, SnapshotVariablesInterface}, + type_layouts::TypeLayoutsInterface, + }, +}; +use vir_crate::{ + common::identifier::WithIdentifier, + low as vir_low, + middle::{self as vir_mid}, +}; + +pub(in super::super) trait PointersInterface { + fn pointer_address( + &mut self, + pointer_type: &vir_mid::Type, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn address_to_pointer( + &mut self, + pointer_type: &vir_mid::Type, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn pointer_slice_len( + &mut self, + pointer_type: &vir_mid::Type, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn pointer_target_snapshot_in_heap( + &mut self, + ty: &vir_mid::Type, + heap: vir_low::VariableDecl, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn pointer_target_snapshot( + &mut self, + ty: &vir_mid::Type, + old_label: &Option, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn pointer_target_as_snapshot_field( + &mut self, + framing_type: &vir_mid::Type, + deref_field_name: &str, + deref_type: vir_low::Type, + framing_place_snapshot: vir_low::Expression, + position: vir_mid::Position, + ) -> SpannedEncodingResult; + fn heap_chunk_to_snapshot( + &mut self, + ty: &vir_mid::Type, + heap_chunk: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn address_in_heap( + &mut self, + heap: vir_low::VariableDecl, + pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult; +} + +impl<'p, 'v: 'p, 'tcx: 'v> PointersInterface for Lowerer<'p, 'v, 'tcx> { + fn pointer_address( + &mut self, + pointer_type: &vir_mid::Type, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + assert!(pointer_type.is_pointer()); + // self.obtain_constant_value(pointer_type, snapshot, position) + let address_type = self.address_type()?; + self.obtain_parameter_snapshot(pointer_type, "address", address_type, snapshot, position) + } + fn address_to_pointer( + &mut self, + pointer_type: &vir_mid::Type, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + assert!(pointer_type.is_pointer()); + self.construct_struct_snapshot(pointer_type, vec![address], position) + } + fn pointer_slice_len( + &mut self, + pointer_type: &vir_mid::Type, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + assert!(pointer_type.is_pointer_to_slice()); + let len_type = self.size_type()?; + self.obtain_parameter_snapshot(pointer_type, "len", len_type, snapshot, position) + } + fn pointer_target_snapshot_in_heap( + &mut self, + ty: &vir_mid::Type, + heap: vir_low::VariableDecl, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let address = self.pointer_address(ty, snapshot, position)?; + let heap_chunk = self.heap_lookup(heap.into(), address, position)?; + // let heap_chunk = vir_low::Expression::container_op_no_pos( + // vir_low::ContainerOpKind::MapLookup, + // heap.ty.clone(), + // vec![heap.into(), address], + // ); + let pointer_type = ty.clone().unwrap_pointer(); + self.heap_chunk_to_snapshot(&pointer_type.target_type, heap_chunk, position) + } + fn pointer_target_snapshot( + &mut self, + ty: &vir_mid::Type, + old_label: &Option, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + if self.use_heap_variable()? { + // let address = self.pointer_address(ty, snapshot, position)?; + let heap = self.heap_variable_version_at_label(old_label)?; + // let heap_chunk = vir_low::Expression::container_op_no_pos( + // vir_low::ContainerOpKind::MapLookup, + // heap.ty.clone(), + // vec![heap.into(), address], + // ); + // let pointer_type = ty.clone().unwrap_pointer(); + // self.heap_chunk_to_snapshot(&pointer_type.target_type, heap_chunk, position) + self.pointer_target_snapshot_in_heap(ty, heap, snapshot, position) + } else { + unimplemented!(); + // let address = self.pointer_address(ty, snapshot, position)?; + // let pointer_type = ty.clone().unwrap_pointer(); + // let target_type = &*pointer_type.target_type; + // self.owned_aliased_snap( + // CallContext::Procedure, + // target_type, + // target_type, + // address, + // position, + // ) + } + } + fn pointer_target_as_snapshot_field( + &mut self, + framing_type: &vir_mid::Type, + deref_field_name: &str, + deref_type: vir_low::Type, + framing_place_snapshot: vir_low::Expression, + position: vir_mid::Position, + ) -> SpannedEncodingResult { + self.obtain_parameter_snapshot( + framing_type, + deref_field_name, + deref_type, + framing_place_snapshot, + position, + ) + } + fn heap_chunk_to_snapshot( + &mut self, + ty: &vir_mid::Type, + heap_chunk: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let return_type = ty.to_snapshot(self)?; + self.create_domain_func_app( + // FIXME: Use HEAP_CHUNK_TYPE_NAME here. + "HeapChunk$", + format!("heap_chunk_to${}", ty.get_identifier()), + vec![heap_chunk], + return_type, + position, + ) + } + fn address_in_heap( + &mut self, + _heap: vir_low::VariableDecl, + _pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + todo!("Delete"); + // let pointer = pointer_place.to_pure_snapshot(self)?; + // let address = + // self.pointer_address(pointer_place.get_type(), pointer, pointer_place.position())?; + // let in_heap = vir_low::Expression::container_op_no_pos( + // vir_low::ContainerOpKind::MapContains, + // heap.ty.clone(), + // vec![heap.into(), address], + // ); + // Ok(in_heap) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/pointers/mod.rs b/prusti-viper/src/encoder/middle/core_proof/pointers/mod.rs new file mode 100644 index 00000000000..0e0b37ac78b --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/pointers/mod.rs @@ -0,0 +1,3 @@ +mod interface; + +pub(super) use self::interface::PointersInterface; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/aliasing/interface.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/aliasing/interface.rs new file mode 100644 index 00000000000..578eef9671a --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/aliasing/interface.rs @@ -0,0 +1,75 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{addresses::AddressesInterface, lowerer::Lowerer}, +}; +use rustc_hash::FxHashSet; +use vir_crate::{low as vir_low, middle as vir_mid}; + +#[derive(Default)] +pub(in super::super) struct PredicatesAliasingState { + non_aliased_places: Vec, + non_aliased_memory_block_addresses: FxHashSet, +} + +pub(in super::super::super) trait PredicatesAliasingInterface { + fn set_non_aliased_places( + &mut self, + places: Vec, + ) -> SpannedEncodingResult<()>; + fn mark_place_as_used_in_memory_block( + &mut self, + place: &vir_mid::Expression, + ) -> SpannedEncodingResult<()>; + fn take_non_aliased_memory_block_addresses( + &mut self, + ) -> SpannedEncodingResult>; +} + +impl<'p, 'v: 'p, 'tcx: 'v> PredicatesAliasingInterface for Lowerer<'p, 'v, 'tcx> { + fn set_non_aliased_places( + &mut self, + places: Vec, + ) -> SpannedEncodingResult<()> { + assert!( + self.predicates_encoding_state + .aliasing + .non_aliased_places + .is_empty(), + "Predicates aliasing state is already initialized." + ); + self.predicates_encoding_state.aliasing.non_aliased_places = places; + Ok(()) + } + + fn mark_place_as_used_in_memory_block( + &mut self, + place: &vir_mid::Expression, + ) -> SpannedEncodingResult<()> { + for non_aliased_place in &self.predicates_encoding_state.aliasing.non_aliased_places { + if place.has_prefix(non_aliased_place) { + let address = self.encode_expression_as_place_address(place)?; + self.predicates_encoding_state + .aliasing + .non_aliased_memory_block_addresses + .insert(address); + return Ok(()); + } + } + Ok(()) + } + + fn take_non_aliased_memory_block_addresses( + &mut self, + ) -> SpannedEncodingResult> { + self.predicates_encoding_state + .aliasing + .non_aliased_places + .clear(); + Ok(std::mem::take( + &mut self + .predicates_encoding_state + .aliasing + .non_aliased_memory_block_addresses, + )) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/aliasing/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/aliasing/mod.rs new file mode 100644 index 00000000000..5fa538f1fc7 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/aliasing/mod.rs @@ -0,0 +1,4 @@ +mod interface; + +pub(in super::super) use self::interface::PredicatesAliasingInterface; +pub(super) use self::interface::PredicatesAliasingState; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/memory_block/interface.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/memory_block/interface.rs index 8bbf051fcdb..31c745c93ec 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/memory_block/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/memory_block/interface.rs @@ -5,11 +5,15 @@ use crate::encoder::{ lowerer::{ DomainsLowererInterface, FunctionsLowererInterface, Lowerer, PredicatesLowererInterface, }, + snapshots::SnapshotValuesInterface, type_layouts::TypeLayoutsInterface, }, }; use rustc_hash::FxHashSet; -use vir_crate::low as vir_low; +use vir_crate::{ + common::expression::QuantifierHelpers, + low::{self as vir_low, operations::ty::Typed}, +}; #[derive(Default)] pub(in super::super) struct PredicatesMemoryBlockState { @@ -21,10 +25,12 @@ trait Private { fn encode_generic_memory_block_predicate( &mut self, predicate_name: &str, + predicate_kind: vir_low::PredicateKind, ) -> SpannedEncodingResult<()>; fn encode_generic_memory_block_acc( &mut self, predicate_name: &str, + predicate_kind: vir_low::PredicateKind, place: vir_low::Expression, size: vir_low::Expression, position: vir_low::Position, @@ -35,6 +41,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { fn encode_generic_memory_block_predicate( &mut self, predicate_name: &str, + predicate_kind: vir_low::PredicateKind, ) -> SpannedEncodingResult<()> { if !self .predicates_encoding_state @@ -48,6 +55,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { .insert(predicate_name.to_string()); let predicate = vir_low::PredicateDecl::new( predicate_name, + predicate_kind, vec![ vir_low::VariableDecl::new("address", self.address_type()?), vir_low::VariableDecl::new("size", self.size_type()?), @@ -61,11 +69,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { fn encode_generic_memory_block_acc( &mut self, predicate_name: &str, + predicate_kind: vir_low::PredicateKind, place: vir_low::Expression, size: vir_low::Expression, position: vir_low::Position, ) -> SpannedEncodingResult { - self.encode_generic_memory_block_predicate(predicate_name)?; + self.encode_generic_memory_block_predicate(predicate_name, predicate_kind)?; let expression = vir_low::Expression::predicate_access_predicate( predicate_name.to_string(), vec![place, size], @@ -78,13 +87,40 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { pub(in super::super::super) trait PredicatesMemoryBlockInterface { fn bytes_type(&mut self) -> SpannedEncodingResult; + fn byte_type(&mut self) -> SpannedEncodingResult; + fn encode_read_byte_expression_int( + &mut self, + bytes: vir_low::Expression, + index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; fn encode_memory_block_predicate(&mut self) -> SpannedEncodingResult<()>; + fn encode_memory_block_stack_acc( + &mut self, + place: vir_low::Expression, + size: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn encode_memory_block_range_acc( + &mut self, + address: vir_low::Expression, + size: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; fn encode_memory_block_stack_drop_acc( &mut self, place: vir_low::Expression, size: vir_low::Expression, position: vir_low::Position, ) -> SpannedEncodingResult; + fn encode_memory_block_heap_drop_acc( + &mut self, + place: vir_low::Expression, + size: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; fn encode_memory_block_bytes_function_name(&mut self) -> SpannedEncodingResult; fn encode_memory_block_bytes_expression( &mut self, @@ -97,8 +133,75 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesMemoryBlockInterface for Lowerer<'p, 'v, 't fn bytes_type(&mut self) -> SpannedEncodingResult { self.domain_type("Bytes") } + fn byte_type(&mut self) -> SpannedEncodingResult { + self.domain_type("Byte") + } + fn encode_read_byte_expression_int( + &mut self, + bytes: vir_low::Expression, + index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + assert_eq!(index.get_type(), &vir_low::Type::Int); + let byte_type = self.byte_type()?; + let size_type = self.size_type_mid()?; + let index_usize = self.construct_constant_snapshot(&size_type, index, position)?; + self.create_domain_func_app( + "Byte", + "Byte$read_byte", + vec![bytes, index_usize], + byte_type, + position, + ) + } fn encode_memory_block_predicate(&mut self) -> SpannedEncodingResult<()> { - self.encode_generic_memory_block_predicate("MemoryBlock") + self.encode_generic_memory_block_predicate( + "MemoryBlock", + vir_low::PredicateKind::MemoryBlock, + ) + } + fn encode_memory_block_stack_acc( + &mut self, + place: vir_low::Expression, + size: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + self.encode_generic_memory_block_acc( + "MemoryBlock", + vir_low::PredicateKind::MemoryBlock, + place, + size, + position, + ) + } + fn encode_memory_block_range_acc( + &mut self, + address: vir_low::Expression, + size: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + use vir_low::macros::*; + let size_type = self.size_type_mid()?; + var_decls! { + index: Int + } + let element_address = + self.address_offset(size.clone(), address, index.clone().into(), position)?; + let predicate = + self.encode_memory_block_stack_acc(element_address.clone(), size, position)?; + let start_index = self.obtain_constant_value(&size_type, start_index, position)?; + let end_index = self.obtain_constant_value(&size_type, end_index, position)?; + let body = expr!( + (([start_index] <= index) && (index < [end_index])) ==> [predicate] + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![element_address])], + body, + ); + Ok(expression) } fn encode_memory_block_stack_drop_acc( &mut self, @@ -106,7 +209,27 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesMemoryBlockInterface for Lowerer<'p, 'v, 't size: vir_low::Expression, position: vir_low::Position, ) -> SpannedEncodingResult { - self.encode_generic_memory_block_acc("MemoryBlockStackDrop", place, size, position) + self.encode_generic_memory_block_acc( + "MemoryBlockStackDrop", + vir_low::PredicateKind::WithoutSnapshotWholeNonAliased, + place, + size, + position, + ) + } + fn encode_memory_block_heap_drop_acc( + &mut self, + address: vir_low::Expression, + size: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + self.encode_generic_memory_block_acc( + "MemoryBlockHeapDrop", + vir_low::PredicateKind::WithoutSnapshotWhole, + address, + size, + position, + ) } fn encode_memory_block_bytes_function_name(&mut self) -> SpannedEncodingResult { Ok("MemoryBlock$bytes".to_string()) diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/mod.rs index 95565515e2e..798e8667747 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/mod.rs @@ -1,11 +1,28 @@ mod memory_block; mod owned; +mod restoration; mod state; +mod aliasing; + +use rustc_hash::FxHashSet; +use std::collections::BTreeMap; +use vir_crate::low as vir_low; pub(super) use self::{ + aliasing::PredicatesAliasingInterface, memory_block::PredicatesMemoryBlockInterface, owned::{ - FracRefUseBuilder, OwnedNonAliasedUseBuilder, PredicatesOwnedInterface, UniqueRefUseBuilder, + OwnedNonAliasedSnapCallBuilder, OwnedNonAliasedUseBuilder, OwnedPredicateInfo, + PredicatesOwnedInterface, SnapshotFunctionInfo, }, + restoration::RestorationInterface, state::PredicatesState, }; + +/// Addidional information about the predicates used by purification +/// optimizations. +#[derive(Clone, Debug)] +pub(super) struct PredicateInfo { + pub(super) owned_predicates_info: BTreeMap, + pub(super) non_aliased_memory_block_addresses: FxHashSet, +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/function_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/function_decl.rs new file mode 100644 index 00000000000..e6a338eb9e4 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/function_decl.rs @@ -0,0 +1,332 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + footprint::FootprintInterface, + lifetimes::LifetimesInterface, + lowerer::Lowerer, + places::PlacesInterface, + snapshots::{ + AssertionToSnapshotConstructor, IntoPureSnapshot, IntoSnapshot, PredicateKind, + SnapshotValidityInterface, SnapshotValuesInterface, + }, + type_layouts::TypeLayoutsInterface, + }, +}; +use vir_crate::{ + common::{ + expression::{BinaryOperationHelpers, ExpressionIterator}, + identifier::WithIdentifier, + }, + low::{self as vir_low}, + middle::{self as vir_mid, operations::ty::Typed}, +}; + +/// A builder for creating snapshot function declarations. +pub(in super::super::super) struct FunctionDeclBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super) lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + pub(in super::super) function_name: &'l str, + pub(in super::super) ty: &'l vir_mid::Type, + pub(in super::super) type_decl: &'l vir_mid::TypeDecl, + pub(in super::super) parameters: Vec, + // pub(in super::super) pres: Vec, + /// The predicate for which this function is a snapshot. + pub(in super::super) predicate: Option, + /// Postconditions that we can assume always when we have the predicate. + pub(in super::super) snapshot_posts: Vec, + /// Postconditions defining the body of the snapshot that are put inside + /// `unfolding self.predicate in ...`. + pub(in super::super) snapshot_body_posts: Vec, + // pub(in super::super) conjuncts: Option>, FIXME: We have no body. + pub(in super::super) position: vir_low::Position, + pub(in super::super) place: vir_low::VariableDecl, + pub(in super::super) address: vir_low::VariableDecl, + pub(in super::super) owned_snapshot_functions_to_encode: Vec, + pub(in super::super) owned_range_snapshot_functions_to_encode: Vec, +} + +impl<'l, 'p, 'v, 'tcx> FunctionDeclBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + function_name: &'l str, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let place = vir_low::VariableDecl::new("place", lowerer.place_option_type()?); + let address = vir_low::VariableDecl::new("address", lowerer.address_type()?); + Ok(Self { + function_name, + ty, + type_decl, + parameters: Vec::new(), + predicate: None, + snapshot_posts: Vec::new(), + snapshot_body_posts: Vec::new(), + // pres: Vec::new(), + // posts: Vec::new(), + // conjuncts: None, + position, + lowerer, + place, + address, + owned_snapshot_functions_to_encode: Vec::new(), + owned_range_snapshot_functions_to_encode: Vec::new(), + }) + } + + pub(in super::super::super) fn get_snapshot_postconditions( + &self, + ) -> SpannedEncodingResult> { + Ok(self.snapshot_posts.clone()) + } + + pub(in super::super::super) fn get_snapshot_body( + &self, + ) -> SpannedEncodingResult> { + Ok(self.snapshot_body_posts.clone()) + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult { + let return_type = self.ty.to_snapshot(self.lowerer)?; + let mut pres = Vec::new(); + let mut posts = self.snapshot_posts; + if let Some(predicate) = self.predicate { + for snapshot_body_post in self.snapshot_body_posts { + posts.push(vir_low::Expression::unfolding( + predicate.clone(), + snapshot_body_post, + self.position, + )); + } + pres.push(vir_low::Expression::PredicateAccessPredicate(predicate)); + } else { + posts.extend(self.snapshot_body_posts); + }; + let function = vir_low::FunctionDecl { + name: format!("{}${}", self.function_name, self.ty.get_identifier()), + kind: vir_low::FunctionKind::Snap, + parameters: self.parameters, + body: None, + pres, + // body: self + // .conjuncts + // .map(|conjuncts| conjuncts.into_iter().conjoin()), + // pres: self.pres, + // posts: self.posts, + posts, + return_type, + }; + Ok(function) + } + + pub(in super::super) fn create_lifetime_parameters(&mut self) -> SpannedEncodingResult<()> { + self.parameters + .extend(self.lowerer.create_lifetime_parameters(self.type_decl)?); + Ok(()) + } + + pub(in super::super) fn create_const_parameters(&mut self) -> SpannedEncodingResult<()> { + for parameter in self.type_decl.get_const_parameters() { + self.parameters + .push(parameter.to_pure_snapshot(self.lowerer)?); + } + Ok(()) + } + + pub(in super::super) fn add_precondition( + &mut self, + predicate: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + // self.pres.push(assertion); + assert!( + self.predicate.is_none(), + "precondition already set: {:?}", + self.predicate + ); + let vir_low::Expression::PredicateAccessPredicate(predicate) = predicate else { + unreachable!("Must be a predicate: {predicate}"); + }; + self.predicate = Some(predicate); + Ok(()) + } + + pub(in super::super) fn add_snapshot_postcondition( + &mut self, + assertion: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + self.snapshot_posts.push(assertion); + Ok(()) + } + + pub(in super::super::super) fn add_snapshot_body_postcondition( + &mut self, + assertion: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + self.snapshot_body_posts.push(assertion); + Ok(()) + } + + pub(in super::super) fn array_length_int( + &mut self, + array_length_mid: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult { + let array_length = array_length_mid.to_pure_snapshot(self.lowerer)?; + let size_type_mid = self.lowerer.size_type_mid()?; + self.lowerer + .obtain_constant_value(&size_type_mid, array_length.into(), self.position) + } + + pub(in super::super) fn result_type(&mut self) -> SpannedEncodingResult { + self.ty.to_snapshot(self.lowerer) + } + + pub(in super::super) fn result(&mut self) -> SpannedEncodingResult { + Ok(vir_low::VariableDecl::result_variable(self.result_type()?)) + } + + pub(in super::super) fn add_validity_postcondition(&mut self) -> SpannedEncodingResult<()> { + let result = self.result()?; + let validity = self + .lowerer + .encode_snapshot_valid_call_for_type(result.into(), self.ty)?; + self.add_snapshot_postcondition(validity) + } + + pub(in super::super) fn add_snapshot_len_equal_to_postcondition( + &mut self, + array_length_mid: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let snapshot = self.result()?; + let snapshot_length = self + .lowerer + .obtain_array_len_snapshot(snapshot.into(), self.position)?; + let array_length_int = self.array_length_int(array_length_mid)?; + let expression = expr! { + ([array_length_int] == [snapshot_length]) + }; + self.add_snapshot_postcondition(expression) + } + + pub(in super::super) fn create_field_snap_call( + &mut self, + field: &vir_mid::FieldDecl, + snap_call: impl FnOnce( + &mut Self, + &vir_mid::FieldDecl, + vir_low::Expression, + vir_low::Expression, + ) -> SpannedEncodingResult, + ) -> SpannedEncodingResult { + let field_place = self.lowerer.encode_field_place( + self.ty, + field, + self.place.clone().into(), + self.position, + )?; + let field_address = self.lowerer.encode_field_address( + self.ty, + field, + self.address.clone().into(), + self.position, + )?; + snap_call(self, field, field_place, field_address) + // let target_slice_len = self.slice_len_expression()?; + // self.lowerer.frac_ref_snap( + // CallContext::BuiltinMethod, + // &field.ty, + // &field.ty, + // field_place, + // self.root_address.clone().into(), + // self.reference_lifetime.clone().into(), + // target_slice_len, + // ) + } + + pub(in super::super) fn create_field_snapshot_equality( + &mut self, + field: &vir_mid::FieldDecl, + snap_call: impl FnOnce( + &mut Self, + &vir_mid::FieldDecl, + vir_low::Expression, + vir_low::Expression, + ) -> SpannedEncodingResult, + ) -> SpannedEncodingResult { + use vir_low::macros::*; + let result = self.result()?; + let field_snapshot = self.lowerer.obtain_struct_field_snapshot( + self.ty, + field, + result.into(), + self.position, + )?; + let snap_call = self.create_field_snap_call(field, snap_call)?; + Ok(expr! { + [field_snapshot] == [snap_call] + }) + } + + // pub(in super::super::super) fn add_snapshot_body_postcondition( + // &mut self, + // precondition_predicate: vir_low::Expression, + // body: vir_low::Expression, + // ) -> SpannedEncodingResult<()> { + // let unfolding = precondition_predicate.into_unfolding(body); + // self.add_postcondition(unfolding) + // } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + is_invariant_pure: bool, + predicate_kind: PredicateKind, + snap_call: &impl Fn( + &mut Self, + &vir_mid::FieldDecl, + vir_low::Expression, + vir_low::Expression, + ) -> SpannedEncodingResult, + ) -> SpannedEncodingResult<()> { + if let Some(invariant) = decl.structural_invariant.clone() { + let mut regular_field_arguments = Vec::new(); + for field in &decl.fields { + let field_snap_call = self.create_field_snap_call(field, snap_call)?; + regular_field_arguments.push(field_snap_call); + // regular_field_arguments.push(self.create_field_snap_call(field)?); + } + let result = self.result()?; + let (deref_fields, deref_range_fields) = self + .lowerer + .structural_invariant_to_deref_fields(&invariant)?; + for deref_owned in &deref_fields { + self.owned_snapshot_functions_to_encode + .push(deref_owned.place.get_type().clone()); + } + for deref_range_owned in &deref_range_fields { + self.owned_range_snapshot_functions_to_encode + .push(deref_range_owned.address.get_type().clone()); + } + let mut constructor_encoder = AssertionToSnapshotConstructor::for_function_body( + predicate_kind, + self.ty, + regular_field_arguments, + decl.fields.clone(), + (deref_fields, deref_range_fields), + self.position, + ); + let invariant_expression = invariant.into_iter().conjoin(); + let permission_expression = invariant_expression.convert_into_permission_expression(); + let constructor = constructor_encoder + .expression_to_snapshot_constructor(self.lowerer, &permission_expression)?; + let body = vir_low::Expression::equals(result.into(), constructor); + if !is_invariant_pure { + self.add_snapshot_body_postcondition(body)?; + } else { + self.add_snapshot_postcondition(body)?; + } + } + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/function_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/function_use.rs new file mode 100644 index 00000000000..2ddd0f84e31 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/function_use.rs @@ -0,0 +1,80 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, + lifetimes::LifetimesInterface, + lowerer::Lowerer, + snapshots::{IntoPureSnapshot, IntoSnapshot}, + }, +}; +use vir_crate::{ + common::identifier::WithIdentifier, + low::{self as vir_low}, + middle as vir_mid, + middle::operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, +}; + +pub(in super::super) struct FunctionCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super) lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + pub(in super::super) function_name: &'l str, + pub(in super::super) context: CallContext, + pub(in super::super) ty: &'l vir_mid::Type, + pub(in super::super) generics: &'l G, + pub(in super::super) arguments: Vec, + pub(in super::super) position: vir_low::Position, +} + +impl<'l, 'p, 'v, 'tcx, G> FunctionCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + function_name: &'l str, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + arguments: Vec, + position: vir_low::Position, + ) -> SpannedEncodingResult { + Ok(Self { + lowerer, + function_name, + context, + ty, + generics, + arguments, + position, + }) + } + + pub(in super::super) fn build(self) -> SpannedEncodingResult { + let return_type = self.ty.to_snapshot(self.lowerer)?; + let call = vir_low::Expression::function_call( + format!("{}${}", self.function_name, self.ty.get_identifier()), + self.arguments, + return_type, + ); + Ok(call.set_default_position(self.position)) + } + + pub(in super::super) fn add_lifetime_arguments(&mut self) -> SpannedEncodingResult<()> { + self.arguments.extend( + self.lowerer + .create_lifetime_arguments(self.context, self.generics)?, + ); + Ok(()) + } + + pub(in super::super) fn add_const_arguments(&mut self) -> SpannedEncodingResult<()> { + // FIXME: remove code duplication with other add_const_arguments methods + for argument in self.generics.get_const_arguments() { + self.arguments + .push(argument.to_pure_snapshot(self.lowerer)?); + } + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/mod.rs index ef427252419..6bcb70532ad 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/mod.rs @@ -1,2 +1,4 @@ +pub(super) mod function_decl; +pub(super) mod function_use; pub(super) mod predicate_decl; pub(super) mod predicate_use; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/predicate_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/predicate_decl.rs index ab23d9db1f2..959bd451004 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/predicate_decl.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/predicate_decl.rs @@ -1,17 +1,24 @@ use crate::encoder::{ errors::SpannedEncodingResult, middle::core_proof::{ + addresses::AddressesInterface, builtin_methods::CallContext, lifetimes::LifetimesInterface, lowerer::Lowerer, - predicates::owned::builders::{ - unique_ref::predicate_use::UniqueRefUseBuilder, FracRefUseBuilder, - }, + places::PlacesInterface, + pointers::PointersInterface, + predicates::{PredicatesMemoryBlockInterface, PredicatesOwnedInterface}, references::ReferencesInterface, - snapshots::{IntoPureSnapshot, SnapshotValidityInterface, SnapshotValuesInterface}, + snapshots::{ + IntoPureSnapshot, IntoSnapshot, IntoSnapshotLowerer, PredicateKind, + SelfFramingAssertionToSnapshot, SnapshotBytesInterface, SnapshotValidityInterface, + SnapshotValuesInterface, + }, type_layouts::TypeLayoutsInterface, + types::TypesInterface, }, }; +use prusti_common::config; use vir_crate::{ common::{expression::ExpressionIterator, identifier::WithIdentifier}, low::{self as vir_low}, @@ -26,6 +33,12 @@ pub(in super::super::super) struct PredicateDeclBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super) parameters: Vec, pub(in super::super) conjuncts: Option>, pub(in super::super) position: vir_low::Position, + /// `place` is used by subtypes that cannot be aliased. + pub(in super::super) place: vir_low::VariableDecl, + // /// `address` is used by subtypes that cannot be aliased. + // pub(in super::super) address: vir_low::VariableDecl, + /// `address` is used by subtypes that can be aliased. + pub(in super::super) address: vir_low::VariableDecl, } impl<'l, 'p, 'v, 'tcx> PredicateDeclBuilder<'l, 'p, 'v, 'tcx> { @@ -37,6 +50,9 @@ impl<'l, 'p, 'v, 'tcx> PredicateDeclBuilder<'l, 'p, 'v, 'tcx> { position: vir_low::Position, ) -> SpannedEncodingResult { Ok(Self { + place: vir_low::VariableDecl::new("place", lowerer.place_option_type()?), + // address: vir_low::VariableDecl::new("address", lowerer.address_type()?), + address: vir_low::VariableDecl::new("address", lowerer.address_type()?), ty, predicate_name, type_decl, @@ -50,6 +66,7 @@ impl<'l, 'p, 'v, 'tcx> PredicateDeclBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super) fn build(self) -> vir_low::PredicateDecl { vir_low::PredicateDecl { name: format!("{}${}", self.predicate_name, self.ty.get_identifier()), + kind: vir_low::PredicateKind::Owned, parameters: self.parameters, body: self .conjuncts @@ -89,84 +106,230 @@ impl<'l, 'p, 'v, 'tcx> PredicateDeclBuilder<'l, 'p, 'v, 'tcx> { self.add_conjunct(validity) } + pub(in super::super) fn add_unique_ref_pointer_predicate( + &mut self, + lifetime: &vir_mid::ty::LifetimeConst, + place: vir_low::VariableDecl, + address: vir_low::VariableDecl, + // _snapshot: &vir_low::VariableDecl, + ) -> SpannedEncodingResult { + let lifetime = lifetime.to_pure_snapshot(self.lowerer)?; + // let pointer_type = &self.lowerer.reference_address_type(self.ty)?; + let pointer_type = { + let reference_type = self.type_decl.clone().unwrap_reference(); + vir_mid::Type::pointer(reference_type.target_type) + }; + self.lowerer.ensure_type_definition(&pointer_type)?; + let expression = self.lowerer.unique_ref( + CallContext::BuiltinMethod, + &pointer_type, + &pointer_type, + place.into(), + address.into(), + lifetime.into(), + None, // FIXME + None, + self.position, + )?; + self.add_conjunct(expression)?; + Ok(pointer_type) + } + + /// `is_unique_ref` – whether the predicate is used in `UniqueRef` or `Owned`. pub(in super::super) fn add_unique_ref_target_predicate( &mut self, target_type: &vir_mid::Type, lifetime: &vir_mid::ty::LifetimeConst, - place: &vir_low::VariableDecl, - snapshot: &vir_low::VariableDecl, + place: vir_low::Expression, + address: vir_low::VariableDecl, + // snapshot: &vir_low::VariableDecl, + is_unique_ref: bool, // FIXME: Refactor to not use this flag. ) -> SpannedEncodingResult<()> { use vir_low::macros::*; let deref_place = self .lowerer - .reference_deref_place(place.clone().into(), self.position)?; - let target_address = - self.lowerer - .reference_address(self.ty, snapshot.clone().into(), self.position)?; - let current_snapshot = self.lowerer.reference_target_current_snapshot( - self.ty, - snapshot.clone().into(), - self.position, - )?; - let final_snapshot = self.lowerer.reference_target_final_snapshot( - self.ty, - snapshot.clone().into(), - self.position, - )?; + .reference_deref_place(place.clone(), self.position)?; let lifetime_alive = self .lowerer .encode_lifetime_const_into_pure_is_alive_variable(lifetime)?; let lifetime = lifetime.to_pure_snapshot(self.lowerer)?; - let mut builder = UniqueRefUseBuilder::new( - self.lowerer, + let (target_address, target_len) = if config::use_snapshot_parameters_in_predicates() { + unimplemented!("TODO: Delete this branch"); + // // FIXME: target_len should be the length of the target slice. + // ( + // self.lowerer + // .reference_address(self.ty, snapshot.clone().into(), self.position)?, + // None, + // ) + } else { + let pointer_type = &self.lowerer.reference_address_type(self.ty)?; + let pointer_snapshot = if is_unique_ref { + self.lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + pointer_type, + pointer_type, + place, + address.into(), + lifetime.clone().into(), + None, + false, + self.position, + )? + } else { + self.lowerer + .encode_snapshot_to_bytes_function(pointer_type)?; + let size_of = self + .lowerer + .encode_type_size_expression2(self.ty, self.type_decl)?; + // let compute_address = ty!(Address); + // let compute_address_expression = expr! { + // ComputeAddress::compute_address( + // [place.clone()], + // [address.into()] + // ) + // }; + // let bytes = self + // .lowerer + // .encode_memory_block_bytes_expression(compute_address_expression, size_of)?; + let bytes = self + .lowerer + .encode_memory_block_bytes_expression(address.into(), size_of)?; + let from_bytes = pointer_type.to_snapshot(self.lowerer)?; + expr! { + Snap::from_bytes([bytes]) + } + }; + let target_address = self.lowerer.pointer_address( + pointer_type, + pointer_snapshot.clone(), + self.position, + )?; + // .obtain_constant_value(address_type, pointer_snapshot, self.position)? + + let target_len = if pointer_type.is_pointer_to_slice() { + Some(self.lowerer.pointer_slice_len( + pointer_type, + pointer_snapshot, + self.position, + )?) + } else { + None + }; + (target_address, target_len) + }; + // let current_snapshot = self.lowerer.reference_target_current_snapshot( + // self.ty, + // snapshot.clone().into(), + // self.position, + // )?; + // let final_snapshot = self.lowerer.reference_target_final_snapshot( + // self.ty, + // snapshot.clone().into(), + // self.position, + // )?; + // let final_snapshot = self.lowerer.unique_ref_snap( + // CallContext::BuiltinMethod, + // target_type, + // target_type, + // deref_place.clone(), + // target_address.clone(), + // lifetime.clone().into(), + // target_len.clone(), + // true, + // self.position, + // )?; + let expression = self.lowerer.unique_ref( CallContext::BuiltinMethod, target_type, target_type, deref_place, target_address, - current_snapshot, - final_snapshot, lifetime.into(), + target_len, + None, + self.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let expression = builder.build(); self.add_conjunct(expr! { [lifetime_alive.into()] ==> [expression] }) } + // FIXME: Code duplication with `add_unique_ref_target_predicate`. pub(in super::super) fn add_frac_ref_target_predicate( &mut self, target_type: &vir_mid::Type, lifetime: &vir_mid::ty::LifetimeConst, - place: &vir_low::VariableDecl, - snapshot: &vir_low::VariableDecl, + place: vir_low::Expression, + address: vir_low::VariableDecl, + // snapshot: &vir_low::VariableDecl, ) -> SpannedEncodingResult<()> { - let deref_place = self + use vir_low::macros::*; + let lifetime_alive = self .lowerer - .reference_deref_place(place.clone().into(), self.position)?; + .encode_lifetime_const_into_pure_is_alive_variable(lifetime)?; + let deref_place = self.lowerer.reference_deref_place(place, self.position)?; + let pointer_type = &self.lowerer.reference_address_type(self.ty)?; + let pointer_snapshot = { + self.lowerer + .encode_snapshot_to_bytes_function(pointer_type)?; + let size_of = self + .lowerer + .encode_type_size_expression2(self.ty, self.type_decl)?; + // let compute_address = ty!(Address); + // let compute_address_expression = expr! { + // ComputeAddress::compute_address( + // [place.clone()], + // [address.into()] + // ) + // }; + // let bytes = self + // .lowerer + // .encode_memory_block_bytes_expression(compute_address_expression, size_of)?; + let bytes = self + .lowerer + .encode_memory_block_bytes_expression(address.into(), size_of)?; + let from_bytes = pointer_type.to_snapshot(self.lowerer)?; + expr! { + Snap::from_bytes([bytes]) + } + }; let target_address = self.lowerer - .reference_address(self.ty, snapshot.clone().into(), self.position)?; - let current_snapshot = self.lowerer.reference_target_current_snapshot( - self.ty, - snapshot.clone().into(), - self.position, - )?; + .pointer_address(pointer_type, pointer_snapshot, self.position)?; + // let target_address = + // self.lowerer + // .reference_address(self.ty, snapshot.clone().into(), self.position)?; + // let current_snapshot = self.lowerer.reference_target_current_snapshot( + // self.ty, + // snapshot.clone().into(), + // self.position, + // )?; let lifetime = lifetime.to_pure_snapshot(self.lowerer)?; - let mut builder = FracRefUseBuilder::new( - self.lowerer, + // let mut builder = FracRefUseBuilder::new( + // self.lowerer, + // CallContext::BuiltinMethod, + // target_type, + // target_type, + // deref_place, + // target_address, + // // current_snapshot, + // lifetime.into(), + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // let expression = builder.build(); + // self.add_conjunct(expression); + let target_len = None; // FIXME + let expression = self.lowerer.frac_ref( CallContext::BuiltinMethod, target_type, target_type, deref_place, target_address, - current_snapshot, lifetime.into(), + target_len, + None, + self.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let expression = builder.build(); - self.add_conjunct(expression) + self.add_conjunct(expr! { [lifetime_alive.into()] ==> [expression] }) } pub(in super::super) fn array_length_int( @@ -194,6 +357,28 @@ impl<'l, 'p, 'v, 'tcx> PredicateDeclBuilder<'l, 'p, 'v, 'tcx> { }; self.add_conjunct(expression) } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + predicate_kind: PredicateKind, + ) -> SpannedEncodingResult> { + if let Some(invariant) = &decl.structural_invariant { + let mut encoder = SelfFramingAssertionToSnapshot::for_predicate_body( + self.place.clone(), + self.address.clone(), + predicate_kind, + ); + for assertion in invariant { + let low_assertion = + encoder.expression_to_snapshot(self.lowerer, assertion, true)?; + self.add_conjunct(low_assertion)?; + } + Ok(encoder.into_created_predicate_types()) + } else { + Ok(Vec::new()) + } + } } pub(in super::super::super) trait PredicateDeclBuilderMethods<'l, 'p, 'v, 'tcx> diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/predicate_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/predicate_use.rs index 6eaa9dc1e77..1cd108629ff 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/predicate_use.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/common/predicate_use.rs @@ -1,8 +1,8 @@ use crate::encoder::{ errors::SpannedEncodingResult, middle::core_proof::{ - builtin_methods::CallContext, lifetimes::LifetimesInterface, lowerer::Lowerer, - snapshots::IntoPureSnapshot, + builtin_methods::CallContext, const_generics::ConstGenericsInterface, + lifetimes::LifetimesInterface, lowerer::Lowerer, }, }; use vir_crate::{ @@ -53,7 +53,7 @@ where pub(in super::super) fn build(self) -> vir_low::Expression { vir_low::Expression::predicate_access_predicate( - format!("{}${}", self.predicate_name, self.ty.get_identifier()), + self.predicate_name(), self.arguments, self.permission_amount .unwrap_or_else(vir_low::Expression::full_permission), @@ -61,6 +61,10 @@ where ) } + pub(in super::super) fn predicate_name(&self) -> String { + format!("{}${}", self.predicate_name, self.ty.get_identifier()) + } + pub(in super::super) fn add_lifetime_arguments(&mut self) -> SpannedEncodingResult<()> { self.arguments.extend( self.lowerer @@ -70,11 +74,15 @@ where } pub(in super::super) fn add_const_arguments(&mut self) -> SpannedEncodingResult<()> { - // FIXME: remove code duplication with other add_const_arguments methods - for argument in self.generics.get_const_arguments() { - self.arguments - .push(argument.to_pure_snapshot(self.lowerer)?); - } + // // FIXME: remove code duplication with other add_const_arguments methods + // for argument in self.generics.get_const_arguments() { + // self.arguments + // .push(argument.to_pure_snapshot(self.lowerer)?); + // } + self.arguments.extend( + self.lowerer + .create_const_arguments(self.context, self.generics)?, + ); Ok(()) } diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/function_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/function_decl.rs new file mode 100644 index 00000000000..a792353ff2c --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/function_decl.rs @@ -0,0 +1,318 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, + lifetimes::LifetimesInterface, + lowerer::Lowerer, + predicates::{ + owned::builders::common::function_decl::FunctionDeclBuilder, PredicatesOwnedInterface, + }, + snapshots::{IntoPureSnapshot, PredicateKind}, + type_layouts::TypeLayoutsInterface, + }, +}; +use vir_crate::{ + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super) struct FracRefSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + inner: FunctionDeclBuilder<'l, 'p, 'v, 'tcx>, + // place: vir_low::VariableDecl, + // root_address: vir_low::VariableDecl, + reference_lifetime: vir_low::VariableDecl, + slice_len: Option, +} + +impl<'l, 'p, 'v, 'tcx> FracRefSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let slice_len = if ty.is_slice() { + Some(vir_mid::VariableDecl::new( + "slice_len", + lowerer.size_type_mid()?, + )) + } else { + None + }; + Ok(Self { + // place: vir_low::VariableDecl::new("place", lowerer.place_type()?), + // root_address: vir_low::VariableDecl::new("root_address", lowerer.address_type()?), + reference_lifetime: vir_low::VariableDecl::new( + "reference_lifetime", + lowerer.lifetime_type()?, + ), + slice_len, + inner: FunctionDeclBuilder::new( + lowerer, + "snap_current_frac_ref", + ty, + type_decl, + Default::default(), + )?, + }) + } + + pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.inner.place.clone()); + self.inner.parameters.push(self.inner.address.clone()); + self.inner.parameters.push(self.reference_lifetime.clone()); + self.inner.create_lifetime_parameters()?; + if let Some(slice_len) = self.slice_len()? { + self.inner.parameters.push(slice_len); + } + self.inner.create_const_parameters()?; + Ok(()) + } + + pub(in super::super::super) fn add_frac_ref_precondition( + &mut self, + ) -> SpannedEncodingResult<()> { + let predicate = self.precondition_predicate()?; + self.inner.add_precondition(predicate) + } + + // FIXME: Code duplication. + fn slice_len(&mut self) -> SpannedEncodingResult> { + self.slice_len + .as_ref() + .map(|slice_len_mid| slice_len_mid.to_pure_snapshot(self.inner.lowerer)) + .transpose() + } + + // FIXME: Code duplication. + fn slice_len_expression(&mut self) -> SpannedEncodingResult> { + Ok(self.slice_len()?.map(|slice_len| slice_len.into())) + } + + fn precondition_predicate(&mut self) -> SpannedEncodingResult { + self.frac_ref_predicate( + self.inner.ty, + self.inner.type_decl, + self.inner.place.clone().into(), + self.inner.address.clone().into(), + self.reference_lifetime.clone().into(), + ) + } + + fn frac_ref_predicate( + &mut self, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + reference_lifetime: vir_low::Expression, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let slice_len = if let Some(slice_len_mid) = &self.slice_len { + let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; + Some(slice_len.into()) + } else { + None + }; + // let mut builder = FracRefUseBuilder::new( + // self.inner.lowerer, + // CallContext::BuiltinMethod, + // ty, + // generics, + // place, + // address, + // reference_lifetime, + // // slice_len, + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // builder.set_maybe_permission_amount(Some(vir_low::Expression::wildcard_permission()))?; + // builder.build() + self.inner.lowerer.frac_ref( + CallContext::BuiltinMethod, + ty, + generics, + place, + address, + reference_lifetime, + slice_len, + Some(vir_low::Expression::wildcard_permission()), + self.inner.position, + ) + } + + pub(in super::super::super) fn get_snapshot_postconditions( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_postconditions() + } + + pub(in super::super::super) fn get_snapshot_body( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_body() + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult { + self.inner.build() + } + + // // FIXME: Code duplication. + // fn create_field_snap_call( + // &mut self, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult { + // let field_place = self.inner.lowerer.encode_field_place( + // self.inner.ty, + // field, + // self.inner.place.clone().into(), + // self.inner.position, + // )?; + // let target_slice_len = self.slice_len_expression()?; + // self.inner.lowerer.frac_ref_snap( + // CallContext::BuiltinMethod, + // &field.ty, + // &field.ty, + // field_place, + // self.root_address.clone().into(), + // self.reference_lifetime.clone().into(), + // target_slice_len, + // ) + // } + + // FIXME: Code duplication. + pub(in super::super::super) fn create_field_snapshot_equality( + &mut self, + field: &vir_mid::FieldDecl, + ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let result = self.inner.result()?; + // let field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + // self.inner.ty, + // field, + // result.into(), + // self.inner.position, + // )?; + // let snap_call = self.create_field_snap_call(&field)?; + // Ok(expr! { + // [field_snapshot] == [snap_call] + // }) + // self.inner.create_field_snap_call(field, |builder, field, field_place| { + // let target_slice_len = self.slice_len_expression()?; + // self.inner.lowerer.frac_ref_snap( + // CallContext::BuiltinMethod, + // &field.ty, + // &field.ty, + // field_place, + // self.root_address.clone().into(), + // self.reference_lifetime.clone().into(), + // target_slice_len, + // ) + // }) + let frac_ref_call = self.field_frac_ref_snap()?; + self.inner + .create_field_snapshot_equality(field, frac_ref_call) + } + + fn field_frac_ref_snap( + &mut self, + ) -> SpannedEncodingResult< + impl Fn( + &mut FunctionDeclBuilder, + &vir_mid::FieldDecl, + vir_low::Expression, + vir_low::Expression, + ) -> SpannedEncodingResult, + > { + let target_slice_len = self.slice_len_expression()?; + // let root_address: vir_low::Expression = self.root_address.clone().into(); + // let root_address = std::rc::Rc::new(root_address); + let lifetime: vir_low::Expression = self.reference_lifetime.clone().into(); + let lifetime = std::rc::Rc::new(lifetime); + Ok( + move |builder: &mut FunctionDeclBuilder, + field: &vir_mid::FieldDecl, + field_place, + field_address| { + builder.lowerer.frac_ref_snap( + CallContext::BuiltinMethod, + &field.ty, + &field.ty, + field_place, + field_address, + (*lifetime).clone(), + target_slice_len.clone(), + builder.position, + ) + }, + ) + } + + // FIXME: Code duplication. + pub(in super::super::super) fn add_snapshot_body_postcondition( + &mut self, + body: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + // let predicate = self.precondition_predicate()?; + // let unfolding = predicate.into_unfolding(body); + // self.inner.add_postcondition(unfolding) + self.inner.add_snapshot_body_postcondition(body) + } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult<()> { + // let precondition_predicate = self.precondition_predicate()?; + let predicate_kind = PredicateKind::FracRef { + lifetime: self.reference_lifetime.clone().into(), + }; + let snap_call = self.field_frac_ref_snap()?; + self.inner + .add_structural_invariant(decl, false, predicate_kind, &snap_call) + } + + // // FIXME: Code duplication. + // pub(in super::super::super) fn add_structural_invariant2( + // &mut self, + // decl: &vir_mid::type_decl::Struct, + // ) -> SpannedEncodingResult<()> { + // if let Some(invariant) = decl.structural_invariant.clone() { + // let mut regular_field_arguments = Vec::new(); + // for field in &decl.fields { + // let frac_ref_call = self.field_frac_ref_snap()?; + // let snap_call = self.inner.create_field_snap_call(field, frac_ref_call)?; + // regular_field_arguments.push(snap_call); + // // regular_field_arguments.push(self.create_field_snap_call(field)?); + // } + // let result = self.inner.result()?; + // let deref_fields = self + // .inner + // .lowerer + // .structural_invariant_to_deref_fields(&invariant)?; + // let mut constructor_encoder = AssertionToSnapshotConstructor::for_function_body( + // PredicateKind::FracRef { + // lifetime: self.reference_lifetime.clone().into(), + // }, + // self.inner.ty, + // regular_field_arguments, + // decl.fields.clone(), + // deref_fields, + // self.inner.position, + // ); + // let invariant_expression = invariant.into_iter().conjoin(); + // let permission_expression = invariant_expression.convert_into_permission_expression(); + // let constructor = constructor_encoder + // .expression_to_snapshot_constructor(self.inner.lowerer, &permission_expression)?; + // self.add_unfolding_postcondition(vir_low::Expression::equals( + // result.into(), + // constructor, + // ))?; + // } + // Ok(()) + // } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/function_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/function_use.rs new file mode 100644 index 00000000000..9d511e04e05 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/function_use.rs @@ -0,0 +1,65 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, lowerer::Lowerer, + predicates::owned::builders::common::function_use::FunctionCallBuilder, + }, +}; +use vir_crate::{ + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super) struct FracRefSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + inner: FunctionCallBuilder<'l, 'p, 'v, 'tcx, G>, +} + +impl<'l, 'p, 'v, 'tcx, G> FracRefSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + place: vir_low::Expression, + root_address: vir_low::Expression, + reference_lifetime: vir_low::Expression, + target_slice_len: Option, + ) -> SpannedEncodingResult { + let mut arguments = vec![place, root_address, reference_lifetime]; + if let Some(len) = target_slice_len { + arguments.push(len); + } + let name = "snap_current_frac_ref"; + let inner = FunctionCallBuilder::new( + lowerer, + name, + context, + ty, + generics, + arguments, + Default::default(), + )?; + Ok(Self { inner }) + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult { + self.inner.build() + } + + pub(in super::super::super) fn add_lifetime_arguments(&mut self) -> SpannedEncodingResult<()> { + self.inner.add_lifetime_arguments() + } + + pub(in super::super::super) fn add_const_arguments(&mut self) -> SpannedEncodingResult<()> { + self.inner.add_const_arguments() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/mod.rs index ef427252419..7714b35a296 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/mod.rs @@ -1,2 +1,4 @@ pub(super) mod predicate_decl; pub(super) mod predicate_use; +pub(super) mod function_decl; +pub(super) mod function_use; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/predicate_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/predicate_decl.rs index 1a6745c0f26..9980510f744 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/predicate_decl.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/predicate_decl.rs @@ -6,11 +6,15 @@ use crate::encoder::{ lifetimes::LifetimesInterface, lowerer::Lowerer, places::PlacesInterface, - predicates::owned::builders::{ - common::predicate_decl::PredicateDeclBuilder, PredicateDeclBuilderMethods, + predicates::{ + owned::builders::{ + common::predicate_decl::PredicateDeclBuilder, PredicateDeclBuilderMethods, + }, + PredicatesOwnedInterface, }, snapshots::{ - IntoPureSnapshot, IntoSnapshot, SnapshotValidityInterface, SnapshotValuesInterface, + IntoPureSnapshot, IntoSnapshot, PredicateKind, SnapshotValidityInterface, + SnapshotValuesInterface, }, type_layouts::TypeLayoutsInterface, }, @@ -21,12 +25,10 @@ use vir_crate::{ middle as vir_mid, }; -use super::predicate_use::FracRefUseBuilder; - pub(in super::super::super) struct FracRefBuilder<'l, 'p, 'v, 'tcx> { inner: PredicateDeclBuilder<'l, 'p, 'v, 'tcx>, - place: vir_low::VariableDecl, - root_address: vir_low::VariableDecl, + // place: vir_low::VariableDecl, + // address: vir_low::VariableDecl, current_snapshot: vir_low::VariableDecl, reference_lifetime: vir_low::VariableDecl, slice_len: Option, @@ -55,8 +57,8 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { None }; Ok(Self { - place: vir_low::VariableDecl::new("place", lowerer.place_type()?), - root_address: vir_low::VariableDecl::new("root_address", lowerer.address_type()?), + // place: vir_low::VariableDecl::new("place", lowerer.place_type()?), + // address: vir_low::VariableDecl::new("address", lowerer.address_type()?), current_snapshot: vir_low::VariableDecl::new( "current_snapshot", ty.to_snapshot(lowerer)?, @@ -68,7 +70,7 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { slice_len, inner: PredicateDeclBuilder::new( lowerer, - "FracRef2", + "FracRef", ty, type_decl, Default::default(), @@ -81,9 +83,9 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { } pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { - self.inner.parameters.push(self.place.clone()); - self.inner.parameters.push(self.root_address.clone()); - self.inner.parameters.push(self.current_snapshot.clone()); + self.inner.parameters.push(self.inner.place.clone()); + self.inner.parameters.push(self.inner.address.clone()); + // self.inner.parameters.push(self.current_snapshot.clone()); self.inner.parameters.push(self.reference_lifetime.clone()); self.inner.create_lifetime_parameters()?; if let Some(slice_len_mid) = &self.slice_len { @@ -105,28 +107,46 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { let field_place = self.inner.lowerer.encode_field_place( self.inner.ty, field, - self.place.clone().into(), + self.inner.place.clone().into(), self.inner.position, )?; - let current_field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + let field_address = self.inner.lowerer.encode_field_address( self.inner.ty, field, - self.current_snapshot.clone().into(), - Default::default(), + self.inner.address.clone().into(), + self.inner.position, )?; - let mut builder = FracRefUseBuilder::new( - self.inner.lowerer, + // let current_field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + // self.inner.ty, + // field, + // self.current_snapshot.clone().into(), + // Default::default(), + // )?; + // let mut builder = FracRefUseBuilder::new( + // self.inner.lowerer, + // CallContext::BuiltinMethod, + // &field.ty, + // &field.ty, + // field_place, + // self.inner.address.clone().into(), + // // current_field_snapshot, + // self.reference_lifetime.clone().into(), + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // let expression = builder.build(); + let TODO_target_slice_len = None; + let expression = self.inner.lowerer.frac_ref( CallContext::BuiltinMethod, &field.ty, &field.ty, field_place, - self.root_address.clone().into(), - current_field_snapshot, + field_address, self.reference_lifetime.clone().into(), + TODO_target_slice_len, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let expression = builder.build(); self.inner.add_conjunct(expression) } @@ -138,30 +158,47 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { let discriminant_place = self.inner.lowerer.encode_field_place( self.inner.ty, &discriminant_field, - self.place.clone().into(), + self.inner.place.clone().into(), self.inner.position, )?; - let current_discriminant_call = self.inner.lowerer.obtain_enum_discriminant( - self.current_snapshot.clone().into(), + let discriminant_address = self.inner.lowerer.encode_field_address( self.inner.ty, + &discriminant_field, + self.inner.address.clone().into(), self.inner.position, )?; - let current_discriminant_snapshot = self.inner.lowerer.construct_constant_snapshot( - &decl.discriminant_type, - current_discriminant_call, - self.inner.position, - )?; - let builder = FracRefUseBuilder::new( - self.inner.lowerer, + // let current_discriminant_call = self.inner.lowerer.obtain_enum_discriminant( + // self.current_snapshot.clone().into(), + // self.inner.ty, + // self.inner.position, + // )?; + // let current_discriminant_snapshot = self.inner.lowerer.construct_constant_snapshot( + // &decl.discriminant_type, + // current_discriminant_call, + // self.inner.position, + // )?; + // let builder = FracRefUseBuilder::new( + // self.inner.lowerer, + // CallContext::BuiltinMethod, + // &decl.discriminant_type, + // &decl.discriminant_type, + // discriminant_place, + // self.inner.address.clone().into(), + // // current_discriminant_snapshot, + // self.reference_lifetime.clone().into(), + // )?; + // let expression = builder.build(); + let expression = self.inner.lowerer.frac_ref( CallContext::BuiltinMethod, &decl.discriminant_type, &decl.discriminant_type, discriminant_place, - self.root_address.clone().into(), - current_discriminant_snapshot, + discriminant_address, self.reference_lifetime.clone().into(), + None, + None, + self.inner.position, )?; - let expression = builder.build(); self.inner.add_conjunct(expression) } @@ -170,11 +207,14 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { target_type: &vir_mid::Type, lifetime: &vir_mid::ty::LifetimeConst, ) -> SpannedEncodingResult<()> { + let place = self.inner.place.clone(); + let address = self.inner.address.clone(); self.inner.add_frac_ref_target_predicate( target_type, lifetime, - &self.place, - &self.current_snapshot, + place.into(), + address, + // &self.current_snapshot, ) } @@ -216,28 +256,46 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { let array_length_int = self.inner.array_length_int(array_length_mid)?; let element_place = self.inner.lowerer.encode_index_place( self.inner.ty, - self.place.clone().into(), + self.inner.place.clone().into(), index.clone().into(), self.inner.position, )?; - let current_element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( - self.current_snapshot.clone().into(), - index_int.clone(), + let element_address = self.inner.lowerer.encode_index_address( + self.inner.ty, + self.inner.address.clone().into(), + index.clone().into(), self.inner.position, )?; - let mut builder = FracRefUseBuilder::new( - self.inner.lowerer, + // let current_element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( + // self.current_snapshot.clone().into(), + // index_int.clone(), + // self.inner.position, + // )?; + // let mut builder = FracRefUseBuilder::new( + // self.inner.lowerer, + // CallContext::BuiltinMethod, + // element_type, + // element_type, + // element_place, + // self.inner.address.clone().into(), + // // current_element_snapshot, + // self.reference_lifetime.clone().into(), + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // let element_predicate_acc = builder.build(); + let TODO_target_slice_len = None; + let element_predicate_acc = self.inner.lowerer.frac_ref( CallContext::BuiltinMethod, element_type, element_type, element_place, - self.root_address.clone().into(), - current_element_snapshot, + element_address, self.reference_lifetime.clone().into(), + TODO_target_slice_len, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let element_predicate_acc = builder.build(); let elements = vir_low::Expression::forall( vec![index], vec![vir_low::Trigger::new(vec![element_predicate_acc.clone()])], @@ -268,28 +326,46 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { let variant_place = self.inner.lowerer.encode_enum_variant_place( self.inner.ty, &variant_index, - self.place.clone().into(), + self.inner.place.clone().into(), self.inner.position, )?; - let current_variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + let variant_address = self.inner.lowerer.encode_enum_variant_address( self.inner.ty, &variant_index, - self.current_snapshot.clone().into(), + self.inner.address.clone().into(), self.inner.position, )?; - let mut builder = FracRefUseBuilder::new( - self.inner.lowerer, + // let current_variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + // self.inner.ty, + // &variant_index, + // self.current_snapshot.clone().into(), + // self.inner.position, + // )?; + // let mut builder = FracRefUseBuilder::new( + // self.inner.lowerer, + // CallContext::BuiltinMethod, + // variant_type, + // variant_type, + // variant_place, + // self.inner.address.clone().into(), + // // current_variant_snapshot, + // self.reference_lifetime.clone().into(), + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // let predicate = builder.build(); + let TODO_target_slice_len = None; + let predicate = self.inner.lowerer.frac_ref( CallContext::BuiltinMethod, variant_type, variant_type, variant_place, - self.root_address.clone().into(), - current_variant_snapshot, + variant_address, self.reference_lifetime.clone().into(), + TODO_target_slice_len, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let predicate = builder.build(); Ok((guard, predicate)) } @@ -300,4 +376,39 @@ impl<'l, 'p, 'v, 'tcx> FracRefBuilder<'l, 'p, 'v, 'tcx> { self.inner .add_conjunct(variant_predicates.into_iter().create_match()) } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult> { + self.inner.add_structural_invariant( + decl, + PredicateKind::FracRef { + lifetime: self.reference_lifetime.clone().into(), + }, + ) + } + + // pub(in super::super::super) fn add_structural_invariant( + // &mut self, + // decl: &vir_mid::type_decl::Struct, + // ) -> SpannedEncodingResult> { + // if let Some(invariant) = &decl.structural_invariant { + // let mut encoder = SelfFramingAssertionToSnapshot::for_predicate_body( + // self.inner.place.clone(), + // self.inner.address.clone(), + // PredicateKind::FracRef { + // lifetime: self.reference_lifetime.clone().into(), + // }, + // ); + // for assertion in invariant { + // let low_assertion = + // encoder.expression_to_snapshot(self.inner.lowerer, assertion, true)?; + // self.inner.add_conjunct(low_assertion)?; + // } + // Ok(encoder.into_created_predicate_types()) + // } else { + // Ok(Vec::new()) + // } + // } } diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/predicate_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/predicate_use.rs index 04f8bb3dd27..94e101eb93a 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/predicate_use.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/frac_ref/predicate_use.rs @@ -3,7 +3,6 @@ use crate::encoder::{ middle::core_proof::{ builtin_methods::CallContext, lowerer::Lowerer, predicates::owned::builders::common::predicate_use::PredicateUseBuilder, - snapshots::SnapshotValuesInterface, type_layouts::TypeLayoutsInterface, }, }; use vir_crate::{ @@ -19,7 +18,7 @@ where G: WithLifetimes + WithConstArguments, { inner: PredicateUseBuilder<'l, 'p, 'v, 'tcx, G>, - current_snapshot: vir_low::Expression, + target_slice_len: Option, } impl<'l, 'p, 'v, 'tcx, G> FracRefUseBuilder<'l, 'p, 'v, 'tcx, G> @@ -33,27 +32,28 @@ where ty: &'l vir_mid::Type, generics: &'l G, place: vir_low::Expression, - root_address: vir_low::Expression, - current_snapshot: vir_low::Expression, + address: vir_low::Expression, lifetime: vir_low::Expression, + target_slice_len: Option, + position: vir_low::Position, ) -> SpannedEncodingResult { + let mut arguments = vec![place, address, lifetime]; + if let Some(len) = target_slice_len.clone() { + arguments.push(len); + } let inner = PredicateUseBuilder::new( - lowerer, - "FracRef2", - context, - ty, - generics, - vec![place, root_address, current_snapshot.clone(), lifetime], - Default::default(), + lowerer, "FracRef", context, ty, generics, arguments, position, )?; Ok(Self { inner, - current_snapshot, + target_slice_len, }) } - pub(in super::super::super::super::super) fn build(self) -> vir_low::Expression { - self.inner.build() + pub(in super::super::super::super::super) fn build( + self, + ) -> SpannedEncodingResult { + Ok(self.inner.build()) } pub(in super::super::super::super::super) fn add_lifetime_arguments( @@ -66,18 +66,26 @@ where &mut self, ) -> SpannedEncodingResult<()> { if self.inner.ty.is_slice() { - let snapshot_length = self - .inner - .lowerer - .obtain_array_len_snapshot(self.current_snapshot.clone(), self.inner.position)?; - let size_type = self.inner.lowerer.size_type_mid()?; - let argument = self.inner.lowerer.construct_constant_snapshot( - &size_type, - snapshot_length, - self.inner.position, - )?; - self.inner.arguments.push(argument); + unimplemented!(); + // let snapshot_length = self + // .inner + // .lowerer + // .obtain_array_len_snapshot(self.current_snapshot.clone(), self.inner.position)?; + // let size_type = self.inner.lowerer.size_type_mid()?; + // let argument = self.inner.lowerer.construct_constant_snapshot( + // &size_type, + // snapshot_length, + // self.inner.position, + // )?; + // self.inner.arguments.push(argument); } self.inner.add_const_arguments() } + + pub(in super::super::super::super::super) fn set_maybe_permission_amount( + &mut self, + permission_amount: Option, + ) -> SpannedEncodingResult<()> { + self.inner.set_maybe_permission_amount(permission_amount) + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/mod.rs index f271de25200..fde005b0128 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/mod.rs @@ -1,15 +1,39 @@ mod common; mod frac_ref; mod owned_non_aliased; +mod owned_aliased; mod unique_ref; pub(super) use self::{ - common::predicate_decl::PredicateDeclBuilderMethods, frac_ref::predicate_decl::FracRefBuilder, - owned_non_aliased::predicate_decl::OwnedNonAliasedBuilder, - unique_ref::predicate_decl::UniqueRefBuilder, + common::predicate_decl::PredicateDeclBuilderMethods, + frac_ref::{ + function_decl::FracRefSnapFunctionBuilder, function_use::FracRefSnapCallBuilder, + predicate_decl::FracRefBuilder, + }, + owned_aliased::{ + function_decl::OwnedAliasedSnapFunctionBuilder, + function_range_decl::OwnedAliasedRangeSnapFunctionBuilder, + predicate_decl::OwnedAliasedBuilder, + }, + owned_non_aliased::{ + function_decl::OwnedNonAliasedSnapFunctionBuilder, predicate_decl::OwnedNonAliasedBuilder, + }, + unique_ref::{ + function_current_decl::UniqueRefCurrentSnapFunctionBuilder, + function_current_use::UniqueRefCurrentSnapCallBuilder, + function_final_decl::UniqueRefFinalSnapFunctionBuilder, + function_final_use::UniqueRefFinalSnapCallBuilder, predicate_decl::UniqueRefBuilder, + }, }; pub(in super::super::super) use self::{ frac_ref::predicate_use::FracRefUseBuilder, - owned_non_aliased::predicate_use::OwnedNonAliasedUseBuilder, + owned_aliased::{ + function_range_use::OwnedAliasedRangeSnapCallBuilder, + function_use::OwnedAliasedSnapCallBuilder, + predicate_range_use::OwnedAliasedRangeUseBuilder, + }, + owned_non_aliased::{ + function_use::OwnedNonAliasedSnapCallBuilder, predicate_use::OwnedNonAliasedUseBuilder, + }, unique_ref::predicate_use::UniqueRefUseBuilder, }; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_decl.rs new file mode 100644 index 00000000000..aa163c4bfce --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_decl.rs @@ -0,0 +1,550 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + lifetimes::LifetimesInterface, + lowerer::Lowerer, + places::PlacesInterface, + predicates::{ + owned::builders::common::function_decl::FunctionDeclBuilder, + PredicatesMemoryBlockInterface, PredicatesOwnedInterface, + }, + references::ReferencesInterface, + snapshots::{ + IntoPureSnapshot, PredicateKind, SnapshotBytesInterface, SnapshotValidityInterface, + SnapshotValuesInterface, + }, + type_layouts::TypeLayoutsInterface, + }, +}; + +use vir_crate::{ + common::{expression::QuantifierHelpers, position::Positioned}, + low::{self as vir_low}, + middle::{self as vir_mid}, +}; + +pub(in super::super::super) struct OwnedAliasedSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + inner: FunctionDeclBuilder<'l, 'p, 'v, 'tcx>, + // address: vir_low::VariableDecl, + slice_len: Option, +} + +impl<'l, 'p, 'v, 'tcx> OwnedAliasedSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super) fn new( + _lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + _ty: &'l vir_mid::Type, + _type_decl: &'l vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + unimplemented!(); + // let slice_len = if ty.is_slice() { + // Some(vir_mid::VariableDecl::new( + // "slice_len", + // lowerer.size_type_mid()?, + // )) + // } else { + // None + // }; + // Ok(Self { + // // address: vir_low::VariableDecl::new("address", lowerer.address_type()?), + // slice_len, + // inner: FunctionDeclBuilder::new( + // lowerer, + // "snap_owned_aliased", + // ty, + // type_decl, + // Default::default(), + // )?, + // }) + } + + pub(in super::super::super) fn get_snapshot_postconditions( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_postconditions() + } + + pub(in super::super::super) fn get_snapshot_body( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_body() + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult { + self.inner.build() + } + + pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.inner.address.clone()); + self.inner.create_lifetime_parameters()?; + if let Some(slice_len_mid) = &self.slice_len { + let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; + self.inner.parameters.push(slice_len); + } + self.inner.create_const_parameters()?; + Ok(()) + } + + // FIXME: Code duplication. + pub(in super::super::super) fn get_slice_len( + &self, + ) -> SpannedEncodingResult { + Ok(self.slice_len.as_ref().unwrap().clone()) + } + + // fn owned_predicate( + // &mut self, + // ty: &vir_mid::Type, + // generics: &G, + // address: vir_low::Expression, + // ) -> SpannedEncodingResult + // where + // G: WithLifetimes + WithConstArguments, + // { + // let mut builder = OwnedNonAliasedUseBuilder::new( + // self.inner.lowerer, + // CallContext::BuiltinMethod, + // ty, + // generics, + // place, + // root_address, + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // builder.build() + // } + + // FIXME: Code duplication with add_quantified_permission. + pub(in super::super::super) fn add_quantifiers( + &mut self, + array_length_mid: &vir_mid::VariableDecl, + element_type: &vir_mid::Type, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let size_type_mid = self.inner.lowerer.size_type_mid()?; + var_decls! { + index_int: Int + }; + let index = self.inner.lowerer.construct_constant_snapshot( + &size_type_mid, + index_int.clone().into(), + self.inner.position, + )?; + let index_validity = self + .inner + .lowerer + .encode_snapshot_valid_call_for_type(index.clone(), &size_type_mid)?; + let array_length_int = self.inner.array_length_int(array_length_mid)?; + let element_address = self.inner.lowerer.encode_index_address( + self.inner.ty, + self.inner.address.clone().into(), + index, + self.inner.position, + )?; + let element_predicate_acc = { + self.inner.lowerer.owned_aliased( + CallContext::BuiltinMethod, + element_type, + element_type, + element_address.clone(), + None, + self.inner.position, + )? + }; + let result = self.inner.result()?.into(); + let element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( + result, + index_int.clone().into(), + self.inner.position, + )?; + let element_snap_call = self.inner.lowerer.owned_aliased_snap( + CallContext::BuiltinMethod, + element_type, + element_type, + element_address, + self.inner.position, + )?; + let elements = vir_low::Expression::forall( + vec![index_int.clone()], + vec![vir_low::Trigger::new(vec![element_predicate_acc])], + expr! { + ([index_validity] && (index_int < [array_length_int])) ==> + ([element_snapshot] == [element_snap_call]) + }, + ); + self.add_snapshot_body_postcondition(elements) + } + + pub(in super::super::super) fn add_snapshot_body_postcondition( + &mut self, + body: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + // let predicate = self.precondition_predicate()?; + // let unfolding = predicate.into_unfolding(body); + // self.inner.add_postcondition(unfolding) + self.inner.add_snapshot_body_postcondition(body) + } + + pub(in super::super::super) fn add_validity_postcondition( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_validity_postcondition() + } + + pub(in super::super::super) fn add_snapshot_len_equal_to_postcondition( + &mut self, + array_length_mid: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult<()> { + self.inner + .add_snapshot_len_equal_to_postcondition(array_length_mid) + } + + pub(in super::super::super) fn add_owned_precondition(&mut self) -> SpannedEncodingResult<()> { + let predicate = self.precondition_predicate()?; + self.inner.add_precondition(predicate) + } + + fn precondition_predicate(&mut self) -> SpannedEncodingResult { + self.inner.lowerer.owned_aliased( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + self.inner.address.clone().into(), + Some(vir_low::Expression::wildcard_permission()), + self.inner.position, + ) + } + + // fn compute_address(&self) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let compute_address = ty!(Address); + // let expression = expr! { + // ComputeAddress::compute_address( + // [self.place.clone().into()], + // [self.root_address.clone().into()] + // ) + // }; + // Ok(expression) + // } + + fn size_of(&mut self) -> SpannedEncodingResult { + self.inner + .lowerer + .encode_type_size_expression2(self.inner.ty, self.inner.type_decl) + } + + // FIXME: Code duplication. + fn add_bytes_snapshot_equality_with( + &mut self, + snap_ty: &vir_mid::Type, + snapshot: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let size_of = self.size_of()?; + let bytes = self + .inner + .lowerer + .encode_memory_block_bytes_expression(self.inner.address.clone().into(), size_of)?; + let to_bytes = ty! { Bytes }; + let expression = expr! { + [bytes] == (Snap::to_bytes([snapshot])) + }; + self.add_snapshot_body_postcondition(expression) + } + + pub(in super::super::super) fn add_bytes_snapshot_equality( + &mut self, + ) -> SpannedEncodingResult<()> { + let result = self.inner.result()?.into(); + self.add_bytes_snapshot_equality_with(self.inner.ty, result) + } + + pub(in super::super::super) fn add_bytes_address_snapshot_equality( + &mut self, + ) -> SpannedEncodingResult<()> { + let result = self.inner.result()?.into(); + let address_type = self.inner.lowerer.reference_address_type(self.inner.ty)?; + self.inner + .lowerer + .encode_snapshot_to_bytes_function(&address_type)?; + let target_address_snapshot = self.inner.lowerer.reference_address_snapshot( + self.inner.ty, + result, + self.inner.position, + )?; + self.add_bytes_snapshot_equality_with(&address_type, target_address_snapshot) + } + + // // fn create_field_snap_call( + // // &mut self, + // // field: &vir_mid::FieldDecl, + // // ) -> SpannedEncodingResult { + // // let field_place = self.inner.lowerer.encode_field_place( + // // self.inner.ty, + // // field, + // // self.place.clone().into(), + // // self.inner.position, + // // )?; + // // self.inner.lowerer.owned_non_aliased_snap( + // // CallContext::BuiltinMethod, + // // &field.ty, + // // &field.ty, + // // field_place, + // // self.root_address.clone().into(), + // // self.inner.position, + // // ) + // // } + + // // pub(in super::super::super) fn create_field_snapshot_equality( + // // &mut self, + // // field: &vir_mid::FieldDecl, + // // ) -> SpannedEncodingResult { + // // use vir_low::macros::*; + // // let result = self.inner.result()?; + // // let field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + // // self.inner.ty, + // // field, + // // result.into(), + // // self.inner.position, + // // )?; + // // let snap_call = self.create_field_snap_call(&field)?; + // // Ok(expr! { + // // [field_snapshot] == [snap_call] + // // }) + // // } + + pub(in super::super::super) fn create_field_snapshot_equality( + &mut self, + field: &vir_mid::FieldDecl, + ) -> SpannedEncodingResult { + let owned_call = self.field_owned_snap()?; + self.inner.create_field_snapshot_equality(field, owned_call) + } + + fn field_owned_snap( + &mut self, + ) -> SpannedEncodingResult< + impl Fn( + &mut FunctionDeclBuilder, + &vir_mid::FieldDecl, + vir_low::Expression, + vir_low::Expression, + ) -> SpannedEncodingResult, + > { + let address: vir_low::Expression = self.inner.address.clone().into(); + let _address = std::rc::Rc::new(address); + Ok( + move |builder: &mut FunctionDeclBuilder, + field: &vir_mid::FieldDecl, + _, + field_address| { + // let field_address = builder.lowerer.encode_field_address( + // builder.ty, + // field, + // (*address).clone(), + // builder.position, + // )?; + builder.lowerer.owned_aliased_snap( + CallContext::BuiltinMethod, + &field.ty, + &field.ty, + field_address, + builder.position, + ) + }, + ) + } + + pub(in super::super::super) fn create_discriminant_snapshot_equality( + &mut self, + decl: &vir_mid::type_decl::Enum, + ) -> SpannedEncodingResult { + use vir_low::macros::*; + let result = self.inner.result()?; + let discriminant_snapshot = self.inner.lowerer.obtain_enum_discriminant( + result.into(), + self.inner.ty, + self.inner.position, + )?; + let discriminant_field = decl.discriminant_field(); + let discriminant_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + &discriminant_field, + self.inner.address.clone().into(), + self.inner.position, + )?; + let snap_call = self.inner.lowerer.owned_aliased_snap( + CallContext::BuiltinMethod, + &decl.discriminant_type, + &decl.discriminant_type, + discriminant_address, + self.inner.position, + )?; + let snap_call_int = self.inner.lowerer.obtain_constant_value( + &decl.discriminant_type, + snap_call, + self.inner.position, + )?; + Ok(expr! { + [discriminant_snapshot] == [snap_call_int] + }) + } + + pub(in super::super::super) fn create_variant_snapshot_equality( + &mut self, + discriminant_value: vir_mid::DiscriminantValue, + variant: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)> { + use vir_low::macros::*; + let result = self.inner.result()?; + let discriminant_call = self.inner.lowerer.obtain_enum_discriminant( + result.clone().into(), + self.inner.ty, + self.inner.position, + )?; + let guard = expr! { + [ discriminant_call ] == [ discriminant_value.into() ] + }; + let variant_index = variant.name.clone().into(); + let variant_address = self.inner.lowerer.encode_enum_variant_address( + self.inner.ty, + &variant_index, + self.inner.address.clone().into(), + self.inner.position, + )?; + let variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + self.inner.ty, + &variant_index, + result.into(), + self.inner.position, + )?; + let ty = self.inner.ty.clone(); + let variant_type = ty.variant(variant_index); + let snap_call = self.inner.lowerer.owned_aliased_snap( + CallContext::BuiltinMethod, + &variant_type, + // Enum variant and enum have the same set of lifetime parameters, + // so we use type_decl here. We cannot use `variant_type` because + // `ty` is normalized. + self.inner.type_decl, + variant_address, + self.inner.position, + )?; + let equality = expr! { + [variant_snapshot] == [snap_call] + }; + Ok((guard, equality)) + } + + pub(in super::super::super) fn add_reference_snapshot_equalities( + &mut self, + decl: &vir_mid::type_decl::Reference, + lifetime: &vir_mid::ty::LifetimeConst, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let result = self.inner.result()?; + let guard = self + .inner + .lowerer + .encode_lifetime_const_into_pure_is_alive_variable(lifetime)?; + let lifetime = lifetime.to_pure_snapshot(self.inner.lowerer)?; + let place = self + .inner + .lowerer + .encode_aliased_place_root(self.inner.position)?; + let deref_place = self + .inner + .lowerer + .reference_deref_place(place, self.inner.position)?; + let current_snapshot = self.inner.lowerer.reference_target_current_snapshot( + self.inner.ty, + result.clone().into(), + self.inner.position, + )?; + let final_snapshot = self.inner.lowerer.reference_target_final_snapshot( + self.inner.ty, + result.clone().into(), + self.inner.position, + )?; + let address = self.inner.lowerer.reference_address( + self.inner.ty, + result.clone().into(), + self.inner.position, + )?; + let slice_len = self.inner.lowerer.reference_slice_len( + self.inner.ty, + result.into(), + self.inner.position, + )?; + let equalities = if decl.uniqueness.is_unique() { + let current_snap_call = self.inner.lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + &decl.target_type, + &decl.target_type, + deref_place.clone(), + address.clone(), + lifetime.clone().into(), + slice_len.clone(), + false, + self.inner.position, + )?; + let final_snap_call = self.inner.lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + &decl.target_type, + &decl.target_type, + deref_place, + address, + lifetime.into(), + slice_len, + true, + self.inner.position, + )?; + expr! { + ([current_snapshot] == [current_snap_call]) && + ([final_snapshot] == [final_snap_call]) + } + } else { + let snap_call = self.inner.lowerer.frac_ref_snap( + CallContext::BuiltinMethod, + &decl.target_type, + &decl.target_type, + deref_place, + address, + lifetime.into(), + slice_len, + self.inner.position, + )?; + expr! { + [current_snapshot] == [snap_call] + } + }; + let expression = expr! { + guard ==> [equalities] + }; + self.add_snapshot_body_postcondition(expression) + } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult<()> { + // let precondition_predicate = self.precondition_predicate()?; + let predicate_kind = PredicateKind::Owned; + let snap_call = self.field_owned_snap()?; + self.inner + .add_structural_invariant(decl, false, predicate_kind, &snap_call) + } + + pub(in super::super::super) fn take_owned_snapshot_functions_to_encode( + &mut self, + ) -> Vec { + std::mem::take(&mut self.inner.owned_snapshot_functions_to_encode) + } + + pub(in super::super::super) fn take_owned_range_snapshot_functions_to_encode( + &mut self, + ) -> Vec { + std::mem::take(&mut self.inner.owned_range_snapshot_functions_to_encode) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_range_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_range_decl.rs new file mode 100644 index 00000000000..b9f5aff36e7 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_range_decl.rs @@ -0,0 +1,202 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + lifetimes::LifetimesInterface, + lowerer::Lowerer, + pointers::PointersInterface, + predicates::{ + owned::builders::common::function_decl::FunctionDeclBuilder, PredicatesOwnedInterface, + }, + snapshots::{IntoPureSnapshot, IntoSnapshot, SnapshotValuesInterface}, + type_layouts::TypeLayoutsInterface, + }, +}; + +use vir_crate::{ + common::{ + expression::{BinaryOperationHelpers, QuantifierHelpers}, + identifier::WithIdentifier, + position::Positioned, + }, + low::{self as vir_low}, + middle::{self as vir_mid}, +}; + +pub(in super::super::super) struct OwnedAliasedRangeSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + inner: FunctionDeclBuilder<'l, 'p, 'v, 'tcx>, + address: vir_low::VariableDecl, + start_index: vir_low::VariableDecl, + end_index: vir_low::VariableDecl, + slice_len: Option, + pres: Vec, + posts: Vec, +} + +impl<'l, 'p, 'v, 'tcx> OwnedAliasedRangeSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let slice_len = if ty.is_slice() { + Some(vir_mid::VariableDecl::new( + "slice_len", + lowerer.size_type_mid()?, + )) + } else { + None + }; + Ok(Self { + address: vir_low::VariableDecl::new("address", ty.to_snapshot(lowerer)?), + start_index: vir_low::VariableDecl::new("start_index", lowerer.size_type()?), + end_index: vir_low::VariableDecl::new("end_index", lowerer.size_type()?), + slice_len, + inner: FunctionDeclBuilder::new( + lowerer, + "snap_owned_range_aliased", + ty, + type_decl, + Default::default(), + )?, + pres: Vec::new(), + posts: Vec::new(), + }) + } + + pub(in super::super::super) fn build(mut self) -> SpannedEncodingResult { + let return_type = self.result_type()?; + let function = vir_low::FunctionDecl { + name: format!( + "{}${}", + self.inner.function_name, + self.inner.ty.get_identifier() + ), + kind: vir_low::FunctionKind::SnapRange, + parameters: self.inner.parameters, + body: None, + pres: self.pres, + posts: self.posts, + return_type, + }; + Ok(function) + } + + fn result_type(&mut self) -> SpannedEncodingResult { + let vir_mid::Type::Pointer(pointer_type) = self.inner.ty else { + unreachable!("{} must be a pointer type", self.inner.ty); + }; + let element_type = pointer_type.target_type.to_snapshot(self.inner.lowerer)?; + let return_type = vir_low::Type::seq(element_type); + Ok(return_type) + } + + fn result(&mut self) -> SpannedEncodingResult { + Ok(vir_low::VariableDecl::result_variable(self.result_type()?)) + } + + pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.address.clone()); + self.inner.parameters.push(self.start_index.clone()); + self.inner.parameters.push(self.end_index.clone()); + self.inner.create_lifetime_parameters()?; + if let Some(slice_len_mid) = &self.slice_len { + let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; + self.inner.parameters.push(slice_len); + } + self.inner.create_const_parameters()?; + Ok(()) + } + + pub(in super::super::super) fn add_owned_precondition(&mut self) -> SpannedEncodingResult<()> { + let predicates = self.inner.lowerer.owned_aliased_range( + CallContext::BuiltinMethod, + self.inner.ty, + self.inner.type_decl, + self.address.clone().into(), + self.start_index.clone().into(), + self.end_index.clone().into(), + Some(vir_low::Expression::wildcard_permission()), + self.inner.position, + )?; + self.pres.push(predicates); + Ok(()) + } + + pub(in super::super::super) fn add_postcondition(&mut self) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let size_type = self.inner.lowerer.size_type_mid()?; + var_decls! { + index: Int + } + let vir_mid::Type::Pointer(ty) = self.inner.ty else { + unreachable!() + }; + let initial_address = self.inner.lowerer.pointer_address( + self.inner.ty, + self.address.clone().into(), + self.inner.position, + )?; + let vir_mid::Type::Pointer(pointer_type) = self.inner.ty else { + unreachable!() + }; + let size = self + .inner + .lowerer + .encode_type_size_expression2(&pointer_type.target_type, &*pointer_type.target_type)?; + let element_address = self.inner.lowerer.address_offset( + size, + initial_address, + index.clone().into(), + self.inner.position, + )?; + let snap_call = self.inner.lowerer.owned_aliased_snap( + CallContext::BuiltinMethod, + &ty.target_type, + &*ty.target_type, + element_address.clone(), + self.inner.position, + )?; + let result_type = self.result_type()?; + let result = self.result()?; + let start_index = self.inner.lowerer.obtain_constant_value( + &size_type, + self.start_index.clone().into(), + self.inner.position, + )?; + let end_index = self.inner.lowerer.obtain_constant_value( + &size_type, + self.end_index.clone().into(), + self.inner.position, + )?; + let result_len = vir_low::Expression::container_op( + vir_low::ContainerOpKind::SeqLen, + result_type.clone(), + vec![result.clone().into()], + self.inner.position, + ); + let index_diff = vir_low::Expression::subtract(end_index.clone(), start_index.clone()); + self.posts.push(expr!([result_len] == [index_diff])); + let element_snap = vir_low::Expression::container_op( + vir_low::ContainerOpKind::SeqIndex, + result_type, + vec![ + result.into(), + vir_low::Expression::subtract(index.clone().into(), start_index.clone()), + ], + self.inner.position, + ); + let body = expr!( + (([start_index] <= index) && (index < [end_index])) ==> + ([snap_call] == [element_snap]) + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![element_address])], + body, + ); + self.posts.push(expression); + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_range_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_range_use.rs new file mode 100644 index 00000000000..9ab64f7eabc --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_range_use.rs @@ -0,0 +1,96 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, lowerer::Lowerer, + predicates::owned::builders::common::function_use::FunctionCallBuilder, + snapshots::IntoSnapshot, + }, +}; +use vir_crate::{ + common::identifier::WithIdentifier, + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super::super::super) struct OwnedAliasedRangeSnapCallBuilder< + 'l, + 'p, + 'v, + 'tcx, + G, +> where + G: WithLifetimes + WithConstArguments, +{ + inner: FunctionCallBuilder<'l, 'p, 'v, 'tcx, G>, +} + +impl<'l, 'p, 'v, 'tcx, G> OwnedAliasedRangeSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super::super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + address: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let arguments = vec![address, start_index, end_index]; + let inner = FunctionCallBuilder::new( + lowerer, + "snap_owned_range_aliased", + context, + ty, + generics, + arguments, + position, + )?; + Ok(Self { inner }) + } + + pub(in super::super::super::super::super) fn build( + self, + ) -> SpannedEncodingResult { + let vir_mid::Type::Pointer(pointer_type) = self.inner.ty else { + unreachable!("{} must be a pointer type", self.inner.ty); + }; + let element_type = pointer_type.target_type.to_snapshot(self.inner.lowerer)?; + let return_type = vir_low::Type::seq(element_type); + let call = vir_low::Expression::function_call( + format!( + "{}${}", + self.inner.function_name, + self.inner.ty.get_identifier() + ), + self.inner.arguments, + return_type, + ); + Ok(call.set_default_position(self.inner.position)) + } + + // pub(in super::super::super::super::super) fn add_custom_argument( + // &mut self, + // argument: vir_low::Expression, + // ) -> SpannedEncodingResult<()> { + // self.inner.arguments.push(argument); + // Ok(()) + // } + + pub(in super::super::super::super::super) fn add_lifetime_arguments( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_lifetime_arguments() + } + + pub(in super::super::super::super::super) fn add_const_arguments( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_const_arguments() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_use.rs new file mode 100644 index 00000000000..ba8337a387d --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/function_use.rs @@ -0,0 +1,73 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, lowerer::Lowerer, + predicates::owned::builders::common::function_use::FunctionCallBuilder, + }, +}; +use vir_crate::{ + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super::super::super) struct OwnedAliasedSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + inner: FunctionCallBuilder<'l, 'p, 'v, 'tcx, G>, +} + +impl<'l, 'p, 'v, 'tcx, G> OwnedAliasedSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super::super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let arguments = vec![address]; + let inner = FunctionCallBuilder::new( + lowerer, + "snap_owned_aliased", + context, + ty, + generics, + arguments, + position, + )?; + Ok(Self { inner }) + } + + pub(in super::super::super::super::super) fn build( + self, + ) -> SpannedEncodingResult { + self.inner.build() + } + + // pub(in super::super::super::super::super) fn add_custom_argument( + // &mut self, + // argument: vir_low::Expression, + // ) -> SpannedEncodingResult<()> { + // self.inner.arguments.push(argument); + // Ok(()) + // } + + pub(in super::super::super::super::super) fn add_lifetime_arguments( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_lifetime_arguments() + } + + pub(in super::super::super::super::super) fn add_const_arguments( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_const_arguments() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/mod.rs new file mode 100644 index 00000000000..0bc824c1cbb --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/mod.rs @@ -0,0 +1,7 @@ +pub(super) mod function_decl; +pub(super) mod function_use; +pub(super) mod function_range_decl; +pub(super) mod function_range_use; +pub(super) mod predicate_decl; +pub(super) mod predicate_use; +pub(super) mod predicate_range_use; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_decl.rs new file mode 100644 index 00000000000..60a58566f74 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_decl.rs @@ -0,0 +1,309 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + lowerer::Lowerer, + places::PlacesInterface, + predicates::{ + owned::builders::{ + common::predicate_decl::PredicateDeclBuilder, PredicateDeclBuilderMethods, + }, + PredicatesOwnedInterface, + }, + snapshots::{IntoPureSnapshot, PredicateKind, SnapshotValuesInterface}, + type_layouts::TypeLayoutsInterface, + }, +}; + +use vir_crate::{ + common::{expression::GuardedExpressionIterator, position::Positioned}, + low::{self as vir_low}, + middle::{self as vir_mid}, +}; + +pub(in super::super::super) struct OwnedAliasedBuilder<'l, 'p, 'v, 'tcx> { + inner: PredicateDeclBuilder<'l, 'p, 'v, 'tcx>, + slice_len: Option, +} + +impl<'l, 'p, 'v, 'tcx> PredicateDeclBuilderMethods<'l, 'p, 'v, 'tcx> + for OwnedAliasedBuilder<'l, 'p, 'v, 'tcx> +{ + fn inner(&mut self) -> &mut PredicateDeclBuilder<'l, 'p, 'v, 'tcx> { + &mut self.inner + } +} + +impl<'l, 'p, 'v, 'tcx> OwnedAliasedBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super) fn new( + _lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + _ty: &'l vir_mid::Type, + _type_decl: &'l vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + unimplemented!() + // let slice_len = if ty.is_slice() { + // Some(vir_mid::VariableDecl::new( + // "slice_len", + // lowerer.size_type_mid()?, + // )) + // } else { + // None + // }; + // let position = type_decl.position(); + // Ok(Self { + // slice_len, + // inner: PredicateDeclBuilder::new(lowerer, "OwnedAliased", ty, type_decl, position)?, + // }) + } + + pub(in super::super::super) fn build(self) -> vir_low::PredicateDecl { + self.inner.build() + } + + pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.inner.address.clone()); + self.inner.create_lifetime_parameters()?; + if let Some(slice_len_mid) = &self.slice_len { + let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; + self.inner.parameters.push(slice_len); + } + self.inner.create_const_parameters()?; + Ok(()) + } + + fn size_of(&mut self) -> SpannedEncodingResult { + self.inner + .lowerer + .encode_type_size_expression2(self.inner.ty, self.inner.type_decl) + } + + fn padding_size(&mut self) -> SpannedEncodingResult { + self.inner + .lowerer + .encode_type_padding_size_expression(self.inner.ty) + } + + pub(in super::super::super) fn add_base_memory_block(&mut self) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let size_of = self.size_of()?; + let address = &self.inner.address; + let expression = expr! { + acc(MemoryBlock(address, [size_of])) + }; + self.inner.add_conjunct(expression) + } + + pub(in super::super::super) fn add_padding_memory_block( + &mut self, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let padding_size = self.padding_size()?; + let address = &self.inner.address; + let expression = expr! { + acc(MemoryBlock(address, [padding_size])) + }; + self.inner.add_conjunct(expression) + } + + pub(in super::super::super) fn add_field_predicate( + &mut self, + field: &vir_mid::FieldDecl, + ) -> SpannedEncodingResult<()> { + let field_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + field, + self.inner.address.clone().into(), + self.inner.position, + )?; + let expression = self.inner.lowerer.owned_aliased( + CallContext::BuiltinMethod, + &field.ty, + &field.ty, + field_address, + None, + self.inner.position, + )?; + self.inner.add_conjunct(expression) + } + + pub(in super::super::super) fn add_discriminant_predicate( + &mut self, + decl: &vir_mid::type_decl::Enum, + ) -> SpannedEncodingResult<()> { + let discriminant_field = decl.discriminant_field(); + let discriminant_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + &discriminant_field, + self.inner.address.clone().into(), + self.inner.position, + )?; + let expression = self.inner.lowerer.owned_aliased( + CallContext::BuiltinMethod, + &decl.discriminant_type, + &decl.discriminant_type, + discriminant_address, + None, + self.inner.position, + )?; + self.inner.add_conjunct(expression) + } + + pub(in super::super::super) fn add_unique_ref_target_predicate( + &mut self, + target_type: &vir_mid::Type, + lifetime: &vir_mid::ty::LifetimeConst, + ) -> SpannedEncodingResult<()> { + let place = self + .inner + .lowerer + .encode_aliased_place_root(self.inner.position)?; + let root_address = self.inner.address.clone(); + self.inner.add_unique_ref_target_predicate( + target_type, + lifetime, + place, + root_address, + false, + ) + } + + pub(in super::super::super) fn add_frac_ref_target_predicate( + &mut self, + target_type: &vir_mid::Type, + lifetime: &vir_mid::ty::LifetimeConst, + ) -> SpannedEncodingResult<()> { + let place = self + .inner + .lowerer + .encode_aliased_place_root(self.inner.position)?; + let root_address = self.inner.address.clone(); + self.inner + .add_frac_ref_target_predicate(target_type, lifetime, place, root_address) + } + + // FIXME: Code duplication. + pub(in super::super::super) fn get_slice_len( + &self, + ) -> SpannedEncodingResult { + Ok(self.slice_len.as_ref().unwrap().clone()) + } + + // pub(in super::super::super) fn add_quantified_permission( + // &mut self, + // array_length_mid: &vir_mid::VariableDecl, + // element_type: &vir_mid::Type, + // ) -> SpannedEncodingResult<()> { + // use vir_low::macros::*; + // let size_type = self.inner.lowerer.size_type()?; + // let size_type_mid = self.inner.lowerer.size_type_mid()?; + // var_decls! { + // index: {size_type} + // }; + // let index_validity = self + // .inner + // .lowerer + // .encode_snapshot_valid_call_for_type(index.clone().into(), &size_type_mid)?; + // let index_int = self.inner.lowerer.obtain_constant_value( + // &size_type_mid, + // index.clone().into(), + // self.inner.position, + // )?; + // let array_length_int = self.inner.array_length_int(array_length_mid)?; + // let element_place = self.inner.lowerer.encode_index_place( + // self.inner.ty, + // self.inner.place.clone().into(), + // index.clone().into(), + // self.inner.position, + // )?; + // let element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( + // self.snapshot.clone().into(), + // index_int.clone(), + // self.inner.position, + // )?; + // let element_predicate_acc = self.inner.lowerer.owned_non_aliased( + // CallContext::BuiltinMethod, + // element_type, + // element_type, + // element_place, + // self.inner.root_address.clone().into(), + // element_snapshot, + // None, + // )?; + // let elements = vir_low::Expression::forall( + // vec![index], + // vec![vir_low::Trigger::new(vec![element_predicate_acc.clone()])], + // expr! { + // ([index_validity] && ([index_int] < [array_length_int])) ==> + // [element_predicate_acc] + // }, + // ); + // self.inner.add_conjunct(elements) + // } + + pub(in super::super::super) fn create_variant_predicate( + &mut self, + decl: &vir_mid::type_decl::Enum, + discriminant_value: vir_mid::DiscriminantValue, + variant: &vir_mid::type_decl::Struct, + variant_type: &vir_mid::Type, + ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)> { + use vir_low::macros::*; + let discriminant_call = { + let discriminant_field = decl.discriminant_field(); + let discriminant_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + &discriminant_field, + self.inner.place.clone().into(), + self.inner.position, + )?; + let discriminant_snapshot = self.inner.lowerer.owned_aliased_snap( + CallContext::BuiltinMethod, + &decl.discriminant_type, + &decl.discriminant_type, + discriminant_address, + self.inner.position, + )?; + self.inner.lowerer.obtain_constant_value( + &decl.discriminant_type, + discriminant_snapshot, + self.inner.position, + )? + }; + let guard = expr! { + [ discriminant_call ] == [ discriminant_value.into() ] + }; + let variant_index = variant.name.clone().into(); + let variant_address = self.inner.lowerer.encode_enum_variant_address( + self.inner.ty, + &variant_index, + self.inner.place.clone().into(), + self.inner.position, + )?; + let predicate = self.inner.lowerer.owned_aliased( + CallContext::BuiltinMethod, + variant_type, + variant_type, + variant_address, + None, + self.inner.position, + )?; + Ok((guard, predicate)) + } + + pub(in super::super::super) fn add_variant_predicates( + &mut self, + variant_predicates: Vec<(vir_low::Expression, vir_low::Expression)>, + ) -> SpannedEncodingResult<()> { + self.inner + .add_conjunct(variant_predicates.into_iter().create_match()) + } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult> { + self.inner + .add_structural_invariant(decl, PredicateKind::Owned) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_range_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_range_use.rs new file mode 100644 index 00000000000..ac231995e3c --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_range_use.rs @@ -0,0 +1,112 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, builtin_methods::CallContext, lowerer::Lowerer, + pointers::PointersInterface, predicates::PredicatesOwnedInterface, + snapshots::SnapshotValuesInterface, type_layouts::TypeLayoutsInterface, + }, +}; + +use vir_crate::{ + common::expression::QuantifierHelpers, + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super::super::super) struct OwnedAliasedRangeUseBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + address: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, +} + +impl<'l, 'p, 'v, 'tcx, G> OwnedAliasedRangeUseBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super::super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + address: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult { + Ok(Self { + lowerer, + context, + ty, + generics, + address, + start_index, + end_index, + permission_amount, + position, + }) + } + + pub(in super::super::super::super::super) fn build( + self, + ) -> SpannedEncodingResult { + use vir_low::macros::*; + let size_type = self.lowerer.size_type_mid()?; + var_decls! { + index: Int + } + let vir_mid::Type::Pointer(ty) = self.ty else { + unreachable!() + }; + let initial_address = self + .lowerer + .pointer_address(self.ty, self.address, self.position)?; + let vir_mid::Type::Pointer(pointer_type) = self.ty else { + unreachable!() + }; + let size = self + .lowerer + .encode_type_size_expression2(&pointer_type.target_type, &*pointer_type.target_type)?; + let element_address = self.lowerer.address_offset( + size, + initial_address, + index.clone().into(), + self.position, + )?; + let predicate = self.lowerer.owned_aliased( + self.context, + &ty.target_type, + self.generics, + element_address.clone(), + self.permission_amount, + self.position, + )?; + let start_index = + self.lowerer + .obtain_constant_value(&size_type, self.start_index, self.position)?; + let end_index = + self.lowerer + .obtain_constant_value(&size_type, self.end_index, self.position)?; + let body = expr!( + (([start_index] <= index) && (index < [end_index])) ==> [predicate] + ); + let expression = vir_low::Expression::forall( + vec![index], + vec![vir_low::Trigger::new(vec![element_address])], + body, + ); + Ok(expression) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_use.rs new file mode 100644 index 00000000000..79d84844db5 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_aliased/predicate_use.rs @@ -0,0 +1,72 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, lowerer::Lowerer, + predicates::owned::builders::common::predicate_use::PredicateUseBuilder, + }, +}; + +use vir_crate::{ + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super::super::super) struct OwnedAliasedUseBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + inner: PredicateUseBuilder<'l, 'p, 'v, 'tcx, G>, +} + +impl<'l, 'p, 'v, 'tcx, G> OwnedAliasedUseBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super::super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + address: vir_low::Expression, + ) -> SpannedEncodingResult { + let arguments = vec![address]; + let inner = PredicateUseBuilder::new( + lowerer, + "OwnedAliased", + context, + ty, + generics, + arguments, + Default::default(), + )?; + Ok(Self { inner }) + } + + pub(in super::super::super::super::super) fn build( + self, + ) -> SpannedEncodingResult { + Ok(self.inner.build()) + } + + pub(in super::super::super::super::super) fn add_lifetime_arguments( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_lifetime_arguments() + } + + pub(in super::super::super::super::super) fn add_const_arguments( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_const_arguments() + } + + pub(in super::super::super::super::super) fn set_maybe_permission_amount( + &mut self, + permission_amount: Option, + ) -> SpannedEncodingResult<()> { + self.inner.set_maybe_permission_amount(permission_amount) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/function_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/function_decl.rs new file mode 100644 index 00000000000..bfdd4ac84f2 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/function_decl.rs @@ -0,0 +1,662 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + lifetimes::LifetimesInterface, + lowerer::Lowerer, + places::PlacesInterface, + predicates::{ + owned::builders::common::function_decl::FunctionDeclBuilder, OwnedNonAliasedUseBuilder, + PredicatesMemoryBlockInterface, PredicatesOwnedInterface, + }, + references::ReferencesInterface, + snapshots::{ + IntoPureSnapshot, IntoSnapshotLowerer, PredicateKind, SnapshotBytesInterface, + SnapshotValidityInterface, SnapshotValuesInterface, + }, + type_layouts::TypeLayoutsInterface, + }, +}; + +use vir_crate::{ + common::{expression::QuantifierHelpers, position::Positioned}, + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super) struct OwnedNonAliasedSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + inner: FunctionDeclBuilder<'l, 'p, 'v, 'tcx>, + place: vir_low::VariableDecl, + address: vir_low::VariableDecl, + slice_len: Option, +} + +impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let slice_len = if ty.is_slice() { + Some(vir_mid::VariableDecl::new( + "slice_len", + lowerer.size_type_mid()?, + )) + } else { + None + }; + Ok(Self { + place: vir_low::VariableDecl::new("place", lowerer.place_option_type()?), + address: vir_low::VariableDecl::new("address", lowerer.address_type()?), + slice_len, + inner: FunctionDeclBuilder::new( + lowerer, + "snap_owned_non_aliased", + ty, + type_decl, + Default::default(), + )?, + }) + } + + pub(in super::super::super) fn get_snapshot_postconditions( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_postconditions() + } + + pub(in super::super::super) fn get_snapshot_body( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_body() + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult { + self.inner.build() + } + + pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.place.clone()); + self.inner.parameters.push(self.address.clone()); + self.inner.create_lifetime_parameters()?; + if let Some(slice_len_mid) = &self.slice_len { + let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; + self.inner.parameters.push(slice_len); + } + self.inner.create_const_parameters()?; + Ok(()) + } + + // FIXME: Code duplication. + pub(in super::super::super) fn get_slice_len( + &self, + ) -> SpannedEncodingResult { + Ok(self.slice_len.as_ref().unwrap().clone()) + } + + fn owned_predicate( + &mut self, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let mut builder = OwnedNonAliasedUseBuilder::new( + self.inner.lowerer, + CallContext::BuiltinMethod, + ty, + generics, + place, + address, + self.inner.position, + )?; + builder.add_lifetime_arguments()?; + builder.add_const_arguments()?; + builder.set_maybe_permission_amount(Some(vir_low::Expression::wildcard_permission()))?; + builder.build() + } + + // FIXME: Code duplication with add_quantified_permission. + pub(in super::super::super) fn add_quantifiers( + &mut self, + array_length_mid: &vir_mid::VariableDecl, + element_type: &vir_mid::Type, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let size_type_mid = self.inner.lowerer.size_type_mid()?; + var_decls! { + index_int: Int + }; + let index = self.inner.lowerer.construct_constant_snapshot( + &size_type_mid, + index_int.clone().into(), + self.inner.position, + )?; + let index_validity = self + .inner + .lowerer + .encode_snapshot_valid_call_for_type(index.clone(), &size_type_mid)?; + let array_length_int = self.inner.array_length_int(array_length_mid)?; + let element_place = self.inner.lowerer.encode_index_place( + self.inner.ty, + self.place.clone().into(), + index.clone(), + self.inner.position, + )?; + let element_address = self.inner.lowerer.encode_index_address( + self.inner.ty, + self.address.clone().into(), + index, + self.inner.position, + )?; + let element_predicate_acc = { + self.owned_predicate( + element_type, + element_type, + element_place.clone(), + element_address.clone(), + )? + }; + let result = self.inner.result()?.into(); + let element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( + result, + index_int.clone().into(), + self.inner.position, + )?; + let element_snap_call = self.inner.lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + element_type, + element_type, + element_place, + element_address, + self.inner.position, + )?; + let elements = vir_low::Expression::forall( + vec![index_int.clone()], + vec![vir_low::Trigger::new(vec![element_predicate_acc])], + expr! { + ([index_validity] && (index_int < [array_length_int])) ==> + ([element_snapshot] == [element_snap_call]) + }, + ); + self.add_snapshot_body_postcondition(elements) + } + + pub(in super::super::super) fn add_snapshot_body_postcondition( + &mut self, + body: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + // let predicate = self.precondition_predicate()?; + // let unfolding = predicate.into_unfolding(body); + // self.inner.add_postcondition(unfolding) + self.inner.add_snapshot_body_postcondition(body) + } + + pub(in super::super::super) fn add_validity_postcondition( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_validity_postcondition() + } + + pub(in super::super::super) fn add_snapshot_len_equal_to_postcondition( + &mut self, + array_length_mid: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult<()> { + self.inner + .add_snapshot_len_equal_to_postcondition(array_length_mid) + } + + pub(in super::super::super) fn add_owned_precondition(&mut self) -> SpannedEncodingResult<()> { + let predicate = self.precondition_predicate()?; + self.inner.add_precondition(predicate) + } + + fn precondition_predicate(&mut self) -> SpannedEncodingResult { + self.owned_predicate( + self.inner.ty, + self.inner.type_decl, + self.place.clone().into(), + self.address.clone().into(), + ) + } + + // fn compute_address(&self) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let compute_address = ty!(Address); + // let expression = expr! { + // ComputeAddress::compute_address( + // [self.place.clone().into()], + // [self.address.clone().into()] + // ) + // }; + // Ok(expression) + // } + + fn size_of(&mut self) -> SpannedEncodingResult { + self.inner + .lowerer + .encode_type_size_expression2(self.inner.ty, self.inner.type_decl) + } + + fn add_bytes_snapshot_equality_with( + &mut self, + snap_ty: &vir_mid::Type, + snapshot: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let size_of = self.size_of()?; + let bytes = self + .inner + .lowerer + .encode_memory_block_bytes_expression(self.address.clone().into(), size_of)?; + let to_bytes = ty! { Bytes }; + let expression = expr! { + [bytes] == (Snap::to_bytes([snapshot])) + }; + self.add_snapshot_body_postcondition(expression) + } + + pub(in super::super::super) fn add_bytes_snapshot_equality( + &mut self, + ) -> SpannedEncodingResult<()> { + let result = self.inner.result()?.into(); + self.add_bytes_snapshot_equality_with(self.inner.ty, result) + } + + pub(in super::super::super) fn add_bytes_address_snapshot_equality( + &mut self, + ) -> SpannedEncodingResult<()> { + let result = self.inner.result()?.into(); + let address_type = self.inner.lowerer.reference_address_type(self.inner.ty)?; + self.inner + .lowerer + .encode_snapshot_to_bytes_function(&address_type)?; + let target_address_snapshot = self.inner.lowerer.reference_address_snapshot( + self.inner.ty, + result, + self.inner.position, + )?; + self.add_bytes_snapshot_equality_with(&address_type, target_address_snapshot) + } + + // fn create_field_snap_call( + // &mut self, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult { + // let field_place = self.inner.lowerer.encode_field_place( + // self.inner.ty, + // field, + // self.place.clone().into(), + // self.inner.position, + // )?; + // self.inner.lowerer.owned_non_aliased_snap( + // CallContext::BuiltinMethod, + // &field.ty, + // &field.ty, + // field_place, + // self.address.clone().into(), + // self.inner.position, + // ) + // } + + // pub(in super::super::super) fn create_field_snapshot_equality( + // &mut self, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let result = self.inner.result()?; + // let field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + // self.inner.ty, + // field, + // result.into(), + // self.inner.position, + // )?; + // let snap_call = self.create_field_snap_call(&field)?; + // Ok(expr! { + // [field_snapshot] == [snap_call] + // }) + // } + + pub(in super::super::super) fn create_field_snapshot_equality( + &mut self, + field: &vir_mid::FieldDecl, + ) -> SpannedEncodingResult { + let owned_call = self.field_owned_snap()?; + self.inner.create_field_snapshot_equality(field, owned_call) + } + + fn field_owned_snap( + &mut self, + ) -> SpannedEncodingResult< + impl Fn( + &mut FunctionDeclBuilder, + &vir_mid::FieldDecl, + vir_low::Expression, + vir_low::Expression, + ) -> SpannedEncodingResult, + > { + Ok( + move |builder: &mut FunctionDeclBuilder, + field: &vir_mid::FieldDecl, + field_place, + field_address| { + builder.lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + &field.ty, + &field.ty, + field_place, + field_address, + builder.position, + ) + }, + ) + } + + pub(in super::super::super) fn create_discriminant_snapshot_equality( + &mut self, + decl: &vir_mid::type_decl::Enum, + ) -> SpannedEncodingResult { + use vir_low::macros::*; + let result = self.inner.result()?; + let discriminant_snapshot = self.inner.lowerer.obtain_enum_discriminant( + result.into(), + self.inner.ty, + self.inner.position, + )?; + let discriminant_field = decl.discriminant_field(); + let discriminant_place = self.inner.lowerer.encode_field_place( + self.inner.ty, + &discriminant_field, + self.place.clone().into(), + self.inner.position, + )?; + let discriminant_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + &discriminant_field, + self.address.clone().into(), + self.inner.position, + )?; + let snap_call = self.inner.lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + &decl.discriminant_type, + &decl.discriminant_type, + discriminant_place, + discriminant_address, + self.inner.position, + )?; + let snap_call_int = self.inner.lowerer.obtain_constant_value( + &decl.discriminant_type, + snap_call, + self.inner.position, + )?; + Ok(expr! { + [discriminant_snapshot] == [snap_call_int] + }) + } + + pub(in super::super::super) fn create_variant_snapshot_equality( + &mut self, + discriminant_value: vir_mid::DiscriminantValue, + variant: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)> { + use vir_low::macros::*; + let result = self.inner.result()?; + let discriminant_call = self.inner.lowerer.obtain_enum_discriminant( + result.clone().into(), + self.inner.ty, + self.inner.position, + )?; + let guard = expr! { + [ discriminant_call ] == [ discriminant_value.into() ] + }; + let variant_index = variant.name.clone().into(); + let variant_place = self.inner.lowerer.encode_enum_variant_place( + self.inner.ty, + &variant_index, + self.place.clone().into(), + self.inner.position, + )?; + let variant_address = self.inner.lowerer.encode_enum_variant_address( + self.inner.ty, + &variant_index, + self.address.clone().into(), + self.inner.position, + )?; + let variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + self.inner.ty, + &variant_index, + result.into(), + self.inner.position, + )?; + let ty = self.inner.ty.clone(); + // let mut enum_ty = ty.unwrap_enum(); + // enum_ty.lifetimes = variant.lifetimes.clone(); + // enum_ty.variant = Some(variant_index); + // let variant_type = vir_mid::Type::Enum(enum_ty); + let variant_type = ty.variant(variant_index); + let snap_call = self.inner.lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + &variant_type, + // Enum variant and enum have the same set of lifetime parameters, + // so we use type_decl here. We cannot use `variant_type` because + // `ty` is normalized. + self.inner.type_decl, + variant_place, + variant_address, + self.inner.position, + )?; + let equality = expr! { + [variant_snapshot] == [snap_call] + }; + Ok((guard, equality)) + } + + pub(in super::super::super) fn add_reference_snapshot_equalities( + &mut self, + decl: &vir_mid::type_decl::Reference, + lifetime: &vir_mid::ty::LifetimeConst, + ) -> SpannedEncodingResult<()> { + use vir_low::macros::*; + let result = self.inner.result()?; + let guard = self + .inner + .lowerer + .encode_lifetime_const_into_pure_is_alive_variable(lifetime)?; + let lifetime = lifetime.to_pure_snapshot(self.inner.lowerer)?; + let deref_place = self + .inner + .lowerer + .reference_deref_place(self.place.clone().into(), self.inner.position)?; + let current_snapshot = self.inner.lowerer.reference_target_current_snapshot( + self.inner.ty, + result.clone().into(), + self.inner.position, + )?; + let address = self.inner.lowerer.reference_address( + self.inner.ty, + result.clone().into(), + self.inner.position, + )?; + let slice_len = self.inner.lowerer.reference_slice_len( + self.inner.ty, + result.clone().into(), + self.inner.position, + )?; + let equalities = if decl.uniqueness.is_unique() { + let final_snapshot = self.inner.lowerer.reference_target_final_snapshot( + self.inner.ty, + result.clone().into(), + self.inner.position, + )?; + let current_snap_call = self.inner.lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + &decl.target_type, + &decl.target_type, + deref_place.clone(), + address.clone(), + lifetime.clone().into(), + slice_len.clone(), + false, + self.inner.position, + )?; + let final_snap_call = self.inner.lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + &decl.target_type, + &decl.target_type, + deref_place, + address, + lifetime.into(), + slice_len, + true, + self.inner.position, + )?; + expr! { + ([current_snapshot] == [current_snap_call]) && + ([final_snapshot] == [final_snap_call]) + } + } else { + let snap_call = self.inner.lowerer.frac_ref_snap( + CallContext::BuiltinMethod, + &decl.target_type, + &decl.target_type, + deref_place, + address, + lifetime.into(), + slice_len, + self.inner.position, + )?; + expr! { + [current_snapshot] == [snap_call] + } + }; + let expression = expr! { + guard ==> [equalities] + }; + self.add_snapshot_body_postcondition(expression) + } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult<()> { + // let precondition_predicate = self.precondition_predicate()?; + let predicate_kind = PredicateKind::Owned; + let snap_call = self.field_owned_snap()?; + self.inner + .add_structural_invariant(decl, false, predicate_kind, &snap_call) + } + + pub(in super::super::super) fn take_owned_snapshot_functions_to_encode( + &mut self, + ) -> Vec { + std::mem::take(&mut self.inner.owned_snapshot_functions_to_encode) + } + + pub(in super::super::super) fn take_owned_range_snapshot_functions_to_encode( + &mut self, + ) -> Vec { + std::mem::take(&mut self.inner.owned_range_snapshot_functions_to_encode) + } + + // // FIXME: Code duplication. + // pub(in super::super::super) fn add_structural_invariant( + // &mut self, + // decl: &vir_mid::type_decl::Struct, + // ) -> SpannedEncodingResult<()> { + // if let Some(invariant) = decl.structural_invariant.clone() { + // let mut regular_field_arguments = Vec::new(); + // for field in &decl.fields { + // let owned_call = self.field_owned_snap()?; + // let snap_call = self.inner.create_field_snap_call(field, owned_call)?; + // regular_field_arguments.push(snap_call); + // // regular_field_arguments.push(self.create_field_snap_call(field)?); + // } + // let result = self.inner.result()?; + // let deref_fields = self + // .inner + // .lowerer + // .structural_invariant_to_deref_fields(&invariant)?; + // let mut constructor_encoder = AssertionToSnapshotConstructor::for_function_body( + // PredicateKind::Owned, + // self.inner.ty, + // regular_field_arguments, + // decl.fields.clone(), + // deref_fields, + // self.inner.position, + // ); + // let invariant_expression = invariant.into_iter().conjoin(); + // let permission_expression = invariant_expression.convert_into_permission_expression(); + // let constructor = constructor_encoder + // .expression_to_snapshot_constructor(self.inner.lowerer, &permission_expression)?; + // self.add_snapshot_body_postcondition(vir_low::Expression::equals( + // result.into(), + // constructor, + // ))?; + // // let mut equalities = Vec::new(); + // // for assertion in invariant { + // // for (guard, place) in assertion.collect_guarded_owned_places() { + // // let parameter = self.inner.lowerer.compute_deref_parameter(&place)?; + // // let deref_result_snapshot = self.inner.lowerer.obtain_parameter_snapshot( + // // self.inner.ty, + // // ¶meter.name, + // // parameter.ty, + // // result.clone().into(), + // // self.inner.position, + // // )?; + // // let ty = place.get_type(); + // // let place_low = self.inner.lowerer.encode_expression_as_place(&place)?; + // // let root_address_low = { + // // // Code duplication with pointer_deref_into_address + // // let deref_place = place.get_last_dereferenced_pointer().unwrap(); + // // // TODO: replace self in deref_place with result. + // // let base_snapshot = deref_place.to_pure_snapshot(self.inner.lowerer)?; + // // let ty = deref_place.get_type(); + // // self.inner + // // .lowerer + // // .pointer_address(ty, base_snapshot, place.position())? + // // }; + // // let snap_call = self.inner.lowerer.owned_non_aliased_snap( + // // CallContext::BuiltinMethod, + // // ty, + // // ty, + // // place_low, + // // root_address_low, + // // self.inner.position, + // // )?; + // // equalities.push(expr! { + // // [deref_result_snapshot] == [snap_call] + // // }); + // // } + // // } + // // self.add_snapshot_body_postcondition(equalities.into_iter().conjoin())?; + // } + + // // // FIXME: Code duplication with encode_assign_method_rvalue + // // if let Some(invariant) = &decl.structural_invariant { + // // let mut assertion_encoder = + // // crate::encoder::middle::core_proof::builtin_methods::AssertionEncoder::new( + // // &decl, + // // Vec::new(), + // // &None, + // // ); + // // assertion_encoder.set_result_value(self.inner.result()?.clone()); + // // assertion_encoder.set_in_function(); + // // for assertion in invariant { + // // let low_assertion = assertion_encoder.expression_to_snapshot( + // // self.inner.lowerer, + // // assertion, + // // true, + // // )?; + // // self.add_snapshot_body_postcondition(low_assertion)?; + // // } + // // } + // Ok(()) + // } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/function_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/function_use.rs new file mode 100644 index 00000000000..93ef015593d --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/function_use.rs @@ -0,0 +1,74 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, lowerer::Lowerer, + predicates::owned::builders::common::function_use::FunctionCallBuilder, + }, +}; +use vir_crate::{ + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super::super::super) struct OwnedNonAliasedSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + inner: FunctionCallBuilder<'l, 'p, 'v, 'tcx, G>, +} + +impl<'l, 'p, 'v, 'tcx, G> OwnedNonAliasedSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super::super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + place: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let arguments = vec![place, address]; + let inner = FunctionCallBuilder::new( + lowerer, + "snap_owned_non_aliased", + context, + ty, + generics, + arguments, + position, + )?; + Ok(Self { inner }) + } + + pub(in super::super::super::super::super) fn build( + self, + ) -> SpannedEncodingResult { + self.inner.build() + } + + pub(in super::super::super::super::super) fn add_custom_argument( + &mut self, + argument: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + self.inner.arguments.push(argument); + Ok(()) + } + + pub(in super::super::super::super::super) fn add_lifetime_arguments( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_lifetime_arguments() + } + + pub(in super::super::super::super::super) fn add_const_arguments( + &mut self, + ) -> SpannedEncodingResult<()> { + self.inner.add_const_arguments() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/mod.rs index ef427252419..6bcb70532ad 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/mod.rs @@ -1,2 +1,4 @@ +pub(super) mod function_decl; +pub(super) mod function_use; pub(super) mod predicate_decl; pub(super) mod predicate_use; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_decl.rs index 768d0b4dd55..d997ab7c237 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_decl.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_decl.rs @@ -1,4 +1,3 @@ -use super::predicate_use::OwnedNonAliasedUseBuilder; use crate::encoder::{ errors::SpannedEncodingResult, middle::core_proof::{ @@ -10,26 +9,27 @@ use crate::encoder::{ owned::builders::{ common::predicate_decl::PredicateDeclBuilder, PredicateDeclBuilderMethods, }, - PredicatesMemoryBlockInterface, + PredicatesMemoryBlockInterface, PredicatesOwnedInterface, }, - references::ReferencesInterface, snapshots::{ - IntoPureSnapshot, IntoSnapshot, SnapshotBytesInterface, SnapshotValidityInterface, - SnapshotValuesInterface, + IntoPureSnapshot, IntoSnapshot, IntoSnapshotLowerer, PredicateKind, + SnapshotValidityInterface, SnapshotValuesInterface, }, type_layouts::TypeLayoutsInterface, }, }; +use prusti_common::config; use vir_crate::{ - common::expression::{GuardedExpressionIterator, QuantifierHelpers}, + common::{ + expression::{GuardedExpressionIterator, QuantifierHelpers}, + position::Positioned, + }, low::{self as vir_low}, - middle as vir_mid, + middle::{self as vir_mid}, }; pub(in super::super::super) struct OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { inner: PredicateDeclBuilder<'l, 'p, 'v, 'tcx>, - place: vir_low::VariableDecl, - root_address: vir_low::VariableDecl, snapshot: vir_low::VariableDecl, slice_len: Option, } @@ -56,18 +56,11 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { } else { None }; + let position = type_decl.position(); Ok(Self { - place: vir_low::VariableDecl::new("place", lowerer.place_type()?), - root_address: vir_low::VariableDecl::new("root_address", lowerer.address_type()?), snapshot: vir_low::VariableDecl::new("snapshot", ty.to_snapshot(lowerer)?), slice_len, - inner: PredicateDeclBuilder::new( - lowerer, - "OwnedNonAliased", - ty, - type_decl, - Default::default(), - )?, + inner: PredicateDeclBuilder::new(lowerer, "OwnedNonAliased", ty, type_decl, position)?, }) } @@ -76,9 +69,11 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { } pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { - self.inner.parameters.push(self.place.clone()); - self.inner.parameters.push(self.root_address.clone()); - self.inner.parameters.push(self.snapshot.clone()); + self.inner.parameters.push(self.inner.place.clone()); + self.inner.parameters.push(self.inner.address.clone()); + if config::use_snapshot_parameters_in_predicates() { + self.inner.parameters.push(self.snapshot.clone()); + } self.inner.create_lifetime_parameters()?; if let Some(slice_len_mid) = &self.slice_len { let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; @@ -92,17 +87,17 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { self.inner.add_validity(&self.snapshot) } - fn compute_address(&self) -> SpannedEncodingResult { - use vir_low::macros::*; - let compute_address = ty!(Address); - let expression = expr! { - ComputeAddress::compute_address( - [self.place.clone().into()], - [self.root_address.clone().into()] - ) - }; - Ok(expression) - } + // fn compute_address(&self) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let compute_address = ty!(Address); + // let expression = expr! { + // ComputeAddress::compute_address( + // [self.inner.place.clone().into()], + // [self.inner.address.clone().into()] + // ) + // }; + // Ok(expression) + // } fn size_of(&mut self) -> SpannedEncodingResult { self.inner @@ -118,10 +113,11 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super::super) fn add_base_memory_block(&mut self) -> SpannedEncodingResult<()> { use vir_low::macros::*; - let compute_address = self.compute_address()?; + // let compute_address = self.compute_address()?; let size_of = self.size_of()?; + let address = &self.inner.address; let expression = expr! { - acc(MemoryBlock([compute_address], [size_of])) + acc(MemoryBlock(address, [size_of])) }; self.inner.add_conjunct(expression) } @@ -130,10 +126,12 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { &mut self, ) -> SpannedEncodingResult<()> { use vir_low::macros::*; - let compute_address = self.compute_address()?; + // let compute_address = self.compute_address()?; let padding_size = self.padding_size()?; + let address = &self.inner.address; let expression = expr! { - acc(MemoryBlock([compute_address], [padding_size])) + // acc(MemoryBlock([compute_address], [padding_size])) + acc(MemoryBlock(address, [padding_size])) }; self.inner.add_conjunct(expression) } @@ -148,7 +146,8 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { let bytes = self .inner .lowerer - .encode_memory_block_bytes_expression(self.compute_address()?, size_of)?; + // .encode_memory_block_bytes_expression(self.compute_address()?, size_of)?; + .encode_memory_block_bytes_expression(self.inner.address.clone().into(), size_of)?; let to_bytes = ty! { Bytes }; let expression = expr! { [bytes] == (Snap::to_bytes([snapshot])) @@ -162,20 +161,20 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { self.add_bytes_snapshot_equality_with(self.inner.ty, self.snapshot.clone().into()) } - pub(in super::super::super) fn add_bytes_address_snapshot_equality( - &mut self, - ) -> SpannedEncodingResult<()> { - let address_type = self.inner.lowerer.reference_address_type(self.inner.ty)?; - self.inner - .lowerer - .encode_snapshot_to_bytes_function(&address_type)?; - let target_address_snapshot = self.inner.lowerer.reference_address_snapshot( - self.inner.ty, - self.snapshot.clone().into(), - self.inner.position, - )?; - self.add_bytes_snapshot_equality_with(&address_type, target_address_snapshot) - } + // pub(in super::super::super) fn add_bytes_address_snapshot_equality( + // &mut self, + // ) -> SpannedEncodingResult<()> { + // let address_type = self.inner.lowerer.reference_address_type(self.inner.ty)?; + // self.inner + // .lowerer + // .encode_snapshot_to_bytes_function(&address_type)?; + // let target_address_snapshot = self.inner.lowerer.reference_address_snapshot( + // self.inner.ty, + // self.snapshot.clone().into(), + // self.inner.position, + // )?; + // self.add_bytes_snapshot_equality_with(&address_type, target_address_snapshot) + // } pub(in super::super::super) fn add_field_predicate( &mut self, @@ -184,27 +183,30 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { let field_place = self.inner.lowerer.encode_field_place( self.inner.ty, field, - self.place.clone().into(), + self.inner.place.clone().into(), self.inner.position, )?; - let field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + let field_address = self.inner.lowerer.encode_field_address( self.inner.ty, field, - self.snapshot.clone().into(), - Default::default(), + self.inner.address.clone().into(), + self.inner.position, )?; - let mut builder = OwnedNonAliasedUseBuilder::new( - self.inner.lowerer, + // let field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + // self.inner.ty, + // field, + // self.snapshot.clone().into(), + // Default::default(), + // )?; + let expression = self.inner.lowerer.owned_non_aliased( CallContext::BuiltinMethod, &field.ty, &field.ty, field_place, - self.root_address.clone().into(), - field_snapshot, + field_address, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let expression = builder.build(); self.inner.add_conjunct(expression) } @@ -216,29 +218,34 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { let discriminant_place = self.inner.lowerer.encode_field_place( self.inner.ty, &discriminant_field, - self.place.clone().into(), + self.inner.place.clone().into(), self.inner.position, )?; - let discriminant_call = self.inner.lowerer.obtain_enum_discriminant( - self.snapshot.clone().into(), + let discriminant_address = self.inner.lowerer.encode_field_address( self.inner.ty, + &discriminant_field, + self.inner.address.clone().into(), self.inner.position, )?; - let discriminant_snapshot = self.inner.lowerer.construct_constant_snapshot( - &decl.discriminant_type, - discriminant_call, + let _discriminant_call = self.inner.lowerer.obtain_enum_discriminant( + self.snapshot.clone().into(), + self.inner.ty, self.inner.position, )?; - let builder = OwnedNonAliasedUseBuilder::new( - self.inner.lowerer, + // let discriminant_snapshot = self.inner.lowerer.construct_constant_snapshot( + // &decl.discriminant_type, + // discriminant_call, + // self.inner.position, + // )?; + let expression = self.inner.lowerer.owned_non_aliased( CallContext::BuiltinMethod, &decl.discriminant_type, &decl.discriminant_type, discriminant_place, - self.root_address.clone().into(), - discriminant_snapshot, + discriminant_address, + None, + self.inner.position, )?; - let expression = builder.build(); self.inner.add_conjunct(expression) } @@ -247,11 +254,15 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { target_type: &vir_mid::Type, lifetime: &vir_mid::ty::LifetimeConst, ) -> SpannedEncodingResult<()> { + let place = self.inner.place.clone(); + let address = self.inner.address.clone(); self.inner.add_unique_ref_target_predicate( target_type, lifetime, - &self.place, - &self.snapshot, + place.into(), + address, + // &self.snapshot, + false, ) } @@ -260,10 +271,18 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { target_type: &vir_mid::Type, lifetime: &vir_mid::ty::LifetimeConst, ) -> SpannedEncodingResult<()> { - self.inner - .add_frac_ref_target_predicate(target_type, lifetime, &self.place, &self.snapshot) + let place = self.inner.place.clone(); + let address = self.inner.address.clone(); + self.inner.add_frac_ref_target_predicate( + target_type, + lifetime, + place.into(), + address, + // &self.snapshot, + ) } + // FIXME: Code duplication. pub(in super::super::super) fn get_slice_len( &self, ) -> SpannedEncodingResult { @@ -301,27 +320,30 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { let array_length_int = self.inner.array_length_int(array_length_mid)?; let element_place = self.inner.lowerer.encode_index_place( self.inner.ty, - self.place.clone().into(), + self.inner.place.clone().into(), index.clone().into(), self.inner.position, )?; - let element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( - self.snapshot.clone().into(), - index_int.clone(), + let element_address = self.inner.lowerer.encode_index_address( + self.inner.ty, + self.inner.address.clone().into(), + index.clone().into(), self.inner.position, )?; - let mut builder = OwnedNonAliasedUseBuilder::new( - self.inner.lowerer, + // let element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( + // self.snapshot.clone().into(), + // index_int.clone(), + // self.inner.position, + // )?; + let element_predicate_acc = self.inner.lowerer.owned_non_aliased( CallContext::BuiltinMethod, element_type, element_type, element_place, - self.root_address.clone().into(), - element_snapshot, + element_address, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let element_predicate_acc = builder.build(); let elements = vir_low::Expression::forall( vec![index], vec![vir_low::Trigger::new(vec![element_predicate_acc.clone()])], @@ -335,16 +357,46 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super::super) fn create_variant_predicate( &mut self, + decl: &vir_mid::type_decl::Enum, discriminant_value: vir_mid::DiscriminantValue, variant: &vir_mid::type_decl::Struct, variant_type: &vir_mid::Type, ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)> { use vir_low::macros::*; - let discriminant_call = self.inner.lowerer.obtain_enum_discriminant( - self.snapshot.clone().into(), - self.inner.ty, - self.inner.position, - )?; + let discriminant_call = if config::use_snapshot_parameters_in_predicates() { + self.inner.lowerer.obtain_enum_discriminant( + self.snapshot.clone().into(), + self.inner.ty, + self.inner.position, + )? + } else { + let discriminant_field = decl.discriminant_field(); + let discriminant_place = self.inner.lowerer.encode_field_place( + self.inner.ty, + &discriminant_field, + self.inner.place.clone().into(), + self.inner.position, + )?; + let discriminant_address = self.inner.lowerer.encode_field_address( + self.inner.ty, + &discriminant_field, + self.inner.address.clone().into(), + self.inner.position, + )?; + let discriminant_snapshot = self.inner.lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + &decl.discriminant_type, + &decl.discriminant_type, + discriminant_place, + discriminant_address, + self.inner.position, + )?; + self.inner.lowerer.obtain_constant_value( + &decl.discriminant_type, + discriminant_snapshot, + self.inner.position, + )? + }; let guard = expr! { [ discriminant_call ] == [ discriminant_value.into() ] }; @@ -352,27 +404,30 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { let variant_place = self.inner.lowerer.encode_enum_variant_place( self.inner.ty, &variant_index, - self.place.clone().into(), + self.inner.place.clone().into(), self.inner.position, )?; - let variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + let variant_address = self.inner.lowerer.encode_enum_variant_address( self.inner.ty, &variant_index, - self.snapshot.clone().into(), + self.inner.address.clone().into(), self.inner.position, )?; - let mut builder = OwnedNonAliasedUseBuilder::new( - self.inner.lowerer, + // let variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + // self.inner.ty, + // &variant_index, + // self.snapshot.clone().into(), + // self.inner.position, + // )?; + let predicate = self.inner.lowerer.owned_non_aliased( CallContext::BuiltinMethod, variant_type, variant_type, variant_place, - self.root_address.clone().into(), - variant_snapshot, + variant_address, + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let predicate = builder.build(); Ok((guard, predicate)) } @@ -383,4 +438,313 @@ impl<'l, 'p, 'v, 'tcx> OwnedNonAliasedBuilder<'l, 'p, 'v, 'tcx> { self.inner .add_conjunct(variant_predicates.into_iter().create_match()) } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult> { + self.inner + .add_structural_invariant(decl, PredicateKind::Owned) + } + + // pub(in super::super::super) fn add_structural_invariant( + // &mut self, + // decl: &vir_mid::type_decl::Struct, + // ) -> SpannedEncodingResult<()> { + // if let Some(invariant) = &decl.structural_invariant { + // let mut encoder = SelfFramingAssertionToSnapshot::for_predicate_body( + // self.inner.place.clone(), + // self.inner.address.clone(), + // PredicateKind::Owned, + // ); + // // let mut encoder = PredicateAssertionEncoder { + // // place: &self.inner.place, + // // address: &self.inner.address, + // // snap_calls: Default::default(), + // // }; + // for assertion in invariant { + // let low_assertion = + // encoder.expression_to_snapshot(self.inner.lowerer, assertion, true)?; + // self.inner.add_conjunct(low_assertion)?; + // } + // } + // Ok(()) + // } } + +// // FIXME: Move this to its own module. +// FIXME: This should be replaced by prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/self_framing.rs +// struct PredicateAssertionEncoder<'a> { +// place: &'a vir_low::VariableDecl, +// address: &'a vir_low::VariableDecl, +// /// Mapping from place to snapshot. We use a vector because we need to know +// /// the insertion order. +// snap_calls: Vec<(vir_mid::Expression, vir_low::Expression)>, +// } + +// impl<'a> PredicateAssertionEncoder<'a> { +// // FIXME: Code duplication. +// fn pointer_deref_into_address<'p, 'v, 'tcx>( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// place: &vir_mid::Expression, +// ) -> SpannedEncodingResult { +// if let Some(deref_place) = place.get_last_dereferenced_pointer() { +// let base_snapshot = self.expression_to_snapshot(lowerer, deref_place, true)?; +// let ty = deref_place.get_type(); +// lowerer.pointer_address(ty, base_snapshot, place.position()) +// } else { +// unreachable!() +// } +// // match place { +// // vir_mid::Expression::Deref(deref) => { +// // let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, true)?; +// // let ty = deref.base.get_type(); +// // assert!(ty.is_pointer()); +// // lowerer.pointer_address(ty, base_snapshot, place.position()) +// // } +// // _ => unreachable!(), +// // } +// } +// } + +// impl<'a, 'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for PredicateAssertionEncoder<'a> { +// fn expression_to_snapshot( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// expression: &vir_mid::Expression, +// expect_math_bool: bool, +// ) -> SpannedEncodingResult { +// for (place, call) in &self.snap_calls { +// if place == expression { +// return Ok(call.clone()); +// } +// } +// self.expression_to_snapshot_impl(lowerer, expression, expect_math_bool) +// } + +// fn variable_to_snapshot( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// variable: &vir_mid::VariableDecl, +// ) -> SpannedEncodingResult { +// assert!(variable.is_self_variable(), "{} must be self", variable); +// Ok(vir_low::VariableDecl { +// name: variable.name.clone(), +// ty: self.type_to_snapshot(lowerer, &variable.ty)?, +// }) +// } + +// fn labelled_old_to_snapshot( +// &mut self, +// _lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// _old: &vir_mid::LabelledOld, +// _expect_math_bool: bool, +// ) -> SpannedEncodingResult { +// unreachable!("Old expression are not allowed in predicates"); +// } + +// fn func_app_to_snapshot( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// app: &vir_mid::FuncApp, +// expect_math_bool: bool, +// ) -> SpannedEncodingResult { +// todo!() +// } + +// fn binary_op_to_snapshot( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// op: &vir_mid::BinaryOp, +// expect_math_bool: bool, +// ) -> SpannedEncodingResult { +// // TODO: Create impl versions of each method so that I can override +// // without copying. +// let mut introduced_snap = false; +// if op.op_kind == vir_mid::BinaryOpKind::And { +// if let box vir_mid::Expression::AccPredicate(expression) = &op.left { +// if expression.predicate.is_owned_non_aliased() { +// introduced_snap = true; +// } +// } +// } +// let expression = self.binary_op_to_snapshot_impl(lowerer, op, expect_math_bool)?; +// if introduced_snap { +// // TODO: Use the snap calls from this vector instead of generating +// // on demand. This must always succeed because we require +// // expressions to be framed. +// self.snap_calls.pop(); +// } +// Ok(expression) +// } + +// fn acc_predicate_to_snapshot( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// acc_predicate: &vir_mid::AccPredicate, +// expect_math_bool: bool, +// ) -> SpannedEncodingResult { +// assert!(expect_math_bool); +// let expression = match &*acc_predicate.predicate { +// vir_mid::Predicate::OwnedNonAliased(predicate) => { +// let ty = predicate.place.get_type(); +// let place = lowerer.encode_expression_as_place(&predicate.place)?; +// let address = self.pointer_deref_into_address(lowerer, &predicate.place)?; +// let snapshot = true.into(); +// let acc = lowerer.owned_non_aliased( +// CallContext::Procedure, +// ty, +// ty, +// place.clone(), +// address.clone(), +// snapshot, +// None, +// )?; +// let snap_call = lowerer.owned_non_aliased_snap( +// CallContext::BuiltinMethod, +// ty, +// ty, +// place, +// address, +// predicate.place.position(), +// )?; +// self.snap_calls.push((predicate.place.clone(), snap_call)); +// acc +// } +// vir_mid::Predicate::MemoryBlockHeap(predicate) => { +// let place = lowerer.encode_expression_as_place(&predicate.address)?; +// let address = self.pointer_deref_into_address(lowerer, &predicate.address)?; +// use vir_low::macros::*; +// let compute_address = ty!(Address); +// let address = expr! { +// ComputeAddress::compute_address([place], [address]) +// }; +// let size = +// self.expression_to_snapshot(lowerer, &predicate.size, expect_math_bool)?; +// lowerer.encode_memory_block_stack_acc(address, size, acc_predicate.position)? +// } +// vir_mid::Predicate::MemoryBlockHeapDrop(predicate) => { +// let place = self.pointer_deref_into_address(lowerer, &predicate.address)?; +// let size = +// self.expression_to_snapshot(lowerer, &predicate.size, expect_math_bool)?; +// lowerer.encode_memory_block_heap_drop_acc(place, size, acc_predicate.position)? +// } +// _ => unimplemented!("{acc_predicate}"), +// }; +// Ok(expression) +// } + +// fn field_to_snapshot( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// field: &vir_mid::Field, +// expect_math_bool: bool, +// ) -> SpannedEncodingResult { +// match &*field.base { +// vir_mid::Expression::Local(local) => { +// assert!(local.variable.is_self_variable()); +// let field_place = lowerer.encode_field_place( +// &local.variable.ty, +// &field.field, +// self.inner.place.clone().into(), +// field.position, +// )?; +// lowerer.owned_non_aliased_snap( +// CallContext::BuiltinMethod, +// &field.field.ty, +// &field.field.ty, +// field_place, +// self.inner.address.clone().into(), +// local.position, +// ) +// } +// _ => { +// // FIXME: Code duplication because Rust does not have syntax for calling +// // overriden methods. +// let base_snapshot = +// self.expression_to_snapshot(lowerer, &field.base, expect_math_bool)?; +// let result = if field.field.is_discriminant() { +// let ty = field.base.get_type(); +// // FIXME: Create a method for obtainging the discriminant type. +// let type_decl = lowerer.encoder.get_type_decl_mid(ty)?; +// let enum_decl = type_decl.unwrap_enum(); +// let discriminant_call = +// lowerer.obtain_enum_discriminant(base_snapshot, ty, field.position)?; +// lowerer.construct_constant_snapshot( +// &enum_decl.discriminant_type, +// discriminant_call, +// field.position, +// )? +// } else { +// lowerer.obtain_struct_field_snapshot( +// field.base.get_type(), +// &field.field, +// base_snapshot, +// field.position, +// )? +// }; +// self.ensure_bool_expression(lowerer, field.get_type(), result, expect_math_bool) +// } +// } +// } + +// // FIXME: Code duplication. +// fn deref_to_snapshot( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// deref: &vir_mid::Deref, +// expect_math_bool: bool, +// ) -> SpannedEncodingResult { +// let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, expect_math_bool)?; +// let ty = deref.base.get_type(); +// let result = if ty.is_reference() { +// lowerer.reference_target_current_snapshot(ty, base_snapshot, Default::default())? +// } else { +// let aliased_root_place = lowerer.encode_aliased_place_root(deref.position)?; +// let address = lowerer.pointer_address(ty, base_snapshot, deref.position)?; +// lowerer.owned_non_aliased_snap( +// CallContext::BuiltinMethod, +// &deref.ty, +// &deref.ty, +// aliased_root_place, +// address, +// deref.position, +// )? +// // snap_owned_non_aliased$I32(aliased_place_root(), destructor$Snap$ptr$I32$$address(snap_owned_non_aliased$ptr$I32(field_place$$struct$m_T5$$$f$p2(place), address))) + +// // FIXME: This should be unreachable. Most likely, in predicates we should use snap +// // functions. +// // let heap = vir_low::VariableDecl::new("predicate_heap$", lowerer.heap_type()?); +// // lowerer.pointer_target_snapshot_in_heap( +// // deref.base.get_type(), +// // heap, +// // base_snapshot, +// // deref.position, +// // )? +// }; +// self.ensure_bool_expression(lowerer, deref.get_type(), result, expect_math_bool) +// } + +// fn owned_non_aliased_snap( +// &mut self, +// lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// ty: &vir_mid::Type, +// pointer_snapshot: &vir_mid::Expression, +// ) -> SpannedEncodingResult { +// unimplemented!() +// } + +// fn call_context(&self) -> CallContext { +// CallContext::BuiltinMethod +// } + +// // fn unfolding_to_snapshot( +// // &mut self, +// // lowerer: &mut Lowerer<'p, 'v, 'tcx>, +// // unfolding: &vir_mid::Unfolding, +// // expect_math_bool: bool, +// // ) -> SpannedEncodingResult { +// // todo!() +// // } +// } diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_use.rs index f40e59eee36..bc1a826b325 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_use.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_use.rs @@ -1,11 +1,16 @@ use crate::encoder::{ errors::SpannedEncodingResult, middle::core_proof::{ - builtin_methods::CallContext, lowerer::Lowerer, - predicates::owned::builders::common::predicate_use::PredicateUseBuilder, + builtin_methods::CallContext, + lowerer::Lowerer, + predicates::{ + owned::builders::common::predicate_use::PredicateUseBuilder, PredicatesOwnedInterface, + }, }, }; +use prusti_common::config; use vir_crate::{ + common::expression::BinaryOperationHelpers, low::{self as vir_low}, middle::{ self as vir_mid, @@ -18,6 +23,7 @@ where G: WithLifetimes + WithConstArguments, { inner: PredicateUseBuilder<'l, 'p, 'v, 'tcx, G>, + snapshot: Option, } impl<'l, 'p, 'v, 'tcx, G> OwnedNonAliasedUseBuilder<'l, 'p, 'v, 'tcx, G> @@ -30,23 +36,57 @@ where ty: &'l vir_mid::Type, generics: &'l G, place: vir_low::Expression, - root_address: vir_low::Expression, - snapshot: vir_low::Expression, + address: vir_low::Expression, + position: vir_mid::Position, ) -> SpannedEncodingResult { + let arguments = vec![place, address]; let inner = PredicateUseBuilder::new( lowerer, "OwnedNonAliased", context, ty, generics, - vec![place, root_address, snapshot], - Default::default(), + arguments, + position, )?; - Ok(Self { inner }) + Ok(Self { + inner, + snapshot: None, + }) + } + + pub(in super::super::super::super::super) fn build( + mut self, + ) -> SpannedEncodingResult { + let expression = if let Some(snapshot) = self.snapshot.take() { + let snap_call = self.inner.lowerer.owned_non_aliased_snap( + self.inner.context, + self.inner.ty, + self.inner.generics, + self.inner.arguments[0].clone(), + self.inner.arguments[1].clone(), + self.inner.position, + )?; + vir_low::Expression::and( + self.inner.build(), + vir_low::Expression::equals(snapshot, snap_call), + ) + } else { + self.inner.build() + }; + Ok(expression) } - pub(in super::super::super::super::super) fn build(self) -> vir_low::Expression { - self.inner.build() + pub(in super::super::super::super::super) fn add_snapshot_argument( + &mut self, + snapshot: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + if config::use_snapshot_parameters_in_predicates() { + self.inner.arguments.push(snapshot); + } else { + self.snapshot = Some(snapshot); + } + Ok(()) } pub(in super::super::super::super::super) fn add_lifetime_arguments( diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_current_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_current_decl.rs new file mode 100644 index 00000000000..8606c60b358 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_current_decl.rs @@ -0,0 +1,477 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + lifetimes::LifetimesInterface, + lowerer::Lowerer, + predicates::{ + owned::builders::common::function_decl::FunctionDeclBuilder, PredicatesOwnedInterface, + }, + snapshots::{IntoPureSnapshot, PredicateKind}, + type_layouts::TypeLayoutsInterface, + }, +}; +use vir_crate::{ + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super) struct UniqueRefCurrentSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + inner: FunctionDeclBuilder<'l, 'p, 'v, 'tcx>, + // place: vir_low::VariableDecl, + address: vir_low::VariableDecl, + reference_lifetime: vir_low::VariableDecl, + slice_len: Option, +} + +impl<'l, 'p, 'v, 'tcx> UniqueRefCurrentSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let slice_len = if ty.is_slice() { + Some(vir_mid::VariableDecl::new( + "slice_len", + lowerer.size_type_mid()?, + )) + } else { + None + }; + let function_name = "snap_current_unique_ref"; + Ok(Self { + address: vir_low::VariableDecl::new("address", lowerer.address_type()?), + reference_lifetime: vir_low::VariableDecl::new( + "reference_lifetime", + lowerer.lifetime_type()?, + ), + slice_len, + inner: FunctionDeclBuilder::new( + lowerer, + function_name, + ty, + type_decl, + Default::default(), + )?, + }) + } + + pub(in super::super::super) fn get_snapshot_postconditions( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_postconditions() + } + + pub(in super::super::super) fn get_snapshot_body( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_body() + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult { + self.inner.build() + } + + pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.inner.place.clone()); + self.inner.parameters.push(self.address.clone()); + self.inner.parameters.push(self.reference_lifetime.clone()); + self.inner.create_lifetime_parameters()?; + if let Some(slice_len_mid) = &self.slice_len { + let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; + self.inner.parameters.push(slice_len); + } + self.inner.create_const_parameters()?; + Ok(()) + } + + // // FIXME: Code duplication. + // pub(in super::super::super) fn get_slice_len( + // &self, + // ) -> SpannedEncodingResult { + // Ok(self.slice_len.as_ref().unwrap().clone()) + // } + + fn unique_ref_predicate( + &mut self, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + reference_lifetime: vir_low::Expression, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let slice_len = if let Some(slice_len_mid) = &self.slice_len { + let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; + Some(slice_len.into()) + } else { + None + }; + self.inner.lowerer.unique_ref( + CallContext::BuiltinMethod, + ty, + generics, + place, + address, + reference_lifetime, + slice_len, + Some(vir_low::Expression::wildcard_permission()), + self.inner.position, + ) + } + + // // FIXME: Code duplication with add_quantified_permission. + // pub(in super::super::super) fn add_quantifiers( + // &mut self, + // array_length_mid: &vir_mid::VariableDecl, + // element_type: &vir_mid::Type, + // ) -> SpannedEncodingResult<()> { + // use vir_low::macros::*; + // let size_type_mid = self.inner.lowerer.size_type_mid()?; + // var_decls! { + // index_int: Int + // }; + // let index = self.inner.lowerer.construct_constant_snapshot( + // &size_type_mid, + // index_int.clone().into(), + // self.inner.position, + // )?; + // let index_validity = self + // .inner + // .lowerer + // .encode_snapshot_valid_call_for_type(index.clone(), &size_type_mid)?; + // let array_length_int = self.inner.array_length_int(array_length_mid)?; + // let element_place = self.inner.lowerer.encode_index_place( + // self.inner.ty, + // self.place.clone().into(), + // index, + // self.inner.position, + // )?; + // let element_predicate_acc = { + // self.owned_predicate( + // element_type, + // element_type, + // element_place.clone(), + // self.address.clone().into(), + // )? + // }; + // let result = self.inner.result()?.into(); + // let element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( + // result, + // index_int.clone().into(), + // self.inner.position, + // )?; + // let element_snap_call = self.inner.lowerer.owned_non_aliased_snap( + // CallContext::BuiltinMethod, + // element_type, + // element_type, + // element_place, + // self.address.clone().into(), + // )?; + // let elements = vir_low::Expression::forall( + // vec![index_int.clone()], + // vec![vir_low::Trigger::new(vec![element_predicate_acc])], + // expr! { + // ([index_validity] && (index_int < [array_length_int])) ==> + // ([element_snapshot] == [element_snap_call]) + // }, + // ); + // self.add_unfolding_postcondition(elements) + // } + + pub(in super::super::super) fn add_snapshot_body_postcondition( + &mut self, + body: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + // let predicate = self.precondition_predicate()?; + // let unfolding = predicate.into_unfolding(body); + self.inner.add_snapshot_body_postcondition(body) + } + + pub(in super::super::super) fn add_snapshot_postcondition( + &mut self, + expression: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + self.inner.add_snapshot_postcondition(expression) + } + + // pub(in super::super::super) fn add_validity_postcondition( + // &mut self, + // ) -> SpannedEncodingResult<()> { + // self.inner.add_validity_postcondition() + // } + + // pub(in super::super::super) fn add_snapshot_len_equal_to_postcondition( + // &mut self, + // array_length_mid: &vir_mid::VariableDecl, + // ) -> SpannedEncodingResult<()> { + // self.inner + // .add_snapshot_len_equal_to_postcondition(array_length_mid) + // } + + pub(in super::super::super) fn add_unique_ref_precondition( + &mut self, + ) -> SpannedEncodingResult<()> { + let predicate = self.precondition_predicate()?; + self.inner.add_precondition(predicate) + } + + fn precondition_predicate(&mut self) -> SpannedEncodingResult { + self.unique_ref_predicate( + self.inner.ty, + self.inner.type_decl, + self.inner.place.clone().into(), + self.address.clone().into(), + self.reference_lifetime.clone().into(), + ) + } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult<()> { + // let precondition_predicate = if self.is_final { + // None + // } else { + // Some(self.precondition_predicate()?) + // }; + let predicate_kind = PredicateKind::UniqueRef { + lifetime: self.reference_lifetime.clone().into(), + is_final: false, + }; + let snap_call = self.field_unique_ref_snap()?; + self.inner + .add_structural_invariant(decl, false, predicate_kind, &snap_call) + } + + // fn compute_address(&self) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let compute_address = ty!(Address); + // let expression = expr! { + // ComputeAddress::compute_address( + // [self.place.clone().into()], + // [self.address.clone().into()] + // ) + // }; + // Ok(expression) + // } + + // fn size_of(&mut self) -> SpannedEncodingResult { + // self.inner + // .lowerer + // .encode_type_size_expression2(self.inner.ty, self.inner.type_decl) + // } + + // fn add_bytes_snapshot_equality_with( + // &mut self, + // snap_ty: &vir_mid::Type, + // snapshot: vir_low::Expression, + // ) -> SpannedEncodingResult<()> { + // use vir_low::macros::*; + // let size_of = self.size_of()?; + // let bytes = self + // .inner + // .lowerer + // .encode_memory_block_bytes_expression(self.compute_address()?, size_of)?; + // let to_bytes = ty! { Bytes }; + // let expression = expr! { + // [bytes] == (Snap::to_bytes([snapshot])) + // }; + // self.add_unfolding_postcondition(expression) + // } + + // pub(in super::super::super) fn add_bytes_snapshot_equality( + // &mut self, + // ) -> SpannedEncodingResult<()> { + // let result = self.inner.result()?.into(); + // self.add_bytes_snapshot_equality_with(self.inner.ty, result) + // } + + // pub(in super::super::super) fn add_bytes_address_snapshot_equality( + // &mut self, + // ) -> SpannedEncodingResult<()> { + // let result = self.inner.result()?.into(); + // let address_type = self.inner.lowerer.reference_address_type(self.inner.ty)?; + // self.inner + // .lowerer + // .encode_snapshot_to_bytes_function(&address_type)?; + // let target_address_snapshot = self.inner.lowerer.reference_address_snapshot( + // self.inner.ty, + // result, + // self.inner.position, + // )?; + // self.add_bytes_snapshot_equality_with(&address_type, target_address_snapshot) + // } + + // pub(in super::super::super) fn create_field_snapshot_equality( + // &mut self, + // field: &vir_mid::FieldDecl, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let result = self.inner.result()?; + // let field_place = self.inner.lowerer.encode_field_place( + // self.inner.ty, + // field, + // self.place.clone().into(), + // self.inner.position, + // )?; + // let field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + // self.inner.ty, + // field, + // result.into(), + // self.inner.position, + // )?; + // let snap_call = self.inner.lowerer.owned_non_aliased_snap( + // CallContext::BuiltinMethod, + // &field.ty, + // &field.ty, + // field_place, + // self.address.clone().into(), + // )?; + // Ok(expr! { + // [field_snapshot] == [snap_call] + // }) + // } + + // pub(in super::super::super) fn create_discriminant_snapshot_equality( + // &mut self, + // decl: &vir_mid::type_decl::Enum, + // ) -> SpannedEncodingResult { + // use vir_low::macros::*; + // let result = self.inner.result()?; + // let discriminant_snapshot = self.inner.lowerer.obtain_enum_discriminant( + // result.into(), + // self.inner.ty, + // self.inner.position, + // )?; + // let discriminant_field = decl.discriminant_field(); + // let discriminant_place = self.inner.lowerer.encode_field_place( + // self.inner.ty, + // &discriminant_field, + // self.place.clone().into(), + // self.inner.position, + // )?; + // let snap_call = self.inner.lowerer.owned_non_aliased_snap( + // CallContext::BuiltinMethod, + // &decl.discriminant_type, + // &decl.discriminant_type, + // discriminant_place, + // self.address.clone().into(), + // )?; + // let snap_call_int = self.inner.lowerer.obtain_constant_value( + // &decl.discriminant_type, + // snap_call, + // self.inner.position, + // )?; + // Ok(expr! { + // [discriminant_snapshot] == [snap_call_int] + // }) + // } + + // pub(in super::super::super) fn create_variant_snapshot_equality( + // &mut self, + // discriminant_value: vir_mid::DiscriminantValue, + // variant: &vir_mid::type_decl::Struct, + // ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)> { + // use vir_low::macros::*; + // let result = self.inner.result()?; + // let discriminant_call = self.inner.lowerer.obtain_enum_discriminant( + // result.clone().into(), + // self.inner.ty, + // self.inner.position, + // )?; + // let guard = expr! { + // [ discriminant_call ] == [ discriminant_value.into() ] + // }; + // let variant_index = variant.name.clone().into(); + // let variant_place = self.inner.lowerer.encode_enum_variant_place( + // self.inner.ty, + // &variant_index, + // self.place.clone().into(), + // self.inner.position, + // )?; + // let variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + // self.inner.ty, + // &variant_index, + // result.into(), + // self.inner.position, + // )?; + // let variant_type = self.inner.ty.clone().variant(variant_index); + // let snap_call = self.inner.lowerer.owned_non_aliased_snap( + // CallContext::BuiltinMethod, + // &variant_type, + // &variant_type, + // variant_place, + // self.address.clone().into(), + // )?; + // let equality = expr! { + // [variant_snapshot] == [snap_call] + // }; + // Ok((guard, equality)) + // } + + // FIXME: Code duplication. + fn slice_len(&mut self) -> SpannedEncodingResult> { + self.slice_len + .as_ref() + .map(|slice_len_mid| slice_len_mid.to_pure_snapshot(self.inner.lowerer)) + .transpose() + } + + // FIXME: Code duplication. + fn slice_len_expression(&mut self) -> SpannedEncodingResult> { + Ok(self.slice_len()?.map(|slice_len| slice_len.into())) + } + + pub(in super::super::super) fn create_field_snapshot_equality( + &mut self, + field: &vir_mid::FieldDecl, + ) -> SpannedEncodingResult { + let unique_ref_call = self.field_unique_ref_snap()?; + self.inner + .create_field_snapshot_equality(field, unique_ref_call) + } + + fn field_unique_ref_snap( + &mut self, + ) -> SpannedEncodingResult< + impl Fn( + &mut FunctionDeclBuilder, + &vir_mid::FieldDecl, + vir_low::Expression, + vir_low::Expression, + ) -> SpannedEncodingResult, + > { + let target_slice_len = self.slice_len_expression()?; + // let address: vir_low::Expression = self.address.clone().into(); + // let address = std::rc::Rc::new(address); + let lifetime: vir_low::Expression = self.reference_lifetime.clone().into(); + let lifetime = std::rc::Rc::new(lifetime); + let is_final = false; // FIXME: Non-used parameter. + Ok( + move |builder: &mut FunctionDeclBuilder, + field: &vir_mid::FieldDecl, + field_place, + field_address| { + builder.lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + &field.ty, + &field.ty, + field_place, + // (*address).clone(), + field_address, + (*lifetime).clone(), + target_slice_len.clone(), + is_final, + builder.position, + ) + }, + ) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_current_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_current_use.rs new file mode 100644 index 00000000000..1ea01214377 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_current_use.rs @@ -0,0 +1,65 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, lowerer::Lowerer, + predicates::owned::builders::common::function_use::FunctionCallBuilder, + }, +}; +use vir_crate::{ + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super) struct UniqueRefCurrentSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + inner: FunctionCallBuilder<'l, 'p, 'v, 'tcx, G>, +} + +impl<'l, 'p, 'v, 'tcx, G> UniqueRefCurrentSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + place: vir_low::Expression, + root_address: vir_low::Expression, + reference_lifetime: vir_low::Expression, + target_slice_len: Option, + ) -> SpannedEncodingResult { + let mut arguments = vec![place, root_address, reference_lifetime]; + if let Some(len) = target_slice_len { + arguments.push(len); + } + let name = "snap_current_unique_ref"; + let inner = FunctionCallBuilder::new( + lowerer, + name, + context, + ty, + generics, + arguments, + Default::default(), + )?; + Ok(Self { inner }) + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult { + self.inner.build() + } + + pub(in super::super::super) fn add_lifetime_arguments(&mut self) -> SpannedEncodingResult<()> { + self.inner.add_lifetime_arguments() + } + + pub(in super::super::super) fn add_const_arguments(&mut self) -> SpannedEncodingResult<()> { + self.inner.add_const_arguments() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_final_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_final_decl.rs new file mode 100644 index 00000000000..24ad09206fa --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_final_decl.rs @@ -0,0 +1,233 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + function_gas::FunctionGasInterface, + lifetimes::LifetimesInterface, + lowerer::{DomainsLowererInterface, Lowerer}, + predicates::{ + owned::builders::common::function_decl::FunctionDeclBuilder, PredicatesOwnedInterface, + }, + snapshots::{IntoPureSnapshot, IntoSnapshot, PredicateKind}, + type_layouts::TypeLayoutsInterface, + }, +}; +use vir_crate::{ + common::{ + expression::{ExpressionIterator, QuantifierHelpers}, + identifier::WithIdentifier, + }, + low::{self as vir_low}, + middle::{self as vir_mid}, +}; + +pub(in super::super::super) struct UniqueRefFinalSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + inner: FunctionDeclBuilder<'l, 'p, 'v, 'tcx>, + // place: vir_low::VariableDecl, + address: vir_low::VariableDecl, + reference_lifetime: vir_low::VariableDecl, + slice_len: Option, +} + +impl<'l, 'p, 'v, 'tcx> UniqueRefFinalSnapFunctionBuilder<'l, 'p, 'v, 'tcx> { + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + ty: &'l vir_mid::Type, + type_decl: &'l vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let slice_len = if ty.is_slice() { + Some(vir_mid::VariableDecl::new( + "slice_len", + lowerer.size_type_mid()?, + )) + } else { + None + }; + let function_name = "snap_final_unique_ref"; + Ok(Self { + address: vir_low::VariableDecl::new("address", lowerer.address_type()?), + reference_lifetime: vir_low::VariableDecl::new( + "reference_lifetime", + lowerer.lifetime_type()?, + ), + slice_len, + inner: FunctionDeclBuilder::new( + lowerer, + function_name, + ty, + type_decl, + Default::default(), + )?, + }) + } + + pub(in super::super::super) fn get_snapshot_postconditions( + &self, + ) -> SpannedEncodingResult> { + self.inner.get_snapshot_postconditions() + } + + pub(in super::super::super) fn get_snapshot_body( + &self, + ) -> SpannedEncodingResult> { + let body = self.inner.get_snapshot_body()?; + assert_eq!(body.len(), 0); + Ok(body) + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult<(String, vir_low::Type)> { + use vir_low::macros::*; + let return_type = self.inner.ty.to_snapshot(self.inner.lowerer)?; + let function_name = format!( + "{}${}", + self.inner.function_name, + self.inner.ty.get_identifier() + ); + let gas = self.inner.lowerer.function_gas_parameter()?; + let parameters = { + let mut parameters = self.inner.parameters.clone(); + parameters.push(gas.clone()); + parameters + }; + let mut arguments_succ_gas: Vec<_> = self + .inner + .parameters + .into_iter() + .map(|parameter| parameter.clone().into()) + .collect(); + let mut arguments_gas = arguments_succ_gas.clone(); + arguments_succ_gas.push( + self.inner + .lowerer + .add_function_gas_level(gas.clone().into())?, + ); + arguments_gas.push(gas.into()); + let call_succ_gas = self.inner.lowerer.create_domain_func_app( + "UniqueRefSnapFunctions", + function_name.clone(), + arguments_succ_gas, + return_type.clone(), + Default::default(), + )?; + let call_gas = vir_low::Expression::domain_function_call( + "UniqueRefSnapFunctions", + function_name.clone(), + arguments_gas, + return_type.clone(), + ); + assert_eq!(self.inner.snapshot_body_posts.len(), 0); + let result: vir_low::Expression = var! { __result: {return_type.clone()} }.into(); + let posts_expression = self + .inner + .snapshot_posts + .into_iter() + .conjoin() + .replace_place(&result, &call_succ_gas); + let axiom_body = vir_low::Expression::forall( + parameters, + vec![vir_low::Trigger::new(vec![call_succ_gas.clone()])], + expr! { + [posts_expression] && ([call_succ_gas] == [call_gas]) + }, + ); + let axiom = vir_low::DomainAxiomDecl { + comment: None, + name: format!("{function_name}$definitional_axiom"), + body: axiom_body, + }; + self.inner + .lowerer + .declare_axiom("UniqueRefSnapFunctions", axiom)?; + Ok((function_name, return_type)) + } + + pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { + self.inner.parameters.push(self.inner.place.clone()); + self.inner.parameters.push(self.address.clone()); + self.inner.parameters.push(self.reference_lifetime.clone()); + self.inner.create_lifetime_parameters()?; + if let Some(slice_len_mid) = &self.slice_len { + let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; + self.inner.parameters.push(slice_len); + } + self.inner.create_const_parameters()?; + Ok(()) + } + + pub(in super::super::super) fn add_snapshot_postcondition( + &mut self, + expression: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + self.inner.add_snapshot_postcondition(expression) + } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult<()> { + let predicate_kind = PredicateKind::UniqueRef { + lifetime: self.reference_lifetime.clone().into(), + is_final: true, + }; + let snap_call = self.field_unique_ref_snap()?; + self.inner + .add_structural_invariant(decl, true, predicate_kind, &snap_call) + } + + // FIXME: Code duplication. + fn slice_len(&mut self) -> SpannedEncodingResult> { + self.slice_len + .as_ref() + .map(|slice_len_mid| slice_len_mid.to_pure_snapshot(self.inner.lowerer)) + .transpose() + } + + // FIXME: Code duplication. + fn slice_len_expression(&mut self) -> SpannedEncodingResult> { + Ok(self.slice_len()?.map(|slice_len| slice_len.into())) + } + + pub(in super::super::super) fn create_field_snapshot_equality( + &mut self, + field: &vir_mid::FieldDecl, + ) -> SpannedEncodingResult { + let unique_ref_call = self.field_unique_ref_snap()?; + self.inner + .create_field_snapshot_equality(field, unique_ref_call) + } + + fn field_unique_ref_snap( + &mut self, + ) -> SpannedEncodingResult< + impl Fn( + &mut FunctionDeclBuilder, + &vir_mid::FieldDecl, + vir_low::Expression, + vir_low::Expression, + ) -> SpannedEncodingResult, + > { + let target_slice_len = self.slice_len_expression()?; + let lifetime: vir_low::Expression = self.reference_lifetime.clone().into(); + let lifetime = std::rc::Rc::new(lifetime); + let is_final = true; // FIXME: Unused field. + Ok( + move |builder: &mut FunctionDeclBuilder, + field: &vir_mid::FieldDecl, + field_place, + field_address| { + builder.lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + &field.ty, + &field.ty, + field_place, + field_address, + (*lifetime).clone(), + target_slice_len.clone(), + is_final, + builder.position, + ) + }, + ) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_final_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_final_use.rs new file mode 100644 index 00000000000..448ce36fb12 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/function_final_use.rs @@ -0,0 +1,81 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, function_gas::FunctionGasInterface, lowerer::Lowerer, + predicates::owned::builders::common::function_use::FunctionCallBuilder, + snapshots::IntoSnapshot, + }, +}; +use vir_crate::{ + common::identifier::WithIdentifier, + low::{self as vir_low}, + middle::{ + self as vir_mid, + operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, + }, +}; + +pub(in super::super::super) struct UniqueRefFinalSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + inner: FunctionCallBuilder<'l, 'p, 'v, 'tcx, G>, +} + +impl<'l, 'p, 'v, 'tcx, G> UniqueRefFinalSnapCallBuilder<'l, 'p, 'v, 'tcx, G> +where + G: WithLifetimes + WithConstArguments, +{ + pub(in super::super::super) fn new( + lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + context: CallContext, + ty: &'l vir_mid::Type, + generics: &'l G, + place: vir_low::Expression, + root_address: vir_low::Expression, + reference_lifetime: vir_low::Expression, + target_slice_len: Option, + ) -> SpannedEncodingResult { + let mut arguments = vec![place, root_address, reference_lifetime]; + if let Some(len) = target_slice_len { + arguments.push(len); + } + let name = "snap_final_unique_ref"; + let inner = FunctionCallBuilder::new( + lowerer, + name, + context, + ty, + generics, + arguments, + Default::default(), + )?; + Ok(Self { inner }) + } + + pub(in super::super::super) fn build(self) -> SpannedEncodingResult { + let return_type = self.inner.ty.to_snapshot(self.inner.lowerer)?; + let mut arguments = self.inner.arguments; + let gas_amount = self.inner.lowerer.function_gas_constant(2)?; + arguments.push(gas_amount); + let call = vir_low::Expression::domain_function_call( + "UniqueRefSnapFunctions", + format!( + "{}${}", + self.inner.function_name, + self.inner.ty.get_identifier() + ), + arguments, + return_type, + ); + Ok(call.set_default_position(self.inner.position)) + } + + pub(in super::super::super) fn add_lifetime_arguments(&mut self) -> SpannedEncodingResult<()> { + self.inner.add_lifetime_arguments() + } + + pub(in super::super::super) fn add_const_arguments(&mut self) -> SpannedEncodingResult<()> { + self.inner.add_const_arguments() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/mod.rs index ef427252419..24a022004da 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/mod.rs @@ -1,2 +1,6 @@ +pub(super) mod function_current_decl; +pub(super) mod function_final_decl; +pub(super) mod function_current_use; +pub(super) mod function_final_use; pub(super) mod predicate_decl; pub(super) mod predicate_use; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/predicate_decl.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/predicate_decl.rs index 280520840fa..b6cf3278b14 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/predicate_decl.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/predicate_decl.rs @@ -1,4 +1,3 @@ -use super::predicate_use::UniqueRefUseBuilder; use crate::encoder::{ errors::SpannedEncodingResult, middle::core_proof::{ @@ -7,15 +6,19 @@ use crate::encoder::{ lifetimes::LifetimesInterface, lowerer::Lowerer, places::PlacesInterface, - predicates::owned::builders::{ - common::predicate_decl::PredicateDeclBuilder, PredicateDeclBuilderMethods, + predicates::{ + owned::builders::{ + common::predicate_decl::PredicateDeclBuilder, PredicateDeclBuilderMethods, + }, + PredicatesOwnedInterface, }, snapshots::{ - IntoPureSnapshot, IntoSnapshot, SnapshotValidityInterface, SnapshotValuesInterface, + IntoPureSnapshot, PredicateKind, SnapshotValidityInterface, SnapshotValuesInterface, }, type_layouts::TypeLayoutsInterface, }, }; + use vir_crate::{ common::expression::{GuardedExpressionIterator, QuantifierHelpers}, low::{self as vir_low}, @@ -24,10 +27,8 @@ use vir_crate::{ pub(in super::super::super) struct UniqueRefBuilder<'l, 'p, 'v, 'tcx> { inner: PredicateDeclBuilder<'l, 'p, 'v, 'tcx>, - place: vir_low::VariableDecl, - root_address: vir_low::VariableDecl, - current_snapshot: vir_low::VariableDecl, - final_snapshot: vir_low::VariableDecl, + // current_snapshot: vir_low::VariableDecl, + // final_snapshot: vir_low::VariableDecl, reference_lifetime: vir_low::VariableDecl, slice_len: Option, } @@ -55,13 +56,11 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { None }; Ok(Self { - place: vir_low::VariableDecl::new("place", lowerer.place_type()?), - root_address: vir_low::VariableDecl::new("root_address", lowerer.address_type()?), - current_snapshot: vir_low::VariableDecl::new( - "current_snapshot", - ty.to_snapshot(lowerer)?, - ), - final_snapshot: vir_low::VariableDecl::new("final_snapshot", ty.to_snapshot(lowerer)?), + // current_snapshot: vir_low::VariableDecl::new( + // "current_snapshot", + // ty.to_snapshot(lowerer)?, + // ), + // final_snapshot: vir_low::VariableDecl::new("final_snapshot", ty.to_snapshot(lowerer)?), reference_lifetime: vir_low::VariableDecl::new( "reference_lifetime", lowerer.lifetime_type()?, @@ -69,7 +68,7 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { slice_len, inner: PredicateDeclBuilder::new( lowerer, - "UniqueRef2", + "UniqueRef", ty, type_decl, Default::default(), @@ -82,11 +81,13 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { } pub(in super::super::super) fn create_parameters(&mut self) -> SpannedEncodingResult<()> { - self.inner.parameters.push(self.place.clone()); - self.inner.parameters.push(self.root_address.clone()); - self.inner.parameters.push(self.current_snapshot.clone()); - self.inner.parameters.push(self.final_snapshot.clone()); + self.inner.parameters.push(self.inner.place.clone()); + self.inner.parameters.push(self.inner.address.clone()); self.inner.parameters.push(self.reference_lifetime.clone()); + // if config::use_snapshot_parameters_in_predicates() { + // self.inner.parameters.push(self.current_snapshot.clone()); + // self.inner.parameters.push(self.final_snapshot.clone()); + // } self.inner.create_lifetime_parameters()?; if let Some(slice_len_mid) = &self.slice_len { let slice_len = slice_len_mid.to_pure_snapshot(self.inner.lowerer)?; @@ -96,9 +97,9 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { Ok(()) } - pub(in super::super::super) fn add_validity(&mut self) -> SpannedEncodingResult<()> { - self.inner.add_validity(&self.current_snapshot) - } + // pub(in super::super::super) fn add_validity(&mut self) -> SpannedEncodingResult<()> { + // self.inner.add_validity(&self.current_snapshot) + // } pub(in super::super::super) fn add_field_predicate( &mut self, @@ -107,35 +108,38 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { let field_place = self.inner.lowerer.encode_field_place( self.inner.ty, field, - self.place.clone().into(), - self.inner.position, - )?; - let current_field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( - self.inner.ty, - field, - self.current_snapshot.clone().into(), + self.inner.place.clone().into(), self.inner.position, )?; - let final_field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + let field_address = self.inner.lowerer.encode_field_address( self.inner.ty, field, - self.final_snapshot.clone().into(), + self.inner.address.clone().into(), self.inner.position, )?; - let mut builder = UniqueRefUseBuilder::new( - self.inner.lowerer, + // let current_field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + // self.inner.ty, + // field, + // self.current_snapshot.clone().into(), + // self.inner.position, + // )?; + // let final_field_snapshot = self.inner.lowerer.obtain_struct_field_snapshot( + // self.inner.ty, + // field, + // self.final_snapshot.clone().into(), + // self.inner.position, + // )?; + let expression = self.inner.lowerer.unique_ref( CallContext::BuiltinMethod, &field.ty, &field.ty, field_place, - self.root_address.clone().into(), - current_field_snapshot, - final_field_snapshot, + field_address, self.reference_lifetime.clone().into(), + None, // FIXME: This should be a proper value + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let expression = builder.build(); self.inner.add_conjunct(expression) } @@ -147,54 +151,87 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { let discriminant_place = self.inner.lowerer.encode_field_place( self.inner.ty, &discriminant_field, - self.place.clone().into(), - self.inner.position, - )?; - let current_discriminant_call = self.inner.lowerer.obtain_enum_discriminant( - self.current_snapshot.clone().into(), - self.inner.ty, - self.inner.position, - )?; - let current_discriminant_snapshot = self.inner.lowerer.construct_constant_snapshot( - &decl.discriminant_type, - current_discriminant_call, + self.inner.place.clone().into(), self.inner.position, )?; - let final_discriminant_call = self.inner.lowerer.obtain_enum_discriminant( - self.final_snapshot.clone().into(), + let discriminant_address = self.inner.lowerer.encode_field_address( self.inner.ty, + &discriminant_field, + self.inner.address.clone().into(), self.inner.position, )?; - let final_discriminant_snapshot = self.inner.lowerer.construct_constant_snapshot( - &decl.discriminant_type, - final_discriminant_call, - self.inner.position, - )?; - let builder = UniqueRefUseBuilder::new( - self.inner.lowerer, + // let current_discriminant_call = self.inner.lowerer.obtain_enum_discriminant( + // self.current_snapshot.clone().into(), + // self.inner.ty, + // self.inner.position, + // )?; + // let current_discriminant_snapshot = self.inner.lowerer.construct_constant_snapshot( + // &decl.discriminant_type, + // current_discriminant_call, + // self.inner.position, + // )?; + // let final_discriminant_call = self.inner.lowerer.obtain_enum_discriminant( + // self.final_snapshot.clone().into(), + // self.inner.ty, + // self.inner.position, + // )?; + // let final_discriminant_snapshot = self.inner.lowerer.construct_constant_snapshot( + // &decl.discriminant_type, + // final_discriminant_call, + // self.inner.position, + // )?; + // let builder = UniqueRefUseBuilder::new( + // self.inner.lowerer, + // CallContext::BuiltinMethod, + // &decl.discriminant_type, + // &decl.discriminant_type, + // discriminant_place, + // self.inner.address.clone().into(), + // current_discriminant_snapshot, + // final_discriminant_snapshot, + // self.reference_lifetime.clone().into(), + // )?; + // let expression = builder.build(); + let expression = self.inner.lowerer.unique_ref( CallContext::BuiltinMethod, &decl.discriminant_type, &decl.discriminant_type, discriminant_place, - self.root_address.clone().into(), - current_discriminant_snapshot, - final_discriminant_snapshot, + discriminant_address, self.reference_lifetime.clone().into(), + None, // FIXME: This should be a proper value + None, + self.inner.position, )?; - let expression = builder.build(); self.inner.add_conjunct(expression) } + pub(in super::super::super) fn add_unique_ref_pointer_predicate( + &mut self, + lifetime: &vir_mid::ty::LifetimeConst, + ) -> SpannedEncodingResult { + let place = self.inner.place.clone(); + let address = self.inner.address.clone(); + self.inner.add_unique_ref_pointer_predicate( + lifetime, place, address, + // &self.current_snapshot, + ) + } + pub(in super::super::super) fn add_unique_ref_target_predicate( &mut self, target_type: &vir_mid::Type, lifetime: &vir_mid::ty::LifetimeConst, ) -> SpannedEncodingResult<()> { + let place = self.inner.place.clone(); + let address = self.inner.address.clone(); self.inner.add_unique_ref_target_predicate( target_type, lifetime, - &self.place, - &self.current_snapshot, + place.into(), + address, + // &self.current_snapshot, + true, ) } @@ -203,11 +240,14 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { target_type: &vir_mid::Type, lifetime: &vir_mid::ty::LifetimeConst, ) -> SpannedEncodingResult<()> { + let place = self.inner.place.clone(); + let address = self.inner.address.clone(); self.inner.add_frac_ref_target_predicate( target_type, lifetime, - &self.place, - &self.current_snapshot, + place.into(), + address, + // &self.current_snapshot, ) } @@ -219,12 +259,13 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super::super) fn add_snapshot_len_equal_to( &mut self, - array_length_mid: &vir_mid::VariableDecl, + _array_length_mid: &vir_mid::VariableDecl, ) -> SpannedEncodingResult<()> { - self.inner - .add_snapshot_len_equal_to(&self.current_snapshot, array_length_mid)?; - self.inner - .add_snapshot_len_equal_to(&self.final_snapshot, array_length_mid)?; + unimplemented!(); + // self.inner + // .add_snapshot_len_equal_to(&self.current_snapshot, array_length_mid)?; + // self.inner + // .add_snapshot_len_equal_to(&self.final_snapshot, array_length_mid)?; Ok(()) } @@ -251,34 +292,52 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { let array_length_int = self.inner.array_length_int(array_length_mid)?; let element_place = self.inner.lowerer.encode_index_place( self.inner.ty, - self.place.clone().into(), + self.inner.place.clone().into(), index.clone().into(), self.inner.position, )?; - let current_element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( - self.current_snapshot.clone().into(), - index_int.clone(), - self.inner.position, - )?; - let final_element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( - self.final_snapshot.clone().into(), - index_int.clone(), + let element_address = self.inner.lowerer.encode_index_address( + self.inner.ty, + self.inner.address.clone().into(), + index.clone().into(), self.inner.position, )?; - let mut builder = UniqueRefUseBuilder::new( - self.inner.lowerer, + // let current_element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( + // self.current_snapshot.clone().into(), + // index_int.clone(), + // self.inner.position, + // )?; + // let final_element_snapshot = self.inner.lowerer.obtain_array_element_snapshot( + // self.final_snapshot.clone().into(), + // index_int.clone(), + // self.inner.position, + // )?; + + // let mut builder = UniqueRefUseBuilder::new( + // self.inner.lowerer, + // CallContext::BuiltinMethod, + // element_type, + // element_type, + // element_place, + // self.inner.address.clone().into(), + // current_element_snapshot, + // final_element_snapshot, + // self.reference_lifetime.clone().into(), + // )?; + // builder.add_lifetime_arguments()?; + // builder.add_const_arguments()?; + // let element_predicate_acc = builder.build(); + let element_predicate_acc = self.inner.lowerer.unique_ref( CallContext::BuiltinMethod, element_type, element_type, element_place, - self.root_address.clone().into(), - current_element_snapshot, - final_element_snapshot, + element_address, self.reference_lifetime.clone().into(), + None, // FIXME: This should be a proper value + None, + self.inner.position, )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let element_predicate_acc = builder.build(); let elements = vir_low::Expression::forall( vec![index], vec![vir_low::Trigger::new(vec![element_predicate_acc.clone()])], @@ -292,53 +351,65 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { pub(in super::super::super) fn create_variant_predicate( &mut self, - discriminant_value: vir_mid::DiscriminantValue, - variant: &vir_mid::type_decl::Struct, - variant_type: &vir_mid::Type, + _discriminant_value: vir_mid::DiscriminantValue, + _variant: &vir_mid::type_decl::Struct, + _variant_type: &vir_mid::Type, ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)> { - use vir_low::macros::*; - let discriminant_call = self.inner.lowerer.obtain_enum_discriminant( - self.current_snapshot.clone().into(), - self.inner.ty, - self.inner.position, - )?; - let guard = expr! { - [ discriminant_call ] == [ discriminant_value.into() ] - }; - let variant_index = variant.name.clone().into(); - let variant_place = self.inner.lowerer.encode_enum_variant_place( - self.inner.ty, - &variant_index, - self.place.clone().into(), - self.inner.position, - )?; - let current_variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( - self.inner.ty, - &variant_index, - self.current_snapshot.clone().into(), - self.inner.position, - )?; - let final_variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( - self.inner.ty, - &variant_index, - self.final_snapshot.clone().into(), - self.inner.position, - )?; - let mut builder = UniqueRefUseBuilder::new( - self.inner.lowerer, - CallContext::BuiltinMethod, - variant_type, - variant_type, - variant_place, - self.root_address.clone().into(), - current_variant_snapshot, - final_variant_snapshot, - self.reference_lifetime.clone().into(), - )?; - builder.add_lifetime_arguments()?; - builder.add_const_arguments()?; - let predicate = builder.build(); - Ok((guard, predicate)) + unimplemented!(); + // use vir_low::macros::*; + // let discriminant_call = self.inner.lowerer.obtain_enum_discriminant( + // self.current_snapshot.clone().into(), + // self.inner.ty, + // self.inner.position, + // )?; + // let guard = expr! { + // [ discriminant_call ] == [ discriminant_value.into() ] + // }; + // let variant_index = variant.name.clone().into(); + // let variant_place = self.inner.lowerer.encode_enum_variant_place( + // self.inner.ty, + // &variant_index, + // self.inner.place.clone().into(), + // self.inner.position, + // )?; + // let current_variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + // self.inner.ty, + // &variant_index, + // self.current_snapshot.clone().into(), + // self.inner.position, + // )?; + // let final_variant_snapshot = self.inner.lowerer.obtain_enum_variant_snapshot( + // self.inner.ty, + // &variant_index, + // self.final_snapshot.clone().into(), + // self.inner.position, + // )?; + // // let mut builder = UniqueRefUseBuilder::new( + // // self.inner.lowerer, + // // CallContext::BuiltinMethod, + // // variant_type, + // // variant_type, + // // variant_place, + // // self.inner.address.clone().into(), + // // current_variant_snapshot, + // // final_variant_snapshot, + // // self.reference_lifetime.clone().into(), + // // )?; + // // builder.add_lifetime_arguments()?; + // // builder.add_const_arguments()?; + // // let predicate = builder.build(); + // let predicate = self.inner.lowerer.unique_ref( + // CallContext::BuiltinMethod, + // variant_type, + // variant_type, + // variant_place, + // self.inner.address.clone().into(), + // current_variant_snapshot, + // final_variant_snapshot, + // self.reference_lifetime.clone().into(), + // None, // FIXME: This should be a proper value + // )?; + // Ok((guard, predicate)) } pub(in super::super::super) fn add_variant_predicates( @@ -348,4 +419,41 @@ impl<'l, 'p, 'v, 'tcx> UniqueRefBuilder<'l, 'p, 'v, 'tcx> { self.inner .add_conjunct(variant_predicates.into_iter().create_match()) } + + pub(in super::super::super) fn add_structural_invariant( + &mut self, + decl: &vir_mid::type_decl::Struct, + ) -> SpannedEncodingResult> { + self.inner.add_structural_invariant( + decl, + PredicateKind::UniqueRef { + lifetime: self.reference_lifetime.clone().into(), + is_final: false, + }, + ) + } + + // /// FIXME: Code duplication. + // pub(in super::super::super) fn add_structural_invariant( + // &mut self, + // decl: &vir_mid::type_decl::Struct, + // ) -> SpannedEncodingResult> { + // if let Some(invariant) = &decl.structural_invariant { + // let mut encoder = SelfFramingAssertionToSnapshot::for_predicate_body( + // self.inner.place.clone(), + // self.inner.address.clone(), + // PredicateKind::UniqueRef { + // lifetime: self.reference_lifetime.clone().into(), + // }, + // ); + // for assertion in invariant { + // let low_assertion = + // encoder.expression_to_snapshot(self.inner.lowerer, assertion, true)?; + // self.inner.add_conjunct(low_assertion)?; + // } + // Ok(encoder.into_created_predicate_types()) + // } else { + // Ok(Vec::new()) + // } + // } } diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/predicate_use.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/predicate_use.rs index 799f81227d1..07be1142361 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/predicate_use.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/unique_ref/predicate_use.rs @@ -3,9 +3,9 @@ use crate::encoder::{ middle::core_proof::{ builtin_methods::CallContext, lowerer::Lowerer, predicates::owned::builders::common::predicate_use::PredicateUseBuilder, - snapshots::SnapshotValuesInterface, type_layouts::TypeLayoutsInterface, }, }; + use vir_crate::{ low::{self as vir_low}, middle::{ @@ -19,7 +19,7 @@ where G: WithLifetimes + WithConstArguments, { inner: PredicateUseBuilder<'l, 'p, 'v, 'tcx, G>, - current_snapshot: vir_low::Expression, + target_slice_len: Option, } impl<'l, 'p, 'v, 'tcx, G> UniqueRefUseBuilder<'l, 'p, 'v, 'tcx, G> @@ -33,34 +33,34 @@ where ty: &'l vir_mid::Type, generics: &'l G, place: vir_low::Expression, - root_address: vir_low::Expression, - current_snapshot: vir_low::Expression, - final_snapshot: vir_low::Expression, + address: vir_low::Expression, lifetime: vir_low::Expression, + target_slice_len: Option, + position: vir_low::Position, ) -> SpannedEncodingResult { + let mut arguments = vec![place, address, lifetime]; + if let Some(len) = target_slice_len.clone() { + arguments.push(len); + } let inner = PredicateUseBuilder::new( lowerer, - "UniqueRef2", + "UniqueRef", context, ty, generics, - vec![ - place, - root_address, - current_snapshot.clone(), - final_snapshot, - lifetime, - ], - Default::default(), + arguments, + position, )?; Ok(Self { inner, - current_snapshot, + target_slice_len, }) } - pub(in super::super::super::super::super) fn build(self) -> vir_low::Expression { - self.inner.build() + pub(in super::super::super::super::super) fn build( + self, + ) -> SpannedEncodingResult { + Ok(self.inner.build()) } pub(in super::super::super::super::super) fn add_lifetime_arguments( @@ -73,18 +73,27 @@ where &mut self, ) -> SpannedEncodingResult<()> { if self.inner.ty.is_slice() { - let snapshot_length = self - .inner - .lowerer - .obtain_array_len_snapshot(self.current_snapshot.clone(), self.inner.position)?; - let size_type = self.inner.lowerer.size_type_mid()?; - let argument = self.inner.lowerer.construct_constant_snapshot( - &size_type, - snapshot_length, - self.inner.position, - )?; - self.inner.arguments.push(argument); + // FIXME + eprintln!("FIXME!!!"); + // let snapshot_length = self + // .inner + // .lowerer + // .obtain_array_len_snapshot(self.current_snapshot.clone(), self.inner.position)?; + // let size_type = self.inner.lowerer.size_type_mid()?; + // let argument = self.inner.lowerer.construct_constant_snapshot( + // &size_type, + // snapshot_length, + // self.inner.position, + // )?; + // self.inner.arguments.push(argument); } self.inner.add_const_arguments() } + + pub(in super::super::super::super::super) fn set_maybe_permission_amount( + &mut self, + permission_amount: Option, + ) -> SpannedEncodingResult<()> { + self.inner.set_maybe_permission_amount(permission_amount) + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/encoder.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/encoder.rs index 2d9e138997f..0aedee95e03 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/encoder.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/encoder.rs @@ -1,9 +1,17 @@ +use super::{ + builders::{ + FracRefSnapFunctionBuilder, OwnedAliasedBuilder, OwnedAliasedRangeSnapFunctionBuilder, + OwnedAliasedSnapFunctionBuilder, OwnedNonAliasedSnapFunctionBuilder, + UniqueRefCurrentSnapFunctionBuilder, UniqueRefFinalSnapFunctionBuilder, + }, + interface::{OwnedPredicateInfo, SnapshotFunctionInfo}, +}; use crate::encoder::{ errors::SpannedEncodingResult, high::types::HighTypeEncoderInterface, middle::core_proof::{ compute_address::ComputeAddressInterface, - lowerer::Lowerer, + lowerer::{FunctionsLowererInterface, Lowerer}, places::PlacesInterface, predicates::{ owned::builders::{ @@ -16,34 +24,41 @@ use crate::encoder::{ types::TypesInterface, }, }; +use prusti_common::config; use rustc_hash::FxHashSet; +use std::collections::BTreeMap; use vir_crate::{ - common::identifier::WithIdentifier, + common::{ + expression::{ExpressionIterator, GuardedExpressionIterator}, + identifier::WithIdentifier, + }, low::{self as vir_low}, middle as vir_mid, }; pub(super) struct PredicateEncoder<'l, 'p, 'v, 'tcx> { lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, - unfolded_owned_non_aliased_predicates: &'l FxHashSet, - encoded_owned_predicates: FxHashSet, + encoded_owned_non_aliased_predicates: FxHashSet, + encoded_owned_aliased_predicates: FxHashSet, encoded_mut_borrow_predicates: FxHashSet, encoded_frac_borrow_predicates: FxHashSet, + encoded_owned_range_snapshot_functions: FxHashSet, predicates: Vec, + /// A map from predicate names to snapshot function names and snapshot types. + predicate_info: BTreeMap, } impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { - pub(super) fn new( - lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, - unfolded_owned_non_aliased_predicates: &'l FxHashSet, - ) -> Self { + pub(super) fn new(lowerer: &'l mut Lowerer<'p, 'v, 'tcx>) -> Self { Self { lowerer, - unfolded_owned_non_aliased_predicates, - encoded_owned_predicates: Default::default(), + encoded_owned_non_aliased_predicates: Default::default(), + encoded_owned_aliased_predicates: Default::default(), encoded_mut_borrow_predicates: Default::default(), encoded_frac_borrow_predicates: Default::default(), + encoded_owned_range_snapshot_functions: Default::default(), predicates: Default::default(), + predicate_info: Default::default(), } } @@ -51,23 +66,382 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { self.predicates } + pub(super) fn take_predicate_info(&mut self) -> BTreeMap { + std::mem::take(&mut self.predicate_info) + } + + pub(super) fn encode_owned_non_aliased_snapshot( + &mut self, + normalized_type: &vir_mid::Type, + type_decl: &vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let mut builder = + OwnedNonAliasedSnapFunctionBuilder::new(self.lowerer, normalized_type, type_decl)?; + builder.create_parameters()?; + builder.add_owned_precondition()?; + builder.add_validity_postcondition()?; + match type_decl { + vir_mid::TypeDecl::Bool + | vir_mid::TypeDecl::Int(_) + | vir_mid::TypeDecl::Float(_) + | vir_mid::TypeDecl::Pointer(_) + | vir_mid::TypeDecl::Sequence(_) + | vir_mid::TypeDecl::Map(_) => { + builder.add_bytes_snapshot_equality()?; + } + vir_mid::TypeDecl::Trusted(_) | vir_mid::TypeDecl::TypeVar(_) => {} + vir_mid::TypeDecl::Struct(decl) => { + let mut equalities = Vec::new(); + for field in &decl.fields { + equalities.push(builder.create_field_snapshot_equality(field)?); + } + builder.add_snapshot_body_postcondition(equalities.into_iter().conjoin())?; + builder.add_structural_invariant(decl)?; + } + vir_mid::TypeDecl::Enum(decl) => { + let mut equalities = Vec::new(); + if decl.safety.is_enum() { + let discriminant_equality = + builder.create_discriminant_snapshot_equality(decl)?; + builder.add_snapshot_body_postcondition(discriminant_equality)?; + } + for (discriminant, variant) in decl.iter_discriminant_variants() { + equalities + .push(builder.create_variant_snapshot_equality(discriminant, variant)?); + } + builder.add_snapshot_body_postcondition(equalities.into_iter().create_match())?; + } + vir_mid::TypeDecl::Reference(decl) => { + builder.add_bytes_address_snapshot_equality()?; + // FIXME: Have a getter for the first lifetime. + let lifetime = &decl.lifetimes[0]; + builder.add_reference_snapshot_equalities(decl, lifetime)?; + } + vir_mid::TypeDecl::Array(decl) => { + let length = if normalized_type.is_slice() { + builder.get_slice_len()? + } else { + decl.const_parameters[0].clone() + }; + builder.add_snapshot_len_equal_to_postcondition(&length)?; + builder.add_quantifiers(&length, &decl.element_type)?; + } + _ => { + unimplemented!("{}", type_decl); + } + } + let owned_snapshots_to_encode = builder.take_owned_snapshot_functions_to_encode(); + let owned_range_snapshots_to_encode = + builder.take_owned_range_snapshot_functions_to_encode(); + let snapshot_postconditions = builder.get_snapshot_postconditions()?; + let snapshot_body = builder.get_snapshot_body()?; + let function = builder.build()?; + let function_name = function.name.clone(); + let snapshot_type = function.return_type.clone(); + self.lowerer.declare_function(function)?; + for ty in owned_snapshots_to_encode { + self.encode_owned_non_aliased(&ty)?; + } + for ty in owned_range_snapshots_to_encode { + self.encode_owned_range_snapshot(&ty)?; + } + Ok(OwnedPredicateInfo { + current_snapshot_function: SnapshotFunctionInfo { + function_name, + postconditions: snapshot_postconditions, + body: snapshot_body, + }, + final_snapshot_function: None, + snapshot_type, + }) + } + + pub(super) fn encode_owned_range_snapshot( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()> { + let ty_identifier = ty.get_identifier(); + if self + .encoded_owned_range_snapshot_functions + .contains(&ty_identifier) + { + return Ok(()); + } + let type_decl = self.lowerer.encoder.get_type_decl_mid(ty)?; + let normalized_type = ty.normalize_type(); + let mut builder = + OwnedAliasedRangeSnapFunctionBuilder::new(self.lowerer, &normalized_type, &type_decl)?; + builder.create_parameters()?; + builder.add_owned_precondition()?; + builder.add_postcondition()?; + let function = builder.build()?; + self.lowerer.declare_function(function)?; + Ok(()) + } + + pub(super) fn encode_owned_aliased_snapshot( + &mut self, + normalized_type: &vir_mid::Type, + type_decl: &vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let mut builder = + OwnedAliasedSnapFunctionBuilder::new(self.lowerer, normalized_type, type_decl)?; + builder.create_parameters()?; + builder.add_owned_precondition()?; + builder.add_validity_postcondition()?; + match type_decl { + vir_mid::TypeDecl::Bool + | vir_mid::TypeDecl::Int(_) + | vir_mid::TypeDecl::Float(_) + | vir_mid::TypeDecl::Pointer(_) + | vir_mid::TypeDecl::Sequence(_) + | vir_mid::TypeDecl::Map(_) => { + builder.add_bytes_snapshot_equality()?; + } + vir_mid::TypeDecl::Trusted(_) | vir_mid::TypeDecl::TypeVar(_) => {} + vir_mid::TypeDecl::Struct(decl) => { + let mut equalities = Vec::new(); + for field in &decl.fields { + equalities.push(builder.create_field_snapshot_equality(field)?); + } + builder.add_snapshot_body_postcondition(equalities.into_iter().conjoin())?; + builder.add_structural_invariant(decl)?; + } + vir_mid::TypeDecl::Enum(decl) => { + let mut equalities = Vec::new(); + if decl.safety.is_enum() { + let discriminant_equality = + builder.create_discriminant_snapshot_equality(decl)?; + builder.add_snapshot_body_postcondition(discriminant_equality)?; + } + for (discriminant, variant) in decl.iter_discriminant_variants() { + equalities + .push(builder.create_variant_snapshot_equality(discriminant, variant)?); + } + builder.add_snapshot_body_postcondition(equalities.into_iter().create_match())?; + } + vir_mid::TypeDecl::Reference(decl) => { + builder.add_bytes_address_snapshot_equality()?; + // FIXME: Have a getter for the first lifetime. + let lifetime = &decl.lifetimes[0]; + builder.add_reference_snapshot_equalities(decl, lifetime)?; + } + vir_mid::TypeDecl::Array(decl) => { + let length = if normalized_type.is_slice() { + builder.get_slice_len()? + } else { + decl.const_parameters[0].clone() + }; + builder.add_snapshot_len_equal_to_postcondition(&length)?; + builder.add_quantifiers(&length, &decl.element_type)?; + } + _ => { + unimplemented!("{}", type_decl); + } + } + let owned_snapshots_to_encode = builder.take_owned_snapshot_functions_to_encode(); + let owned_range_snapshots_to_encode = + builder.take_owned_range_snapshot_functions_to_encode(); + let snapshot_postconditions = builder.get_snapshot_postconditions()?; + let snapshot_body = builder.get_snapshot_body()?; + let function = builder.build()?; + let function_name = function.name.clone(); + let snapshot_type = function.return_type.clone(); + self.lowerer.declare_function(function)?; + for ty in owned_snapshots_to_encode { + self.encode_owned_non_aliased(&ty)?; + } + for ty in owned_range_snapshots_to_encode { + self.encode_owned_range_snapshot(&ty)?; + } + Ok(OwnedPredicateInfo { + current_snapshot_function: SnapshotFunctionInfo { + function_name, + postconditions: snapshot_postconditions, + body: snapshot_body, + }, + final_snapshot_function: None, + snapshot_type, + }) + } + + pub(super) fn encode_unique_ref_current_snapshot( + &mut self, + normalized_type: &vir_mid::Type, + type_decl: &vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let mut builder = + UniqueRefCurrentSnapFunctionBuilder::new(self.lowerer, normalized_type, type_decl)?; + builder.create_parameters()?; + builder.add_unique_ref_precondition()?; + match &type_decl { + vir_mid::TypeDecl::Bool + | vir_mid::TypeDecl::Int(_) + | vir_mid::TypeDecl::Float(_) + | vir_mid::TypeDecl::Pointer(_) + | vir_mid::TypeDecl::Sequence(_) + | vir_mid::TypeDecl::Map(_) => { + // For these types the unique ref predicate is abstract. + } + vir_mid::TypeDecl::Trusted(_) | vir_mid::TypeDecl::TypeVar(_) => {} + vir_mid::TypeDecl::Struct(decl) => { + let mut equalities = Vec::new(); + for field in &decl.fields { + equalities.push(builder.create_field_snapshot_equality(field)?); + } + builder.add_snapshot_body_postcondition(equalities.into_iter().conjoin())?; + builder.add_structural_invariant(decl)?; + } + vir_mid::TypeDecl::Enum(_decl) => {} + vir_mid::TypeDecl::Reference(_decl) => {} + vir_mid::TypeDecl::Array(_decl) => {} + _ => { + unimplemented!("{}", type_decl); + } + } + let snapshot_postconditions = builder.get_snapshot_postconditions()?; + let snapshot_body = builder.get_snapshot_body()?; + let function = builder.build()?; + let function_name = function.name.clone(); + let snapshot_type = function.return_type.clone(); + self.lowerer.declare_function(function)?; + Ok(OwnedPredicateInfo { + current_snapshot_function: SnapshotFunctionInfo { + function_name, + postconditions: snapshot_postconditions, + body: snapshot_body, + }, + final_snapshot_function: None, + snapshot_type, + }) + } + + pub(super) fn encode_unique_ref_final_snapshot( + &mut self, + normalized_type: &vir_mid::Type, + type_decl: &vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let mut builder = + UniqueRefFinalSnapFunctionBuilder::new(self.lowerer, normalized_type, type_decl)?; + builder.create_parameters()?; + match &type_decl { + vir_mid::TypeDecl::Bool + | vir_mid::TypeDecl::Int(_) + | vir_mid::TypeDecl::Float(_) + | vir_mid::TypeDecl::Pointer(_) + | vir_mid::TypeDecl::Sequence(_) + | vir_mid::TypeDecl::Map(_) => { + // For these types the unique ref predicate is abstract. + } + vir_mid::TypeDecl::Trusted(_) | vir_mid::TypeDecl::TypeVar(_) => {} + vir_mid::TypeDecl::Struct(decl) => { + let mut equalities = Vec::new(); + for field in &decl.fields { + equalities.push(builder.create_field_snapshot_equality(field)?); + } + builder.add_snapshot_postcondition(equalities.into_iter().conjoin())?; + builder.add_structural_invariant(decl)?; + } + vir_mid::TypeDecl::Enum(_decl) => {} + vir_mid::TypeDecl::Reference(_decl) => { + // For references, the final snapshot is abstract. + } + vir_mid::TypeDecl::Array(_decl) => {} + _ => { + unimplemented!("{}", type_decl); + } + } + let snapshot_postconditions = builder.get_snapshot_postconditions()?; + let snapshot_body = builder.get_snapshot_body()?; + let (function_name, snapshot_type) = builder.build()?; + Ok(OwnedPredicateInfo { + current_snapshot_function: SnapshotFunctionInfo { + function_name, + postconditions: snapshot_postconditions, + body: snapshot_body, + }, + final_snapshot_function: None, + snapshot_type, + }) + } + + pub(super) fn encode_frac_ref_snapshot( + &mut self, + normalized_type: &vir_mid::Type, + type_decl: &vir_mid::TypeDecl, + ) -> SpannedEncodingResult { + let mut builder = + FracRefSnapFunctionBuilder::new(self.lowerer, normalized_type, type_decl)?; + builder.create_parameters()?; + builder.add_frac_ref_precondition()?; + match &type_decl { + vir_mid::TypeDecl::Bool + | vir_mid::TypeDecl::Int(_) + | vir_mid::TypeDecl::Float(_) + | vir_mid::TypeDecl::Pointer(_) + | vir_mid::TypeDecl::Sequence(_) + | vir_mid::TypeDecl::Map(_) => { + // For these types the unique ref predicate is abstract. + } + vir_mid::TypeDecl::Trusted(_) | vir_mid::TypeDecl::TypeVar(_) => {} + vir_mid::TypeDecl::Struct(decl) => { + let mut equalities = Vec::new(); + for field in &decl.fields { + equalities.push(builder.create_field_snapshot_equality(field)?); + } + builder.add_snapshot_body_postcondition(equalities.into_iter().conjoin())?; + builder.add_structural_invariant(decl)?; + } + vir_mid::TypeDecl::Enum(_decl) => {} + vir_mid::TypeDecl::Reference(_decl) => {} + vir_mid::TypeDecl::Array(_decl) => {} + _ => { + unimplemented!("{}", type_decl); + } + } + let snapshot_postconditions = builder.get_snapshot_postconditions()?; + let snapshot_body = builder.get_snapshot_body()?; + let function = builder.build()?; + let function_name = function.name.clone(); + let snapshot_type = function.return_type.clone(); + self.lowerer.declare_function(function)?; + Ok(OwnedPredicateInfo { + current_snapshot_function: SnapshotFunctionInfo { + function_name, + postconditions: snapshot_postconditions, + body: snapshot_body, + }, + final_snapshot_function: None, + snapshot_type, + }) + } + pub(super) fn encode_owned_non_aliased( &mut self, ty: &vir_mid::Type, ) -> SpannedEncodingResult<()> { let ty_identifier = ty.get_identifier(); - if self.encoded_owned_predicates.contains(&ty_identifier) { + if self + .encoded_owned_non_aliased_predicates + .contains(&ty_identifier) + { return Ok(()); } - self.encoded_owned_predicates.insert(ty_identifier); + self.encoded_owned_non_aliased_predicates + .insert(ty_identifier); self.lowerer.encode_compute_address(ty)?; let type_decl = self.lowerer.encoder.get_type_decl_mid(ty)?; let normalized_type = ty.normalize_type(); self.lowerer .encode_snapshot_to_bytes_function(&normalized_type)?; + // if !config::use_snapshot_parameters_in_predicates() { + let predicate_info = + self.encode_owned_non_aliased_snapshot(&normalized_type, &type_decl)?; + // } let mut owned_predicates_to_encode = Vec::new(); + // let mut owned_aliased_predicates_to_encode = Vec::new(); let mut unique_ref_predicates_to_encode = Vec::new(); let mut frac_ref_predicates_to_encode = Vec::new(); self.lowerer.encode_memory_block_predicate()?; @@ -75,7 +449,9 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { builder.create_parameters()?; if !(type_decl.is_type_var() || type_decl.is_trusted()) { builder.create_body(); - builder.add_validity()?; + if config::use_snapshot_parameters_in_predicates() { + builder.add_validity()?; + } } // Build the body. match &type_decl { @@ -86,20 +462,26 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { | vir_mid::TypeDecl::Sequence(_) | vir_mid::TypeDecl::Map(_) => { builder.add_base_memory_block()?; - builder.add_bytes_snapshot_equality()?; + // if config::use_snapshot_parameters_in_predicates() { + // builder.add_bytes_snapshot_equality()?; + // } + if let vir_mid::TypeDecl::Pointer(decl) = &type_decl { + owned_predicates_to_encode.push(decl.target_type.clone()); + } } vir_mid::TypeDecl::Trusted(_) | vir_mid::TypeDecl::TypeVar(_) => {} vir_mid::TypeDecl::Struct(decl) => { builder.add_padding_memory_block()?; for field in &decl.fields { builder.add_field_predicate(field)?; - if !self - .unfolded_owned_non_aliased_predicates - .contains(&field.ty) - { - owned_predicates_to_encode.push(field.ty.clone()); - } + // if !self + // .unfolded_owned_non_aliaseds + // .contains(&field.ty) + // { + owned_predicates_to_encode.push(field.ty.clone()); + // } } + owned_predicates_to_encode.extend(builder.add_structural_invariant(decl)?); } vir_mid::TypeDecl::Enum(decl) => { builder.add_padding_memory_block()?; @@ -114,24 +496,25 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { decl.lifetimes.clone(), ); variant_predicates.push(builder.create_variant_predicate( + decl, discriminant, variant, &variant_type, )?); let variant_type = ty.clone().variant(variant_index); - if !self - .unfolded_owned_non_aliased_predicates - .contains(&variant_type) - { - owned_predicates_to_encode.push(variant_type); - } - } - if !self - .unfolded_owned_non_aliased_predicates - .contains(&decl.discriminant_type) - { - owned_predicates_to_encode.push(decl.discriminant_type.clone()); + // if !self + // .unfolded_owned_non_aliaseds + // .contains(&variant_type) + // { + owned_predicates_to_encode.push(variant_type); + // } } + // if !self + // .unfolded_owned_non_aliaseds + // .contains(&decl.discriminant_type) + // { + owned_predicates_to_encode.push(decl.discriminant_type.clone()); + // } if decl.safety.is_enum() { builder.add_discriminant_predicate(decl)?; } @@ -139,7 +522,9 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { } vir_mid::TypeDecl::Reference(decl) => { builder.add_base_memory_block()?; - builder.add_bytes_address_snapshot_equality()?; + // if config::use_snapshot_parameters_in_predicates() { + // builder.add_bytes_address_snapshot_equality()?; + // } // FIXME: Have a getter for the first lifetime. let lifetime = &decl.lifetimes[0]; if decl.uniqueness.is_unique() { @@ -163,7 +548,9 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { } else { decl.const_parameters[0].clone() }; - builder.add_snapshot_len_equal_to(&length)?; + if config::use_snapshot_parameters_in_predicates() { + builder.add_snapshot_len_equal_to(&length)?; + } builder.add_quantified_permission(&length, &decl.element_type)?; } _ => { @@ -172,12 +559,148 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { unimplemented!("{}", type_decl); } } - self.predicates.push(builder.build()); + let predicate = builder.build(); + self.predicate_info + .insert(predicate.name.clone(), predicate_info); + self.predicates.push(predicate); for ty in owned_predicates_to_encode { // TODO: Optimization: This variant is never unfolded, // encode it as abstract predicate. self.encode_owned_non_aliased(&ty)?; } + // for ty in owned_aliased_predicates_to_encode { + // self.encode_owned_aliased(&ty)?; + // } + for ty in unique_ref_predicates_to_encode { + // TODO: Optimization: This variant is never unfolded, + // encode it as abstract predicate. + self.encode_unique_ref(&ty)?; + } + for ty in frac_ref_predicates_to_encode { + // TODO: Optimization: This variant is never unfolded, + // encode it as abstract predicate. + self.encode_frac_ref(&ty)?; + } + Ok(()) + } + + pub(super) fn encode_owned_aliased(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()> { + let ty_identifier = ty.get_identifier(); + if self + .encoded_owned_aliased_predicates + .contains(&ty_identifier) + { + return Ok(()); + } + + self.encoded_owned_aliased_predicates.insert(ty_identifier); + self.lowerer.encode_compute_address(ty)?; + let type_decl = self.lowerer.encoder.get_type_decl_mid(ty)?; + + let normalized_type = ty.normalize_type(); + self.lowerer + .encode_snapshot_to_bytes_function(&normalized_type)?; + let predicate_info = self.encode_owned_aliased_snapshot(&normalized_type, &type_decl)?; + let mut owned_predicates_to_encode = Vec::new(); + let mut unique_ref_predicates_to_encode = Vec::new(); + let mut frac_ref_predicates_to_encode = Vec::new(); + self.lowerer.encode_memory_block_predicate()?; + let mut builder = OwnedAliasedBuilder::new(self.lowerer, &normalized_type, &type_decl)?; + builder.create_parameters()?; + if !(type_decl.is_type_var() || type_decl.is_trusted()) { + builder.create_body(); + } + // Build the body. + match &type_decl { + vir_mid::TypeDecl::Bool + | vir_mid::TypeDecl::Int(_) + | vir_mid::TypeDecl::Float(_) + | vir_mid::TypeDecl::Pointer(_) + | vir_mid::TypeDecl::Sequence(_) + | vir_mid::TypeDecl::Map(_) => { + builder.add_base_memory_block()?; + if let vir_mid::TypeDecl::Pointer(decl) = &type_decl { + owned_predicates_to_encode.push(decl.target_type.clone()); + } + } + vir_mid::TypeDecl::Trusted(_) | vir_mid::TypeDecl::TypeVar(_) => {} + vir_mid::TypeDecl::Struct(decl) => { + builder.add_padding_memory_block()?; + for field in &decl.fields { + builder.add_field_predicate(field)?; + owned_predicates_to_encode.push(field.ty.clone()); + } + owned_predicates_to_encode.extend(builder.add_structural_invariant(decl)?); + } + vir_mid::TypeDecl::Enum(decl) => { + builder.add_padding_memory_block()?; + let mut variant_predicates = Vec::new(); + for (discriminant, variant) in decl.iter_discriminant_variants() { + let variant_index: vir_mid::ty::VariantIndex = variant.name.clone().into(); + let variant_type = vir_mid::Type::enum_( + decl.name.clone(), + decl.safety, + decl.arguments.clone(), + Some(variant_index.clone()), + decl.lifetimes.clone(), + ); + variant_predicates.push(builder.create_variant_predicate( + decl, + discriminant, + variant, + &variant_type, + )?); + let variant_type = ty.clone().variant(variant_index); + owned_predicates_to_encode.push(variant_type); + } + owned_predicates_to_encode.push(decl.discriminant_type.clone()); + if decl.safety.is_enum() { + builder.add_discriminant_predicate(decl)?; + } + builder.add_variant_predicates(variant_predicates)?; + } + vir_mid::TypeDecl::Reference(decl) => { + builder.add_base_memory_block()?; + // FIXME: Have a getter for the first lifetime. + let lifetime = &decl.lifetimes[0]; + if decl.uniqueness.is_unique() { + builder.add_unique_ref_target_predicate(&decl.target_type, lifetime)?; + unique_ref_predicates_to_encode.push(decl.target_type.clone()); + } else { + builder.add_frac_ref_target_predicate(&decl.target_type, lifetime)?; + frac_ref_predicates_to_encode.push(decl.target_type.clone()); + } + } + vir_mid::TypeDecl::Array(decl) => { + builder.lowerer().encode_place_array_index_axioms(ty)?; + builder + .lowerer() + .ensure_type_definition(&decl.element_type)?; + owned_predicates_to_encode.push(decl.element_type.clone()); + builder.add_const_parameters_validity()?; + // FIXME: Have a getter for the first const parameter. + let _length = if normalized_type.is_slice() { + builder.get_slice_len()? + } else { + decl.const_parameters[0].clone() + }; + unimplemented!(); + // builder.add_quantified_permission(&length, &decl.element_type)?; + } + _ => { + builder.add_base_memory_block()?; + unimplemented!("{}", type_decl); + } + } + let predicate = builder.build(); + self.predicate_info + .insert(predicate.name.clone(), predicate_info); + self.predicates.push(predicate); + for ty in owned_predicates_to_encode { + // TODO: Optimization: This variant is never unfolded, + // encode it as abstract predicate. + self.encode_owned_aliased(&ty)?; + } for ty in unique_ref_predicates_to_encode { // TODO: Optimization: This variant is never unfolded, // encode it as abstract predicate. @@ -198,10 +721,10 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { } self.encoded_frac_borrow_predicates.insert(ty_identifier); self.lowerer.encode_compute_address(ty)?; - let type_decl = self.lowerer.encoder.get_type_decl_mid(ty)?; let normalized_type = ty.normalize_type(); + let predicate_info = self.encode_frac_ref_snapshot(&normalized_type, &type_decl)?; let mut predicates_to_encode = Vec::new(); let mut builder = FracRefBuilder::new(self.lowerer, &normalized_type, &type_decl)?; builder.create_parameters()?; @@ -217,7 +740,7 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { | vir_mid::TypeDecl::TypeVar(_) ) { builder.create_body(); - builder.add_validity()?; + // builder.add_validity()?; } // Build the body. match &type_decl { @@ -234,6 +757,7 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { builder.add_field_predicate(field)?; predicates_to_encode.push(field.ty.clone()); } + predicates_to_encode.extend(builder.add_structural_invariant(decl)?); } vir_mid::TypeDecl::Enum(decl) => { let mut variant_predicates = Vec::new(); @@ -279,7 +803,10 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { unimplemented!("{:?}", type_decl); } } - self.predicates.push(builder.build()); + let predicate = builder.build(); + self.predicate_info + .insert(predicate.name.clone(), predicate_info); + self.predicates.push(predicate); for ty in predicates_to_encode { // TODO: Optimization: This variant is never unfolded, // encode it as abstract predicate. @@ -300,6 +827,12 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { // FIXME: Make get_type_decl_mid to return the erased ty for which it // returned type_decl. let normalized_type = ty.normalize_type(); + // if !config::use_snapshot_parameters_in_predicates() { + let current_predicate_info = + self.encode_unique_ref_current_snapshot(&normalized_type, &type_decl)?; + let final_predicate_info = + self.encode_unique_ref_final_snapshot(&normalized_type, &type_decl)?; + // } let mut unique_ref_predicates_to_encode = Vec::new(); let mut frac_ref_predicates_to_encode = Vec::new(); let mut builder = UniqueRefBuilder::new(self.lowerer, &normalized_type, &type_decl)?; @@ -316,7 +849,9 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { | vir_mid::TypeDecl::TypeVar(_) ) { builder.create_body(); - builder.add_validity()?; + // if config::use_snapshot_parameters_in_predicates() { + // builder.add_validity()?; + // } } // Build the body. match &type_decl { @@ -333,6 +868,7 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { builder.add_field_predicate(field)?; unique_ref_predicates_to_encode.push(field.ty.clone()); } + unique_ref_predicates_to_encode.extend(builder.add_structural_invariant(decl)?); } vir_mid::TypeDecl::Enum(decl) => { let mut variant_predicates = Vec::new(); @@ -355,12 +891,15 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { vir_mid::TypeDecl::Reference(decl) => { // FIXME: Have a getter for the first lifetime. let lifetime = &decl.lifetimes[0]; + let pointer_type = builder.add_unique_ref_pointer_predicate(lifetime)?; if decl.uniqueness.is_unique() { builder.add_unique_ref_target_predicate(&decl.target_type, lifetime)?; unique_ref_predicates_to_encode.push(decl.target_type.clone()); + unique_ref_predicates_to_encode.push(pointer_type); } else { builder.add_frac_ref_target_predicate(&decl.target_type, lifetime)?; frac_ref_predicates_to_encode.push(decl.target_type.clone()); + frac_ref_predicates_to_encode.push(pointer_type); } } vir_mid::TypeDecl::Array(decl) => { @@ -376,14 +915,26 @@ impl<'l, 'p, 'v, 'tcx> PredicateEncoder<'l, 'p, 'v, 'tcx> { } else { decl.const_parameters[0].clone() }; - builder.add_snapshot_len_equal_to(&length)?; + if config::use_snapshot_parameters_in_predicates() { + builder.add_snapshot_len_equal_to(&length)?; + } builder.add_quantified_permission(&length, &decl.element_type)?; } _ => { unimplemented!("{:?}", type_decl); } } - self.predicates.push(builder.build()); + let predicate = builder.build(); + assert_eq!( + current_predicate_info.snapshot_type, + final_predicate_info.snapshot_type + ); + let mut predicate_info = current_predicate_info; + predicate_info.final_snapshot_function = + Some(final_predicate_info.current_snapshot_function); + self.predicate_info + .insert(predicate.name.clone(), predicate_info); + self.predicates.push(predicate); for ty in unique_ref_predicates_to_encode { // TODO: Optimization: This variant is never unfolded, // encode it as abstract predicate. diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/interface.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/interface.rs index 16dc6b0981b..7be3365753a 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/interface.rs @@ -1,13 +1,24 @@ use super::{ - encoder::PredicateEncoder, FracRefUseBuilder, OwnedNonAliasedUseBuilder, UniqueRefUseBuilder, + builders::{ + FracRefSnapCallBuilder, OwnedAliasedRangeSnapCallBuilder, OwnedAliasedRangeUseBuilder, + OwnedNonAliasedSnapCallBuilder, UniqueRefCurrentSnapCallBuilder, + UniqueRefFinalSnapCallBuilder, + }, + encoder::PredicateEncoder, + FracRefUseBuilder, OwnedNonAliasedUseBuilder, UniqueRefUseBuilder, }; use crate::encoder::{ errors::SpannedEncodingResult, - middle::core_proof::{builtin_methods::CallContext, lowerer::Lowerer, types::TypesInterface}, + middle::core_proof::{ + builtin_methods::CallContext, lowerer::Lowerer, places::PlacesInterface, + types::TypesInterface, + }, }; use rustc_hash::FxHashSet; +use std::collections::BTreeMap; use vir_crate::{ - low::{self as vir_low}, + common::expression::BinaryOperationHelpers, + low::{self as vir_low, operations::ty::Typed}, middle::{ self as vir_mid, operations::{const_generics::WithConstArguments, lifetimes::WithLifetimes}, @@ -16,31 +27,88 @@ use vir_crate::{ #[derive(Default)] pub(in super::super) struct PredicatesOwnedState { - unfolded_owned_non_aliased_predicates: FxHashSet, + unfolded_owned_predicates: FxHashSet, used_unique_ref_predicates: FxHashSet, + used_frac_ref_predicates: FxHashSet, + used_owned_range_snapshot_functions: FxHashSet, +} + +/// Addidional information about the predicate used by purification +/// optimizations. +#[derive(Clone, Debug)] +pub(in super::super::super) struct OwnedPredicateInfo { + /// Snapshot function of the current state. + pub(in super::super::super) current_snapshot_function: SnapshotFunctionInfo, + /// Snapshot function of the final state. + pub(in super::super::super) final_snapshot_function: Option, + /// The snapshot type. + pub(in super::super::super) snapshot_type: vir_low::Type, +} + +/// Addidional information about the snapshot function used by purification +/// optimizations. +#[derive(Clone, Debug)] +pub(in super::super::super) struct SnapshotFunctionInfo { + /// The name of the snapshot function. + pub(in super::super::super) function_name: String, + /// The properties that we know to hold when we have a predicate instance. + pub(in super::super::super) postconditions: Vec, + /// The assertions that link the snapshot of the predicate with the + /// snapshots of inner predicates. + pub(in super::super::super) body: Vec, } pub(in super::super::super) trait PredicatesOwnedInterface { - /// Marks that `OwnedNonAliased` was unfolded in the program and we need - /// to provide its body. - fn mark_owned_non_aliased_as_unfolded( - &mut self, - ty: &vir_mid::Type, - ) -> SpannedEncodingResult<()>; + /// Marks that `Owned` was unfolded in the program and we need to + /// provide its body. + fn mark_owned_predicate_as_unfolded(&mut self, ty: &vir_mid::Type) + -> SpannedEncodingResult<()>; /// Marks that `UniqueRef` was used in the program. fn mark_unique_ref_as_used(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()>; + fn mark_frac_ref_as_used(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()>; fn collect_owned_predicate_decls( &mut self, - ) -> SpannedEncodingResult>; + ) -> SpannedEncodingResult<( + Vec, + BTreeMap, + )>; + /// Owned predicate that can be either aliased or non-aliased depending on + /// the value of `place`. + fn owned_predicate( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; /// A version of `owned_non_aliased` for the most common case. + #[allow(clippy::too_many_arguments)] fn owned_non_aliased_full_vars( &mut self, context: CallContext, ty: &vir_mid::Type, generics: &G, place: &vir_low::VariableDecl, - root_address: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn owned_non_aliased_full_vars_with_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, snapshot: &vir_low::VariableDecl, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments; @@ -51,9 +119,95 @@ pub(in super::super::super) trait PredicatesOwnedInterface { ty: &vir_mid::Type, generics: &G, place: vir_low::Expression, - root_address: vir_low::Expression, + address: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn owned_non_aliased_with_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, snapshot: vir_low::Expression, permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn owned_aliased( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + address: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + fn owned_aliased_range( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + address: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn owned_predicate_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn owned_non_aliased_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn owned_aliased_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + fn owned_aliased_range_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + address: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments; @@ -64,10 +218,25 @@ pub(in super::super::super) trait PredicatesOwnedInterface { ty: &vir_mid::Type, generics: &G, place: &vir_low::VariableDecl, - root_address: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, + lifetime: &vir_low::VariableDecl, + target_slice_len: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn unique_ref_full_vars_with_current_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, current_snapshot: &vir_low::VariableDecl, - final_snapshot: &vir_low::VariableDecl, lifetime: &vir_low::VariableDecl, + target_slice_len: Option, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments; @@ -78,10 +247,41 @@ pub(in super::super::super) trait PredicatesOwnedInterface { ty: &vir_mid::Type, generics: &G, place: vir_low::Expression, - root_address: vir_low::Expression, + address: vir_low::Expression, + lifetime: vir_low::Expression, + target_slice_len: Option, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + fn unique_ref_with_current_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, current_snapshot: vir_low::Expression, - final_snapshot: vir_low::Expression, lifetime: vir_low::Expression, + target_slice_len: Option, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn unique_ref_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + lifetime: vir_low::Expression, + target_slice_len: Option, + is_final: bool, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments; @@ -92,9 +292,25 @@ pub(in super::super::super) trait PredicatesOwnedInterface { ty: &vir_mid::Type, generics: &G, place: &vir_low::VariableDecl, - root_address: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, + lifetime: &vir_low::VariableDecl, + target_slice_len: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn frac_ref_full_vars_with_current_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, current_snapshot: &vir_low::VariableDecl, lifetime: &vir_low::VariableDecl, + target_slice_len: Option, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments; @@ -105,29 +321,61 @@ pub(in super::super::super) trait PredicatesOwnedInterface { ty: &vir_mid::Type, generics: &G, place: vir_low::Expression, - root_address: vir_low::Expression, + address: vir_low::Expression, + lifetime: vir_low::Expression, + target_slice_len: Option, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn frac_ref_with_current_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, current_snapshot: vir_low::Expression, lifetime: vir_low::Expression, + target_slice_len: Option, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments; + #[allow(clippy::too_many_arguments)] + fn frac_ref_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + lifetime: vir_low::Expression, + target_slice_len: Option, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments; } impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { - fn mark_owned_non_aliased_as_unfolded( + fn mark_owned_predicate_as_unfolded( &mut self, ty: &vir_mid::Type, ) -> SpannedEncodingResult<()> { if !self .predicates_encoding_state .owned - .unfolded_owned_non_aliased_predicates + .unfolded_owned_predicates .contains(ty) { self.ensure_type_definition(ty)?; self.predicates_encoding_state .owned - .unfolded_owned_non_aliased_predicates + .unfolded_owned_predicates .insert(ty.clone()); } Ok(()) @@ -148,14 +396,38 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { Ok(()) } + fn mark_frac_ref_as_used(&mut self, ty: &vir_mid::Type) -> SpannedEncodingResult<()> { + if !self + .predicates_encoding_state + .owned + .used_frac_ref_predicates + .contains(ty) + { + self.predicates_encoding_state + .owned + .used_frac_ref_predicates + .insert(ty.clone()); + } + Ok(()) + } + fn collect_owned_predicate_decls( &mut self, - ) -> SpannedEncodingResult> { - let unfolded_predicates = std::mem::take( + ) -> SpannedEncodingResult<( + Vec, + BTreeMap, + )> { + // let unfolded_owned_predicates = std::mem::take( + // &mut self + // .predicates_encoding_state + // .owned + // .unfolded_owned_predicates, + // ); + let unfolded_owned_predicates = std::mem::take( &mut self .predicates_encoding_state .owned - .unfolded_owned_non_aliased_predicates, + .unfolded_owned_predicates, ); let used_unique_ref_predicates = std::mem::take( &mut self @@ -163,14 +435,50 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { .owned .used_unique_ref_predicates, ); - let mut predicate_encoder = PredicateEncoder::new(self, &unfolded_predicates); - for ty in &unfolded_predicates { + let used_owned_range_snapshot_functions = std::mem::take( + &mut self + .predicates_encoding_state + .owned + .used_owned_range_snapshot_functions, + ); + let mut predicate_encoder = PredicateEncoder::new(self); + for ty in &unfolded_owned_predicates { predicate_encoder.encode_owned_non_aliased(ty)?; } + // for ty in &unfolded_owned_predicates { + // unimplemented!(); + // // predicate_encoder.encode_owned_aliased(ty)?; + // } for ty in &used_unique_ref_predicates { predicate_encoder.encode_unique_ref(ty)?; } - Ok(predicate_encoder.into_predicates()) + for ty in &used_owned_range_snapshot_functions { + predicate_encoder.encode_owned_range_snapshot(ty)?; + } + let predicate_info = predicate_encoder.take_predicate_info(); + Ok((predicate_encoder.into_predicates(), predicate_info)) + } + + fn owned_predicate( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.mark_owned_predicate_as_unfolded(ty)?; + let mut builder = + OwnedNonAliasedUseBuilder::new(self, context, ty, generics, place, address, position)?; + builder.add_lifetime_arguments()?; + builder.add_const_arguments()?; + builder.set_maybe_permission_amount(permission_amount)?; + builder.build() } fn owned_non_aliased_full_vars( @@ -179,8 +487,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty: &vir_mid::Type, generics: &G, place: &vir_low::VariableDecl, - root_address: &vir_low::VariableDecl, - snapshot: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments, @@ -190,9 +498,34 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty, generics, place.clone().into(), - root_address.clone().into(), + address.clone().into(), + None, + position, + ) + } + + fn owned_non_aliased_full_vars_with_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, + snapshot: &vir_low::VariableDecl, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.owned_non_aliased_with_snapshot( + context, + ty, + generics, + place.clone().into(), + address.clone().into(), snapshot.clone().into(), None, + position, ) } @@ -202,26 +535,194 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty: &vir_mid::Type, generics: &G, place: vir_low::Expression, - root_address: vir_low::Expression, + address: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.owned_predicate( + context, + ty, + generics, + place, + address, + permission_amount, + position, + ) + } + + fn owned_non_aliased_with_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, snapshot: vir_low::Expression, permission_amount: Option, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments, { - let mut builder = OwnedNonAliasedUseBuilder::new( - self, + let predicate = self.owned_non_aliased( + context, + ty, + generics, + place.clone(), + address.clone(), + permission_amount, + position, + )?; + let snap_call = + self.owned_non_aliased_snap(context, ty, generics, place, address, position)?; + Ok(vir_low::Expression::and( + predicate, + vir_low::Expression::equals(snapshot, snap_call), + )) + } + + fn owned_aliased( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + address: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let place = self.place_option_none_constructor(position)?; + self.owned_non_aliased( context, ty, generics, place, - root_address, - snapshot, + address, + permission_amount, + position, + ) + } + + fn owned_aliased_range( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + address: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let builder = OwnedAliasedRangeUseBuilder::new( + self, + context, + ty, + generics, + address, + start_index, + end_index, + permission_amount, + position, + )?; + builder.build() + } + + fn owned_predicate_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.mark_owned_predicate_as_unfolded(ty)?; + let mut builder = OwnedNonAliasedSnapCallBuilder::new( + self, context, ty, generics, place, address, position, )?; builder.add_lifetime_arguments()?; builder.add_const_arguments()?; - builder.set_maybe_permission_amount(permission_amount)?; - Ok(builder.build()) + builder.build() + } + + fn owned_non_aliased_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.owned_predicate_snap(context, ty, generics, place, address, position) + } + + fn owned_aliased_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let place = self.place_option_none_constructor(position)?; + self.owned_predicate_snap(context, ty, generics, place, address, position) + } + + fn owned_aliased_range_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + address: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + if !self + .predicates_encoding_state + .owned + .used_owned_range_snapshot_functions + .contains(ty) + { + self.ensure_type_definition(ty)?; + self.predicates_encoding_state + .owned + .used_owned_range_snapshot_functions + .insert(ty.clone()); + } + let builder = OwnedAliasedRangeSnapCallBuilder::new( + self, + context, + ty, + generics, + address, + start_index, + end_index, + position, + )?; + builder.build() } fn unique_ref_full_vars( @@ -230,10 +731,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty: &vir_mid::Type, generics: &G, place: &vir_low::VariableDecl, - root_address: &vir_low::VariableDecl, - current_snapshot: &vir_low::VariableDecl, - final_snapshot: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, lifetime: &vir_low::VariableDecl, + target_slice_len: Option, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments, @@ -243,10 +744,40 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty, generics, place.clone().into(), - root_address.clone().into(), + address.clone().into(), + lifetime.clone().into(), + target_slice_len, + None, + position, + ) + } + + fn unique_ref_full_vars_with_current_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, + current_snapshot: &vir_low::VariableDecl, + lifetime: &vir_low::VariableDecl, + target_slice_len: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.unique_ref_with_current_snapshot( + context, + ty, + generics, + place.clone().into(), + address.clone().into(), current_snapshot.clone().into(), - final_snapshot.clone().into(), lifetime.clone().into(), + target_slice_len, + None, + position, ) } @@ -256,28 +787,123 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty: &vir_mid::Type, generics: &G, place: vir_low::Expression, - root_address: vir_low::Expression, - current_snapshot: vir_low::Expression, - final_snapshot: vir_low::Expression, + address: vir_low::Expression, lifetime: vir_low::Expression, + target_slice_len: Option, + permission_amount: Option, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments, { + self.mark_unique_ref_as_used(ty)?; let mut builder = UniqueRefUseBuilder::new( self, context, ty, generics, place, - root_address, - current_snapshot, - final_snapshot, + address, lifetime, + target_slice_len, + position, )?; builder.add_lifetime_arguments()?; builder.add_const_arguments()?; - Ok(builder.build()) + builder.set_maybe_permission_amount(permission_amount)?; + builder.build() + } + + fn unique_ref_with_current_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + current_snapshot: vir_low::Expression, + lifetime: vir_low::Expression, + target_slice_len: Option, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let predicate = self.unique_ref( + context, + ty, + generics, + place.clone(), + address.clone(), + lifetime.clone(), + target_slice_len.clone(), + permission_amount, + position, + )?; + let snap_call = self.unique_ref_snap( + context, + ty, + generics, + place, + address, + lifetime, + target_slice_len, + false, + position, + )?; + debug_assert_eq!(current_snapshot.get_type(), snap_call.get_type()); + Ok(vir_low::Expression::and( + predicate, + vir_low::Expression::equals(current_snapshot, snap_call), + )) + } + + fn unique_ref_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + lifetime: vir_low::Expression, + target_slice_len: Option, + is_final: bool, + _position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.mark_unique_ref_as_used(ty)?; + if is_final { + let mut builder = UniqueRefFinalSnapCallBuilder::new( + self, + context, + ty, + generics, + place, + address, + lifetime, + target_slice_len, + )?; + builder.add_lifetime_arguments()?; + builder.add_const_arguments()?; + builder.build() + } else { + let mut builder = UniqueRefCurrentSnapCallBuilder::new( + self, + context, + ty, + generics, + place, + address, + lifetime, + target_slice_len, + )?; + builder.add_lifetime_arguments()?; + builder.add_const_arguments()?; + builder.build() + } } fn frac_ref_full_vars( @@ -286,9 +912,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty: &vir_mid::Type, generics: &G, place: &vir_low::VariableDecl, - root_address: &vir_low::VariableDecl, - current_snapshot: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, lifetime: &vir_low::VariableDecl, + target_slice_len: Option, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments, @@ -298,9 +925,40 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty, generics, place.clone().into(), - root_address.clone().into(), + address.clone().into(), + lifetime.clone().into(), + target_slice_len, + None, + position, + ) + } + + fn frac_ref_full_vars_with_current_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: &vir_low::VariableDecl, + address: &vir_low::VariableDecl, + current_snapshot: &vir_low::VariableDecl, + lifetime: &vir_low::VariableDecl, + target_slice_len: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.frac_ref_with_current_snapshot( + context, + ty, + generics, + place.clone().into(), + address.clone().into(), current_snapshot.clone().into(), lifetime.clone().into(), + target_slice_len, + None, + position, ) } @@ -310,9 +968,11 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty: &vir_mid::Type, generics: &G, place: vir_low::Expression, - root_address: vir_low::Expression, - current_snapshot: vir_low::Expression, + address: vir_low::Expression, lifetime: vir_low::Expression, + target_slice_len: Option, + permission_amount: Option, + position: vir_low::Position, ) -> SpannedEncodingResult where G: WithLifetimes + WithConstArguments, @@ -323,12 +983,87 @@ impl<'p, 'v: 'p, 'tcx: 'v> PredicatesOwnedInterface for Lowerer<'p, 'v, 'tcx> { ty, generics, place, - root_address, - current_snapshot, + address, + lifetime, + target_slice_len, + position, + )?; + builder.add_lifetime_arguments()?; + builder.add_const_arguments()?; + builder.set_maybe_permission_amount(permission_amount)?; + builder.build() + } + + fn frac_ref_with_current_snapshot( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + current_snapshot: vir_low::Expression, + lifetime: vir_low::Expression, + target_slice_len: Option, + permission_amount: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + let predicate = self.frac_ref( + context, + ty, + generics, + place.clone(), + address.clone(), + lifetime.clone(), + target_slice_len.clone(), + permission_amount, + position, + )?; + let snap_call = self.frac_ref_snap( + context, + ty, + generics, + place, + address, + lifetime, + target_slice_len, + position, + )?; + Ok(vir_low::Expression::and( + predicate, + vir_low::Expression::equals(current_snapshot, snap_call), + )) + } + + fn frac_ref_snap( + &mut self, + context: CallContext, + ty: &vir_mid::Type, + generics: &G, + place: vir_low::Expression, + address: vir_low::Expression, + lifetime: vir_low::Expression, + target_slice_len: Option, + _position: vir_low::Position, + ) -> SpannedEncodingResult + where + G: WithLifetimes + WithConstArguments, + { + self.mark_frac_ref_as_used(ty)?; + let mut builder = FracRefSnapCallBuilder::new( + self, + context, + ty, + generics, + place, + address, lifetime, + target_slice_len, )?; builder.add_lifetime_arguments()?; builder.add_const_arguments()?; - Ok(builder.build()) + builder.build() } } diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/mod.rs index 75979d536e4..fe1a6fb470e 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/owned/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/owned/mod.rs @@ -6,6 +6,9 @@ mod interface; pub(super) use self::interface::PredicatesOwnedState; pub(in super::super) use self::{ - builders::{FracRefUseBuilder, OwnedNonAliasedUseBuilder, UniqueRefUseBuilder}, - interface::PredicatesOwnedInterface, + builders::{ + FracRefUseBuilder, OwnedNonAliasedSnapCallBuilder, OwnedNonAliasedUseBuilder, + UniqueRefUseBuilder, + }, + interface::{OwnedPredicateInfo, PredicatesOwnedInterface, SnapshotFunctionInfo}, }; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/restoration/interface.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/restoration/interface.rs new file mode 100644 index 00000000000..c6a31a14fa7 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/restoration/interface.rs @@ -0,0 +1,70 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::lowerer::{Lowerer, PredicatesLowererInterface}, +}; +use rustc_hash::FxHashSet; +use vir_crate::{common::identifier::WithIdentifier, low as vir_low, middle as vir_mid}; + +#[derive(Default)] +pub(in super::super) struct RestorationState { + encoded_restore_raw_borrowed_transition_predicate: FxHashSet, +} + +pub(in super::super::super) trait RestorationInterface { + fn encode_restore_raw_borrowed_transition_predicate( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()>; + fn restore_raw_borrowed( + &mut self, + ty: &vir_mid::Type, + place: vir_low::Expression, + address: vir_low::Expression, + ) -> SpannedEncodingResult; +} + +impl<'p, 'v: 'p, 'tcx: 'v> RestorationInterface for Lowerer<'p, 'v, 'tcx> { + fn encode_restore_raw_borrowed_transition_predicate( + &mut self, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult<()> { + let ty_identifier = ty.get_identifier(); + if !self + .predicates_encoding_state + .restoration + .encoded_restore_raw_borrowed_transition_predicate + .contains(&ty_identifier) + { + self.predicates_encoding_state + .restoration + .encoded_restore_raw_borrowed_transition_predicate + .insert(ty_identifier); + + use vir_low::macros::*; + let predicate = vir_low::PredicateDecl::new( + predicate_name! { RestoreRawBorrowed }, + vir_low::PredicateKind::WithoutSnapshotWhole, + vars!(place: Place, address: Address), + None, + ); + self.declare_predicate(predicate)?; + } + Ok(()) + } + fn restore_raw_borrowed( + &mut self, + ty: &vir_mid::Type, + place: vir_low::Expression, + address: vir_low::Expression, + ) -> SpannedEncodingResult { + self.encode_restore_raw_borrowed_transition_predicate(ty)?; + use vir_low::macros::*; + let predicate = expr! { + acc(RestoreRawBorrowed( + [place], + [address] + )) + }; + Ok(predicate) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/restoration/mod.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/restoration/mod.rs new file mode 100644 index 00000000000..58ddd243565 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/restoration/mod.rs @@ -0,0 +1,6 @@ +//! Encoder of predicates that guard restoration of permissions. + +mod interface; + +pub(in super::super) use self::interface::RestorationInterface; +pub(super) use self::interface::RestorationState; diff --git a/prusti-viper/src/encoder/middle/core_proof/predicates/state.rs b/prusti-viper/src/encoder/middle/core_proof/predicates/state.rs index 6b5f047885e..f04d3f7e72b 100644 --- a/prusti-viper/src/encoder/middle/core_proof/predicates/state.rs +++ b/prusti-viper/src/encoder/middle/core_proof/predicates/state.rs @@ -1,7 +1,12 @@ -use super::{memory_block::PredicatesMemoryBlockState, owned::PredicatesOwnedState}; +use super::{ + aliasing::PredicatesAliasingState, memory_block::PredicatesMemoryBlockState, + owned::PredicatesOwnedState, restoration::RestorationState, +}; #[derive(Default)] pub(in super::super) struct PredicatesState { pub(super) owned: PredicatesOwnedState, pub(super) memory_block: PredicatesMemoryBlockState, + pub(super) restoration: RestorationState, + pub(super) aliasing: PredicatesAliasingState, } diff --git a/prusti-viper/src/encoder/middle/core_proof/references/interface.rs b/prusti-viper/src/encoder/middle/core_proof/references/interface.rs index 20ec1f726b2..0dcb3e669a9 100644 --- a/prusti-viper/src/encoder/middle/core_proof/references/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/references/interface.rs @@ -7,6 +7,7 @@ use crate::encoder::{ snapshots::{ IntoSnapshot, SnapshotAdtsInterface, SnapshotDomainsInterface, SnapshotValuesInterface, }, + type_layouts::TypeLayoutsInterface, types::TypesInterface, }, }; @@ -15,17 +16,17 @@ use vir_crate::{ middle::{self as vir_mid}, }; -trait Private { - fn reference_target_snapshot( - &mut self, - ty: &vir_mid::Type, - snapshot: vir_low::Expression, - position: vir_low::Position, - version: &str, - ) -> SpannedEncodingResult; -} +// trait Private { +// fn reference_target_snapshot( +// &mut self, +// ty: &vir_mid::Type, +// snapshot: vir_low::Expression, +// position: vir_low::Position, +// version: &str, +// ) -> SpannedEncodingResult; +// } -impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { +impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { fn reference_target_snapshot( &mut self, ty: &vir_mid::Type, @@ -72,6 +73,12 @@ pub(in super::super) trait ReferencesInterface { snapshot: vir_low::Expression, position: vir_low::Position, ) -> SpannedEncodingResult; + fn reference_slice_len( + &mut self, + reference_type: &vir_mid::Type, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult>; fn reference_address_snapshot( &mut self, reference_type: &vir_mid::Type, @@ -124,6 +131,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> ReferencesInterface for Lowerer<'p, 'v, 'tcx> { snapshot: vir_low::Expression, position: vir_low::Position, ) -> SpannedEncodingResult { + assert!( + ty.is_unique_reference(), + "Expected unique reference, got {ty}" + ); self.reference_target_snapshot(ty, snapshot, position, "target_final") } fn reference_address( @@ -133,11 +144,33 @@ impl<'p, 'v: 'p, 'tcx: 'v> ReferencesInterface for Lowerer<'p, 'v, 'tcx> { position: vir_low::Position, ) -> SpannedEncodingResult { assert!(reference_type.is_reference()); - let domain_name = self.encode_snapshot_domain_name(reference_type)?; + // let domain_name = self.encode_snapshot_domain_name(reference_type)?; let return_type = self.address_type()?; - Ok(self - .snapshot_destructor_struct_call(&domain_name, "address", return_type, snapshot)? - .set_default_position(position)) + self.obtain_parameter_snapshot(reference_type, "address", return_type, snapshot, position) + // Ok(self + // .snapshot_destructor_struct_call(&domain_name, "address", return_type, snapshot)? + // .set_default_position(position)) + } + fn reference_slice_len( + &mut self, + reference_type: &vir_mid::Type, + snapshot: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult> { + assert!(reference_type.is_reference()); + let len = if reference_type.is_reference_to_slice() { + let return_type = self.size_type()?; + Some(self.obtain_parameter_snapshot( + reference_type, + "len", + return_type, + snapshot, + position, + )?) + } else { + None + }; + Ok(len) } fn reference_address_snapshot( &mut self, @@ -145,9 +178,13 @@ impl<'p, 'v: 'p, 'tcx: 'v> ReferencesInterface for Lowerer<'p, 'v, 'tcx> { snapshot: vir_low::Expression, position: vir_low::Position, ) -> SpannedEncodingResult { - let address = self.reference_address(reference_type, snapshot, position)?; + let address = self.reference_address(reference_type, snapshot.clone(), position)?; + let mut arguments = vec![address]; let address_type = self.reference_address_type(reference_type)?; - self.construct_struct_snapshot(&address_type, vec![address], position) + if let Some(len) = self.reference_slice_len(reference_type, snapshot, position)? { + arguments.push(len); + }; + self.construct_struct_snapshot(&address_type, arguments, position) } fn reference_address_type( &mut self, diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/interface.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/interface.rs index ec84dad71db..0e8677259e6 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/interface.rs @@ -78,6 +78,8 @@ pub(in super::super::super) trait SnapshotAdtsInterface { &mut self, domain_name: &str, variant_name: &str, + unary_operation: Option, + binary_operation: Option, use_main_constructor_destructors: bool, parameters: Vec, ) -> SpannedEncodingResult<()>; @@ -102,13 +104,21 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotAdtsInterface for Lowerer<'p, 'v, 'tcx> { &mut self, domain_name: &str, ) -> SpannedEncodingResult { - self.adt_destructor_main_name(domain_name, "value") + let name = self.adt_destructor_main_name(domain_name, "value")?; + self.snapshots_state + .snapshot_domains_info + .register_constant_destructor(domain_name, &name)?; + Ok(name) } fn snapshot_constructor_struct_name( &mut self, domain_name: &str, ) -> SpannedEncodingResult { - self.adt_constructor_main_name(domain_name) + let name = self.adt_constructor_main_name(domain_name)?; + self.snapshots_state + .snapshot_domains_info + .register_constant_constructor(domain_name, &name)?; + Ok(name) } fn snapshot_constructor_struct_alternative_name( &mut self, @@ -129,6 +139,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotAdtsInterface for Lowerer<'p, 'v, 'tcx> { domain_name: &str, arguments: Vec, ) -> SpannedEncodingResult { + let _ = self.snapshot_constructor_struct_name(domain_name)?; // FIXME: this is a hack to trigger registration. self.adt_constructor_main_call(domain_name, arguments) } fn snapshot_alternative_constructor_struct_call( @@ -181,12 +192,27 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotAdtsInterface for Lowerer<'p, 'v, 'tcx> { &mut self, domain_name: &str, variant_name: &str, + unary_operation: Option, + binary_operation: Option, use_main_constructor_destructors: bool, parameters: Vec, ) -> SpannedEncodingResult<()> { + if let Some(op) = unary_operation { + let constructor_name = self.adt_constructor_variant_name(domain_name, variant_name)?; + self.snapshots_state + .snapshot_domains_info + .register_unary_operation(domain_name, op, constructor_name)?; + } + if let Some(op) = binary_operation { + let constructor_name = self.adt_constructor_variant_name(domain_name, variant_name)?; + self.snapshots_state + .snapshot_domains_info + .register_binary_operation(domain_name, op, constructor_name)?; + } self.adt_register_variant_constructor( domain_name, variant_name, + // operation, use_main_constructor_destructors, parameters, false, @@ -203,6 +229,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotAdtsInterface for Lowerer<'p, 'v, 'tcx> { self.adt_register_variant_constructor( domain_name, variant_name, + // None, use_main_constructor_destructors, parameters, true, @@ -235,6 +262,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotAdtsInterface for Lowerer<'p, 'v, 'tcx> { self.adt_register_variant_constructor( domain_name, variant_name, + // None, false, vars! { value: {parameter_type}}, true, diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/mod.rs index 199dec471a2..3d570c1ca39 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/mod.rs @@ -6,5 +6,6 @@ //! `prusti-viper/src/encoder/middle/core_proof/adts/interface.rs`. mod interface; +mod state; -pub(in super::super) use self::interface::SnapshotAdtsInterface; +pub(in super::super) use self::{interface::SnapshotAdtsInterface, state::SnapshotDomainsInfo}; diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/state.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/state.rs new file mode 100644 index 00000000000..4c338834717 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/adts/state.rs @@ -0,0 +1,85 @@ +use crate::encoder::errors::SpannedEncodingResult; +use std::collections::BTreeMap; +use vir_crate::low::{self as vir_low}; + +#[derive(Default)] +pub(in super::super::super) struct SnapshotDomainsInfo { + /// A map from a snapshot domain name to information about the snapshot domain. + pub(in super::super::super) snapshot_domains: BTreeMap, +} + +#[derive(Default)] +pub(in super::super::super) struct SnapshotDomainInfo { + /// The name of the domain function used to create constant values. + pub(in super::super::super) constant_constructor_name: Option, + /// The name of the domain function used to destruct constant values. + pub(in super::super::super) constant_destructor_name: Option, + /// The binary operators that correspond to the given domain functions. + pub(in super::super::super) binary_operators: BTreeMap, + /// The unary operators that correspond to the given domain functions. + pub(in super::super::super) unary_operators: BTreeMap, +} + +impl SnapshotDomainsInfo { + pub(in super::super) fn register_constant_constructor( + &mut self, + domain_name: &str, + function_name: &str, + ) -> SpannedEncodingResult<()> { + let snapshot_domain = self.get_snapshot_domain(domain_name)?; + if snapshot_domain.constant_constructor_name.is_none() { + snapshot_domain.constant_constructor_name = Some(function_name.to_string()); + } + Ok(()) + } + + pub(in super::super) fn register_constant_destructor( + &mut self, + domain_name: &str, + function_name: &str, + ) -> SpannedEncodingResult<()> { + let snapshot_domain = self.get_snapshot_domain(domain_name)?; + if snapshot_domain.constant_destructor_name.is_none() { + snapshot_domain.constant_destructor_name = Some(function_name.to_string()); + } + Ok(()) + } + + pub(in super::super) fn register_unary_operation( + &mut self, + domain_name: &str, + op: vir_low::UnaryOpKind, + function_name: String, + ) -> SpannedEncodingResult<()> { + let snapshot_domain = self.get_snapshot_domain(domain_name)?; + assert!(snapshot_domain + .unary_operators + .insert(function_name, op) + .is_none()); + Ok(()) + } + + pub(in super::super) fn register_binary_operation( + &mut self, + domain_name: &str, + op: vir_low::BinaryOpKind, + function_name: String, + ) -> SpannedEncodingResult<()> { + let snapshot_domain = self.get_snapshot_domain(domain_name)?; + assert!(snapshot_domain + .binary_operators + .insert(function_name, op) + .is_none()); + Ok(()) + } + + fn get_snapshot_domain( + &mut self, + domain_name: &str, + ) -> SpannedEncodingResult<&mut SnapshotDomainInfo> { + Ok(self + .snapshot_domains + .entry(domain_name.to_string()) + .or_default()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/bytes/interface.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/bytes/interface.rs index cd6f7741c4c..13d3b2752f8 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/bytes/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/bytes/interface.rs @@ -6,6 +6,7 @@ use crate::encoder::{ snapshots::SnapshotDomainsInterface, }, }; +use prusti_common::config; use vir_crate::{ common::identifier::WithIdentifier, low::{self as vir_low}, @@ -38,13 +39,70 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotBytesInterface for Lowerer<'p, 'v, 'tcx> { let domain_name = self.encode_snapshot_domain_name(ty)?; let domain_type = self.encode_snapshot_domain_type(ty)?; let return_type = self.bytes_type()?; + let to_bytes = format!("to_bytes${}", ty.get_identifier()); + let snapshot = vir_low::VariableDecl::new("snapshot", domain_type.clone()); self.declare_domain_function( &domain_name, - std::borrow::Cow::Owned(format!("to_bytes${}", ty.get_identifier())), + std::borrow::Cow::Owned(to_bytes.clone()), false, - std::borrow::Cow::Owned(vec![vir_low::VariableDecl::new("snapshot", domain_type)]), - std::borrow::Cow::Owned(return_type), + std::borrow::Cow::Owned(vec![snapshot.clone()]), + std::borrow::Cow::Owned(return_type.clone()), )?; + if !config::use_snapshot_parameters_in_predicates() + && matches!( + ty, + vir_mid::Type::Bool + | vir_mid::Type::Int(_) + | vir_mid::Type::Float(_) + | vir_mid::Type::Pointer(_) + | vir_mid::Type::Sequence(_) + | vir_mid::Type::Map(_) + ) + { + // This is sound only for primitive types. + let from_bytes = format!("from_bytes${}", ty.get_identifier()); + self.declare_domain_function( + &domain_name, + std::borrow::Cow::Owned(from_bytes.clone()), + false, + std::borrow::Cow::Owned(vec![vir_low::VariableDecl::new( + "bytes", + return_type.clone(), + )]), + std::borrow::Cow::Owned(domain_type.clone()), + )?; + + let to_bytes_call = vir_low::Expression::domain_function_call( + domain_name.clone(), + to_bytes, + vec![snapshot.clone().into()], + return_type, + ); + let from_bytes_call = vir_low::Expression::domain_function_call( + domain_name.clone(), + from_bytes, + vec![to_bytes_call.clone()], + domain_type, + ); + // let body = vir_low::Expression::forall( + // vec![snapshot.clone()], + // vec![vir_low::Trigger::new(vec![to_bytes_call])], + // expr! { + // snapshot == [ from_bytes_call ] + // }, + // ); + let axiom = vir_low::DomainRewriteRuleDecl { + // We use ty identifier to distinguish sequences from arrays. + name: format!("{}${}$to_bytes_injective", domain_name, ty.get_identifier()), + comment: None, + egg_only: false, + variables: vec![snapshot.clone()], + triggers: Some(vec![vir_low::Trigger::new(vec![to_bytes_call])]), + source: snapshot.into(), + target: from_bytes_call, + }; + self.declare_rewrite_rule(&domain_name, axiom)?; + } } Ok(()) } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/domains/interface.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/domains/interface.rs index 6e3c004141f..b20076183ab 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/domains/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/domains/interface.rs @@ -3,6 +3,7 @@ use crate::encoder::{ middle::core_proof::{ lifetimes::LifetimesInterface, lowerer::{DomainsLowererInterface, Lowerer}, + predicates::PredicatesMemoryBlockInterface, }, }; use std::collections::hash_map::Entry; @@ -95,6 +96,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotDomainsInterface for Lowerer<'p, 'v, 'tcx> { vir_mid::Type::MBool => Ok(vir_low::Type::Bool), vir_mid::Type::MInt => Ok(vir_low::Type::Int), vir_mid::Type::MPerm => Ok(vir_low::Type::Perm), + vir_mid::Type::MByte => self.byte_type(), + vir_mid::Type::MBytes => self.bytes_type(), vir_mid::Type::Sequence(seq) => { let enc_elem = self.encode_snapshot_domain_type(&seq.element_type)?; let low_ty = vir_low::Type::seq(enc_elem); diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/constructor.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/constructor.rs new file mode 100644 index 00000000000..94ae6ea630c --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/constructor.rs @@ -0,0 +1,506 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, + footprint::{DerefFields, DerefOwned, DerefOwnedRange}, + lowerer::{DomainsLowererInterface, Lowerer}, + places::PlacesInterface, + pointers::PointersInterface, + predicates::PredicatesOwnedInterface, + snapshots::{ + IntoSnapshot, IntoSnapshotLowerer, SnapshotDomainsInterface, SnapshotValuesInterface, + }, + }, +}; +use rustc_hash::FxHashMap; +use std::collections::BTreeMap; +use vir_crate::{ + common::position::Positioned, + low::{self as vir_low}, + middle::{self as vir_mid, operations::ty::Typed}, +}; + +use super::PredicateKind; + +pub(in super::super::super::super) struct AssertionToSnapshotConstructor<'a> { + predicate_kind: PredicateKind, + ty: &'a vir_mid::Type, + /// Arguments for the regular struct fields. + regular_field_arguments: Vec, + /// A map for replacing `self.field` with a matching argument. Used in + /// assign postcondition. + field_replacement_map: FxHashMap, + /// Mapping from deref fields to their positions in the arguments' list. + deref_fields: BTreeMap, + /// Mapping from deref range fields to their positions in the arguments' list. + deref_range_fields: BTreeMap, + /// Which places are framed on the path being explored. + framed_places: Vec, + /// Which addresses are framed on the path being explored. + /// + /// The tuple is `(address, start_index, end_index)`. + framed_range_addresses: Vec<( + vir_mid::Expression, + vir_mid::Expression, + vir_mid::Expression, + )>, + /// Whether should wrap all snap calls into old. + is_in_old_state: bool, + /// A flag used to check whether a conditional has nested conditionals. + found_conditional: bool, + position: vir_low::Position, +} + +fn deref_fields_into_maps( + regular_field_arguments_len: usize, + (deref_fields, deref_range_fields): DerefFields, +) -> ( + BTreeMap, + BTreeMap, +) { + let deref_fields = deref_fields + .into_iter() + .enumerate() + .map(|(i, DerefOwned { place, .. })| (i + regular_field_arguments_len, place)) + .collect::>(); + let deref_range_fields = deref_range_fields + .into_iter() + .enumerate() + .map(|(i, DerefOwnedRange { address, .. })| { + ( + i + deref_fields.len() + regular_field_arguments_len, + address, + ) + }) + .collect(); + (deref_fields, deref_range_fields) +} + +impl<'a> AssertionToSnapshotConstructor<'a> { + pub(in super::super::super::super) fn for_assign_aggregate_postcondition( + ty: &'a vir_mid::Type, + regular_field_arguments: Vec, + fields: Vec, + all_deref_fields: DerefFields, + position: vir_low::Position, + ) -> Self { + let field_replacement_map = fields + .into_iter() + .zip(regular_field_arguments.iter().cloned()) + .collect(); + let (deref_fields, deref_range_fields) = + deref_fields_into_maps(regular_field_arguments.len(), all_deref_fields); + Self { + predicate_kind: PredicateKind::Owned, + ty, + regular_field_arguments, + field_replacement_map, + deref_fields, + deref_range_fields, + framed_places: Vec::new(), + framed_range_addresses: Vec::new(), + is_in_old_state: true, + found_conditional: false, + position, + } + } + + pub(in super::super::super::super) fn for_function_body( + predicate_kind: PredicateKind, + ty: &'a vir_mid::Type, + regular_field_arguments: Vec, + fields: Vec, + all_deref_fields: DerefFields, + position: vir_low::Position, + ) -> Self { + let field_replacement_map = fields + .into_iter() + .zip(regular_field_arguments.iter().cloned()) + .collect(); + let (deref_fields, deref_range_fields) = + deref_fields_into_maps(regular_field_arguments.len(), all_deref_fields); + Self { + predicate_kind, + ty, + regular_field_arguments, + field_replacement_map, + deref_fields, + deref_range_fields, + framed_places: Vec::new(), + framed_range_addresses: Vec::new(), + is_in_old_state: false, + found_conditional: false, + position, + } + } + + pub(in super::super::super::super) fn expression_to_snapshot_constructor<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + expression: &vir_mid::Expression, + ) -> SpannedEncodingResult { + let constructor_expression = self.expression_to_snapshot(lowerer, expression, true)?; + if self.found_conditional { + Ok(constructor_expression) + } else { + self.generate_snapshot_constructor(lowerer) + } + } + + // FIXME: Code duplication. + fn snap_call<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ty: &vir_mid::Type, + place: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + match &self.predicate_kind { + PredicateKind::Owned => lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + position, + ), + PredicateKind::FracRef { lifetime } => { + let TODO_target_slice_len = None; + lowerer.frac_ref_snap( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + lifetime.clone(), + TODO_target_slice_len, + position, + ) + } + PredicateKind::UniqueRef { lifetime, is_final } => { + let TODO_target_slice_len = None; + lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + lifetime.clone(), + TODO_target_slice_len, + *is_final, + position, + ) + } + } + } + + fn snap_range_call<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ty: &vir_mid::Type, + address: vir_low::Expression, + start_index: vir_low::Expression, + end_index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + match &self.predicate_kind { + PredicateKind::Owned => lowerer.owned_aliased_range_snap( + CallContext::BuiltinMethod, + ty, + ty, + address, + start_index, + end_index, + position, + ), + PredicateKind::FracRef { lifetime: _ } => { + unimplemented!(); + } + PredicateKind::UniqueRef { + lifetime: _, + is_final: _, + } => { + unimplemented!(); + } + } + } + + fn generate_dangling_snapshot<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ty: &vir_mid::Type, + ) -> SpannedEncodingResult { + let domain_name = lowerer.encode_snapshot_domain_name(ty)?; + let function_name = format!("{domain_name}$dangling"); + let return_type = ty.to_snapshot(lowerer)?; + lowerer.create_unique_domain_func_app( + domain_name, + function_name, + Vec::new(), + return_type, + self.position, + ) + } + + fn compute_deref_address<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + let address = match place { + vir_mid::Expression::Local(_) => unreachable!("{place}"), + vir_mid::Expression::Field(_) => todo!(), + vir_mid::Expression::Deref(deref) => { + let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, true)?; + let ty = deref.base.get_type(); + lowerer.pointer_address(ty, base_snapshot, place.position())? + } + _ => unimplemented!("{place}"), + }; + Ok(address) + } + + fn generate_snapshot_constructor<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult { + let mut arguments = self.regular_field_arguments.clone(); + for deref_field in self.deref_fields.clone().values() { + let ty = deref_field.get_type(); + let deref_field_snapshot = if self.framed_places.contains(deref_field) { + // The place is framed, generate the snap call. + let place = lowerer.encode_expression_as_place(deref_field)?; + // Note: we cannot use `encode_expression_as_place_address` here + // because that method can be used only inside procedure with + // SSA addresses. Therefore, we need to compute the address + // ourselves. + let address = self.compute_deref_address(lowerer, deref_field)?; + let snap_call = self.snap_call(lowerer, ty, place, address, self.position)?; + if self.is_in_old_state { + vir_low::Expression::labelled_old(None, snap_call, self.position) + } else { + snap_call + } + } else { + // The place is not framed. Create a dangling (null) snapshot. + self.generate_dangling_snapshot(lowerer, ty)? + }; + arguments.push(deref_field_snapshot); + } + for deref_range_field in self.deref_range_fields.clone().values() { + let ty = deref_range_field.get_type(); + let deref_range_field_snapshot = + if let Some((pointer_value_mid, start_index, end_index)) = self + .framed_range_addresses + .iter() + .find(|(address, _, _)| deref_range_field == address) + .cloned() + { + // The address is framed, generate the snap call. + let pointer_value = + self.expression_to_snapshot(lowerer, &pointer_value_mid, true)?; + let start_index = self.expression_to_snapshot(lowerer, &start_index, true)?; + let end_index = self.expression_to_snapshot(lowerer, &end_index, true)?; + let snap_call = self.snap_range_call( + lowerer, + ty, + pointer_value, + start_index, + end_index, + self.position, + )?; + if self.is_in_old_state { + vir_low::Expression::labelled_old(None, snap_call, self.position) + } else { + snap_call + } + } else { + // The place is not framed. Create a dangling (null) snapshot. + self.generate_dangling_snapshot(lowerer, ty)? + }; + arguments.push(deref_range_field_snapshot); + } + lowerer.construct_struct_snapshot(self.ty, arguments, self.position) + } + + // // FIXME: Code duplication. + // fn pointer_deref_into_address<'p, 'v, 'tcx>( + // &mut self, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // place: &vir_mid::Expression, + // ) -> SpannedEncodingResult { + // if let Some(deref_place) = place.get_last_dereferenced_pointer() { + // let base_snapshot = self.expression_to_snapshot(lowerer, deref_place, true)?; + // let ty = deref_place.get_type(); + // lowerer.pointer_address(ty, base_snapshot, place.position()) + // } else { + // unreachable!() + // } + // } + + fn conditional_branch_to_snapshot<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + branch: &vir_mid::Expression, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + self.found_conditional = false; + let old_framed_places_count = self.framed_places.len(); + let branch_snapshot = self.expression_to_snapshot(lowerer, branch, expect_math_bool)?; + let expression = if !self.found_conditional { + // We reached the lowest level, generate the snapshot constructor. + self.generate_snapshot_constructor(lowerer)? + } else { + branch_snapshot + }; + self.framed_places.truncate(old_framed_places_count); + Ok(expression) + } +} + +impl<'a, 'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> + for AssertionToSnapshotConstructor<'a> +{ + fn conditional_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + conditional: &vir_mid::Conditional, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let guard_snapshot = self.expression_to_snapshot(lowerer, &conditional.guard, true)?; + + let then_expr_snapshot = + self.conditional_branch_to_snapshot(lowerer, &conditional.then_expr, expect_math_bool)?; + let else_expr_snapshot = + self.conditional_branch_to_snapshot(lowerer, &conditional.else_expr, expect_math_bool)?; + + self.found_conditional = true; + Ok(vir_low::Expression::conditional( + guard_snapshot, + then_expr_snapshot, + else_expr_snapshot, + conditional.position, + )) + } + + fn field_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + field: &vir_mid::Field, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + match &*field.base { + vir_mid::Expression::Local(local) + if local.variable.is_self_variable() + && self.field_replacement_map.contains_key(&field.field) => + { + Ok(self.field_replacement_map[&field.field].clone()) + } + _ => self.field_to_snapshot_impl(lowerer, field, expect_math_bool), + } + } + + fn variable_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _variable: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult { + todo!() + } + + fn labelled_old_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _old: &vir_mid::LabelledOld, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn func_app_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _app: &vir_mid::FuncApp, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn acc_predicate_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + acc_predicate: &vir_mid::AccPredicate, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + match &*acc_predicate.predicate { + vir_mid::Predicate::LifetimeToken(_) + | vir_mid::Predicate::MemoryBlockStack(_) + | vir_mid::Predicate::MemoryBlockStackDrop(_) => { + unreachable!(); + } + vir_mid::Predicate::MemoryBlockHeap(_) + | vir_mid::Predicate::MemoryBlockHeapRange(_) + | vir_mid::Predicate::MemoryBlockHeapDrop(_) => { + // Do nothing. + } + vir_mid::Predicate::OwnedNonAliased(predicate) => { + self.framed_places.push(predicate.place.clone()); + } + vir_mid::Predicate::OwnedRange(predicate) => { + self.framed_range_addresses.push(( + predicate.address.clone(), + predicate.start_index.clone(), + predicate.end_index.clone(), + )); + } + vir_mid::Predicate::OwnedSet(_) => todo!(), + } + Ok(true.into()) + } + + // FIXME: Code duplication. + fn pointer_deref_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _deref: &vir_mid::Deref, + _base_snapshot: vir_low::Expression, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + unimplemented!("outdated code"); + // let heap = self + // .heap + // .clone() + // .expect("This function should be reachable only when heap is Some"); + // lowerer.pointer_target_snapshot_in_heap( + // deref.base.get_type(), + // heap, + // base_snapshot, + // deref.position, + // ) + } + + fn call_context(&self) -> CallContext { + todo!() + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + todo!() + } + + fn push_bound_variables( + &mut self, + _variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + todo!() + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + todo!() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/mod.rs new file mode 100644 index 00000000000..21f3d23f056 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/mod.rs @@ -0,0 +1,31 @@ +use vir_crate::low::{self as vir_low}; + +/// Assertions that are self-framing: each dereference of a pointer needs to be +/// behind `own`. +mod self_framing; +/// Assertions where the places (leaves) are translated to `snap` calls. +mod snap; +/// Assertions where the places are translated by using `heap$` pure variable. +mod pure_heap; +/// The snapshot validity assertion. +mod validity; +/// Structural invariant that needs to be translated into a snapshot +/// constructor. +mod constructor; + +pub(in super::super::super::super) enum PredicateKind { + Owned, + FracRef { + lifetime: vir_low::Expression, + }, + UniqueRef { + lifetime: vir_low::Expression, + is_final: bool, + }, +} + +pub(in super::super::super) use self::{ + constructor::AssertionToSnapshotConstructor, + self_framing::{SelfFramingAssertionEncoderState, SelfFramingAssertionToSnapshot}, + validity::ValidityAssertionToSnapshot, +}; diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/pure_heap.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/pure_heap.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/pure_heap.rs @@ -0,0 +1 @@ + diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/self_framing.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/self_framing.rs new file mode 100644 index 00000000000..0b33daac7fb --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/self_framing.rs @@ -0,0 +1,821 @@ +use std::collections::BTreeMap; + +use super::PredicateKind; +use crate::encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + lowerer::{FunctionsLowererInterface, Lowerer}, + places::PlacesInterface, + pointers::PointersInterface, + predicates::{PredicatesMemoryBlockInterface, PredicatesOwnedInterface}, + snapshots::{ + into_snapshot::utils::bound_variable_stack::BoundVariableStack, IntoSnapshotLowerer, + SnapshotValuesInterface, SnapshotVariablesInterface, + }, + type_layouts::TypeLayoutsInterface, + }, +}; +use rustc_hash::FxHashMap; +use vir_crate::{ + common::{identifier::WithIdentifier, position::Positioned}, + low::{self as vir_low}, + middle::{self as vir_mid, operations::ty::Typed}, +}; + +#[derive(Clone, Debug, PartialEq, Eq, derive_more::IsVariant)] +enum CallerKind { + PredicateBody { + /// The `place` parameter of the predicate. + place: vir_low::VariableDecl, + /// The `address` parameter of the predicate. + address: vir_low::VariableDecl, + }, + AssignPrecondition { + /// A map for replacing `self.field` with a matching argument. + field_replacement_map: FxHashMap, + }, + InhaleExhale, + // PlaceExpression, +} + +#[derive(Default)] +pub(in super::super::super::super::super) struct SelfFramingAssertionEncoderState { + states: BTreeMap, +} + +#[derive(Default)] +struct State { + snap_calls: Vec<(vir_mid::Expression, vir_low::Expression)>, + range_snap_calls: Vec<(vir_low::Expression, (vir_mid::Type, vir_low::Position))>, +} + +// Based on +// prusti-viper/src/encoder/middle/core_proof/predicates/owned/builders/owned_non_aliased/predicate_decl.rs, +// whch should be deleted. +pub(in super::super::super::super::super) struct SelfFramingAssertionToSnapshot { + /// Indicates which constructor was used. This is used only for assertions + /// to ensure that certain branches are unreachable. + caller_kind: CallerKind, + /// Do we need to use SSA when encoding variables? + use_ssa: bool, + /// Which kind of predicate is being encoded? + predicate_kind: PredicateKind, + /// Keeps track of types for which we need to encode predicates. + created_predicate_types: Vec, + /// Mapping from place to snapshot. We use a vector because we need to know + /// the insertion order. + snap_calls: Vec<(vir_mid::Expression, vir_low::Expression)>, + /// Mapping from the start of owned range to information needed to compute + /// the snapshot of an element. We use a vector because we need to know the + /// insertion order. + range_snap_calls: Vec<(vir_low::Expression, (vir_mid::Type, vir_low::Position))>, + /// If true, removes the accessibility predicates from the result. + is_target_pure: bool, + /// The old label in the currently converted subexpression. + old_label: Option, + /// Variables introduced by quantifiers. + bound_variable_stack: BoundVariableStack, + /// The label of the current state in which information about `snap_calls` + /// and `range_snap_calls` should be stored. + /// + /// This is used only for inhale and exhale statements. + current_state_label: Option, +} + +impl SelfFramingAssertionToSnapshot { + /// Used for encoding structural invariant as a predicate body. + pub(in super::super::super::super::super) fn for_predicate_body( + place: vir_low::VariableDecl, + address: vir_low::VariableDecl, + predicate_kind: PredicateKind, + ) -> Self { + Self { + caller_kind: CallerKind::PredicateBody { place, address }, + use_ssa: false, + predicate_kind, + created_predicate_types: Vec::new(), + snap_calls: Vec::new(), + range_snap_calls: Vec::new(), + is_target_pure: false, + old_label: None, + bound_variable_stack: Default::default(), + current_state_label: None, + } + } + + /// Used for encoding structural invariant as a assign helper method + /// postcondition. + pub(in super::super::super::super::super) fn for_assign_precondition( + regular_field_arguments: Vec, + fields: Vec, + ) -> Self { + let field_replacement_map = fields + .into_iter() + .zip(regular_field_arguments.iter().cloned()) + .collect(); + Self { + caller_kind: CallerKind::AssignPrecondition { + field_replacement_map, + }, + use_ssa: false, + predicate_kind: PredicateKind::Owned, + created_predicate_types: Vec::new(), + snap_calls: Vec::new(), + range_snap_calls: Vec::new(), + is_target_pure: false, + old_label: None, + bound_variable_stack: Default::default(), + current_state_label: None, + } + } + + /// Used for encoding inhale and exhale statements. + pub(in super::super::super::super::super) fn for_inhale_exhale_expression( + current_state_label: Option, + ) -> Self { + Self { + caller_kind: CallerKind::InhaleExhale, + use_ssa: true, + predicate_kind: PredicateKind::Owned, + created_predicate_types: Vec::new(), + snap_calls: Vec::new(), + range_snap_calls: Vec::new(), + is_target_pure: false, + old_label: None, + bound_variable_stack: Default::default(), + current_state_label, + } + } + + // Use PlaceToSnapshot::for_place instead. + // /// Used for encoding place expressions in procedures. + // pub(in super::super::super::super::super) fn for_place_expression() -> Self { + // Self { + // caller_kind: CallerKind::PlaceExpression, + // use_ssa: true, + // predicate_kind: PredicateKind::Owned, + // created_predicate_types: Vec::new(), + // snap_calls: Vec::new(), + // range_snap_calls: Vec::new(), + // is_target_pure: false, + // old_label: None, + // bound_variable_stack: Default::default(), + // current_state_label: None, + // } + // } + + pub(in super::super::super::super::super) fn into_created_predicate_types( + self, + ) -> Vec { + self.created_predicate_types + } + + fn predicate_place(&self) -> vir_low::Expression { + let CallerKind::PredicateBody { ref place, .. } = self.caller_kind else { + unreachable!() + }; + place.clone().into() + } + + fn predicate_address(&self) -> vir_low::Expression { + let CallerKind::PredicateBody { ref address, .. } = self.caller_kind else { + unreachable!() + }; + address.clone().into() + } + + // // FIXME: Code duplication. + // fn pointer_deref_into_address<'p, 'v, 'tcx>( + // &mut self, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // place: &vir_mid::Expression, + // ) -> SpannedEncodingResult { + // if let Some(deref_place) = place.get_last_dereferenced_pointer() { + // let base_snapshot = self.expression_to_snapshot(lowerer, deref_place, true)?; + // let ty = deref_place.get_type(); + // lowerer.pointer_address(ty, base_snapshot, place.position()) + // } else { + // unreachable!() + // } + // } + + // FIXME: Code duplication. + fn snap_call<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ty: &vir_mid::Type, + place: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + match &self.predicate_kind { + PredicateKind::Owned => lowerer.owned_non_aliased_snap( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + position, + ), + PredicateKind::FracRef { lifetime } => { + let TODO_target_slice_len = None; + lowerer.frac_ref_snap( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + lifetime.clone(), + TODO_target_slice_len, + position, + ) + } + PredicateKind::UniqueRef { lifetime, is_final } => { + assert!(!is_final); + let TODO_target_slice_len = None; + lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + lifetime.clone(), + TODO_target_slice_len, + false, + position, + ) + } + } + } + + fn predicate<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ty: &vir_mid::Type, + place: vir_low::Expression, + address: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + self.created_predicate_types.push(ty.clone()); + match &self.predicate_kind { + PredicateKind::Owned => lowerer.owned_non_aliased( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + None, + position, + ), + PredicateKind::FracRef { lifetime } => { + let TODO_target_slice_len = None; + lowerer.frac_ref( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + lifetime.clone(), + TODO_target_slice_len, + None, + position, + ) + } + PredicateKind::UniqueRef { lifetime, is_final } => { + assert!(!is_final); + let TODO_target_slice_len = None; + let _final_snapshot = lowerer.unique_ref_snap( + CallContext::BuiltinMethod, + ty, + ty, + place.clone(), + address.clone(), + lifetime.clone(), + TODO_target_slice_len.clone(), + true, + position, + )?; + lowerer.unique_ref( + CallContext::BuiltinMethod, + ty, + ty, + place, + address, + lifetime.clone(), + TODO_target_slice_len, + None, + position, + ) + } + } + } + + fn maybe_store_range_snap_call<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + address: &vir_low::Expression, + ty: &vir_mid::Type, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + if let Some(current_state_label) = &self.current_state_label { + let entry = lowerer + .snapshots_state + .self_framing_assertion_encoder_state + .states + .entry(current_state_label.clone()) + .or_default(); + entry + .range_snap_calls + .push((address.clone(), (ty.clone(), position))); + } + Ok(()) + } + + fn get_range_snap_calls<'a, 'p, 'v, 'tcx>( + &'a self, + lowerer: &'a mut Lowerer<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult<&'a [(vir_low::Expression, (vir_mid::Type, vir_low::Position))]> + { + if let Some(old_label) = &self.old_label { + let entry = lowerer + .snapshots_state + .self_framing_assertion_encoder_state + .states + .get(old_label) + .unwrap(); + Ok(&entry.range_snap_calls) + } else { + Ok(&self.range_snap_calls) + } + } + + fn maybe_store_snap_call<'p, 'v, 'tcx>( + &self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + place: &vir_mid::Expression, + snap_call: &vir_low::Expression, + ) -> SpannedEncodingResult<()> { + if let Some(current_state_label) = &self.current_state_label { + let entry = lowerer + .snapshots_state + .self_framing_assertion_encoder_state + .states + .entry(current_state_label.clone()) + .or_default(); + entry.snap_calls.push((place.clone(), snap_call.clone())); + } + Ok(()) + } +} + +impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for SelfFramingAssertionToSnapshot { + fn expression_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + expression: &vir_mid::Expression, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + for (place, call) in &self.snap_calls { + if place == expression { + return Ok(call.clone()); + } + } + self.expression_to_snapshot_impl(lowerer, expression, expect_math_bool) + } + + fn binary_op_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + op: &vir_mid::BinaryOp, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let mut introduced_snap = false; + let mut introduced_range_snap = false; + if op.op_kind == vir_mid::BinaryOpKind::And { + if let box vir_mid::Expression::AccPredicate(expression) = &op.left { + if expression.predicate.is_owned_non_aliased() { + // The recursive call to `acc_predicate_to_snapshot` will + // add a snap call to `self.snap_calls`. + introduced_snap = true; + } + if expression.predicate.is_owned_range() { + // The recursive call to `acc_predicate_to_snapshot` will + // add a snap call to `self.range_snap_calls`. + introduced_range_snap = true; + } + } + } + let expression = self.binary_op_to_snapshot_impl(lowerer, op, expect_math_bool)?; + if introduced_snap { + self.snap_calls.pop(); + } + if introduced_range_snap { + self.range_snap_calls.pop(); + } + Ok(expression) + } + + fn field_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + field: &vir_mid::Field, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + match &*field.base { + vir_mid::Expression::Local(local) if self.caller_kind.is_predicate_body() => { + assert!(local.variable.is_self_variable()); + let field_place = lowerer.encode_field_place( + &local.variable.ty, + &field.field, + self.predicate_place(), + field.position, + )?; + let field_address = lowerer.encode_field_address( + &local.variable.ty, + &field.field, + self.predicate_address(), + field.position, + )?; + self.snap_call( + lowerer, + &field.field.ty, + field_place, + field_address, + local.position, + ) + } + vir_mid::Expression::Local(local) if self.caller_kind.is_assign_precondition() => { + // FIXME: these assertions may be wrong. + assert!(local.variable.is_self_variable()); + let CallerKind::AssignPrecondition { ref field_replacement_map, .. } = self.caller_kind else { + unreachable!() + }; + assert!(field_replacement_map.contains_key(&field.field)); + Ok(field_replacement_map[&field.field].clone()) + } + _ => self.field_to_snapshot_impl(lowerer, field, expect_math_bool), + } + } + + fn variable_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + variable: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult { + assert!( + !self.caller_kind.is_assign_precondition(), + "all variables should be replaced by arguments; got: {variable}" + ); + assert!( + !self.caller_kind.is_predicate_body() || variable.is_self_variable(), + "{variable} must be self" + ); + if self.use_ssa && !self.bound_variable_stack.contains(variable) { + if let Some(label) = &self.old_label { + lowerer.snapshot_variable_version_at_label(variable, label) + } else { + lowerer.current_snapshot_variable_version(variable) + } + } else { + Ok(vir_low::VariableDecl { + name: variable.name.clone(), + ty: self.type_to_snapshot(lowerer, &variable.ty)?, + }) + } + } + + fn labelled_old_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + old: &vir_mid::LabelledOld, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let parent_old_label = std::mem::replace(&mut self.old_label, Some(old.label.clone())); + let result = self.expression_to_snapshot(lowerer, &old.base, expect_math_bool)?; + self.old_label = parent_old_label; + Ok(vir_low::Expression::labelled_old( + Some(old.label.clone()), + result, + old.position, + )) + } + + fn func_app_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + app: &vir_mid::FuncApp, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let arguments = + self.expression_vec_to_snapshot(lowerer, &app.arguments, expect_math_bool)?; + let return_type = self.type_to_snapshot(lowerer, &app.return_type)?; + let func_app = lowerer.call_pure_function_in_procedure_context( + app.get_identifier(), + arguments, + return_type, + app.position, + )?; + let result = vir_low::Expression::FuncApp(func_app); + self.ensure_bool_expression(lowerer, &app.return_type, result, expect_math_bool) + } + + fn acc_predicate_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + acc_predicate: &vir_mid::AccPredicate, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + assert!(expect_math_bool); + assert!( + lowerer + .check_mode + .unwrap() + .supports_accessibility_predicates_in_assertions() + || matches!(self.caller_kind, CallerKind::PredicateBody { .. }) + ); + // assert_ne!(self.caller_kind, CallerKind::PlaceExpression, "unimplemented: report a proper error message"); + let expression = match &*acc_predicate.predicate { + vir_mid::Predicate::OwnedNonAliased(predicate) => { + let ty = predicate.place.get_type(); + let place = lowerer.encode_expression_as_place(&predicate.place)?; + let address = self.pointer_deref_into_address(lowerer, &predicate.place)?; + let acc = self.predicate( + lowerer, + ty, + place.clone(), + address.clone(), + predicate.position, + )?; + let snap_call = + self.snap_call(lowerer, ty, place, address, predicate.place.position())?; + self.maybe_store_snap_call(lowerer, &predicate.place, &snap_call)?; + self.snap_calls.push((predicate.place.clone(), snap_call)); + acc + } + vir_mid::Predicate::OwnedRange(predicate) => { + let ty = predicate.address.get_type(); + let address = self.expression_to_snapshot(lowerer, &predicate.address, true)?; + let start_index = + self.expression_to_snapshot(lowerer, &predicate.start_index, true)?; + let end_index = self.expression_to_snapshot(lowerer, &predicate.end_index, true)?; + self.range_snap_calls + .push((address.clone(), (ty.clone(), predicate.position))); + self.maybe_store_range_snap_call(lowerer, &address, ty, predicate.position)?; + let vir_mid::Type::Pointer(pointer_type) = ty else { + unreachable!(); + }; + self.created_predicate_types + .push((*pointer_type.target_type).clone()); + lowerer.owned_aliased_range( + CallContext::Procedure, + ty, + ty, + address, + start_index, + end_index, + None, + predicate.position, + )? + } + vir_mid::Predicate::MemoryBlockHeap(predicate) => { + match self.predicate_kind { + PredicateKind::Owned => { + let address = + self.pointer_deref_into_address(lowerer, &predicate.address)?; + // let place = lowerer.encode_expression_as_place(&predicate.address)?; + // let address = + // self.pointer_deref_into_address(lowerer, &predicate.address)?; + // use vir_low::macros::*; + // let compute_address = ty!(Address); + // let address = expr! { + // ComputeAddress::compute_address([place], [address]) + // }; + let size = self.expression_to_snapshot( + lowerer, + &predicate.size, + expect_math_bool, + )?; + lowerer.encode_memory_block_stack_acc( + address, + size, + acc_predicate.position, + )? + } + PredicateKind::FracRef { .. } | PredicateKind::UniqueRef { .. } => { + // Memory blocks are not accessible in frac/unique ref predicates. + true.into() + } + } + } + vir_mid::Predicate::MemoryBlockHeapDrop(predicate) => { + match self.predicate_kind { + PredicateKind::Owned => { + // FIXME: Why this does not match the encoding of MemoryBlockHeap? + let address = + self.pointer_deref_into_address(lowerer, &predicate.address)?; + let size = self.expression_to_snapshot( + lowerer, + &predicate.size, + expect_math_bool, + )?; + lowerer.encode_memory_block_heap_drop_acc( + address, + size, + acc_predicate.position, + )? + } + PredicateKind::FracRef { .. } | PredicateKind::UniqueRef { .. } => { + // Memory blocks are not accessible in frac/unique ref predicates. + true.into() + } + } + } + vir_mid::Predicate::MemoryBlockHeapRange(predicate) => { + let pointer_value = + self.expression_to_snapshot(lowerer, &predicate.address, true)?; + let address = lowerer.pointer_address( + predicate.address.get_type(), + pointer_value, + predicate.position, + )?; + let size = self.expression_to_snapshot(lowerer, &predicate.size, true)?; + let start_index = + self.expression_to_snapshot(lowerer, &predicate.start_index, true)?; + let end_index = self.expression_to_snapshot(lowerer, &predicate.end_index, true)?; + lowerer.encode_memory_block_range_acc( + address, + size, + start_index, + end_index, + acc_predicate.position, + )? + } + _ => unimplemented!("{acc_predicate}"), + }; + if self.is_target_pure { + Ok(true.into()) + } else { + Ok(expression) + } + } + + fn deref_own( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + pointer_type: &vir_mid::Type, + pointer: vir_low::Expression, + index: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + // FIXME: improve error reporting by showing which permission was used + // by linking to predicate_position. + let pointer = pointer.remove_unnecessary_old(); + let Some((_, (_todo_remove_ty, _predicate_position))) = self.get_range_snap_calls(lowerer)?.iter().find(|(range_pointer, _)| { + range_pointer == &pointer + }) else { + unimplemented!("Report a proper error message about not syntactically framed deref_own"); + }; + // let address = lowerer.pointer_address( + // pointer_type, + // pointer, + // position, + // )?; + let vir_mid::Type::Pointer(ty) = pointer_type else { + unreachable!() + }; + let size_type = lowerer.size_type_mid()?; + let index_int = lowerer.obtain_constant_value(&size_type, index, position)?; + let element_address = + lowerer.encode_index_address(pointer_type, pointer, index_int, position)?; + let result = lowerer.owned_aliased_snap( + CallContext::BuiltinMethod, + &ty.target_type, + &*ty.target_type, + element_address, + position, + )?; + Ok(result) + } + + // FIXME: Code duplication. + fn pointer_deref_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + deref: &vir_mid::Deref, + _base_snapshot: vir_low::Expression, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + let span = lowerer + .encoder + .error_manager() + .position_manager() + .get_span(deref.position.into()) + .cloned() + .unwrap(); + return Err(SpannedEncodingError::incorrect( + "the place must be syntactically framed by permissions", + span, + )); + // TODO: outdated code, delete. Return true for now because we expect + // the result to not be used. + // unimplemented!("pointer_deref_to_snapshot: {deref} {base_snapshot}"); + // Ok(true.into()) + // let heap = self + // .unsafe_cell_values + // .clone() + // .expect("This function should be reachable only when heap is Some"); + // lowerer.pointer_target_snapshot_in_heap( + // deref.base.get_type(), + // heap, + // base_snapshot, + // deref.position, + // ) + } + + fn unfolding_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + unfolding: &vir_mid::Unfolding, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + // FIXME: Replace all unfolding expressions with snap function calls. + // Currently, we just ignore all unfolding expressions. + self.expression_to_snapshot(lowerer, &unfolding.body, expect_math_bool) + } + + fn eval_in_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + eval_in: &vir_mid::EvalIn, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let box vir_mid::Expression::AccPredicate(predicate) = &eval_in.predicate else { + unimplemented!("A proper error message that this must be a predicate: {}", eval_in.predicate); + }; + let result = match &*predicate.predicate { + vir_mid::Predicate::OwnedNonAliased(predicate) => { + let ty = predicate.place.get_type(); + let place = lowerer.encode_expression_as_place(&predicate.place)?; + let address = self.pointer_deref_into_address(lowerer, &predicate.place)?; + let snap_call = + self.snap_call(lowerer, ty, place, address, predicate.place.position())?; + self.snap_calls.push((predicate.place.clone(), snap_call)); + let result = + self.expression_to_snapshot(lowerer, &eval_in.body, expect_math_bool)?; + self.snap_calls.pop(); + result + } + vir_mid::Predicate::OwnedRange(predicate) => { + let ty = predicate.address.get_type(); + let address = self.expression_to_snapshot(lowerer, &predicate.address, true)?; + self.range_snap_calls + .push((address, (ty.clone(), predicate.position))); + let result = + self.expression_to_snapshot(lowerer, &eval_in.body, expect_math_bool)?; + self.range_snap_calls.pop(); + result + } + _ => unimplemented!( + "A proper error message that this must be an owned predicate: {predicate}" + ), + }; + Ok(result) + } + + fn call_context(&self) -> CallContext { + unimplemented!("FIXME: unused? Delete?") + // match self.caller_kind { + // CallerKind::PredicateBody { .. } | + // CallerKind::AssignPrecondition { ..} => { + // CallContext::BuiltinMethod + // }, + // CallerKind::InhaleExhale => { + // CallContext::Procedure + // }, + // } + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + todo!() + } + + fn push_bound_variables( + &mut self, + variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + self.bound_variable_stack.push(variables); + Ok(()) + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + self.bound_variable_stack.pop(); + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/snap.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/snap.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/snap.rs @@ -0,0 +1 @@ + diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/validity.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/validity.rs new file mode 100644 index 00000000000..f76f4bce2c4 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/assertions/validity.rs @@ -0,0 +1,183 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + builtin_methods::CallContext, + footprint::{DerefFields, DerefOwned, DerefOwnedRange}, + lowerer::Lowerer, + snapshots::{IntoSnapshotLowerer, SnapshotValidityInterface}, + }, +}; +use std::collections::BTreeMap; +use vir_crate::{ + low::{self as vir_low}, + middle::{self as vir_mid, operations::ty::Typed}, +}; + +pub(in super::super::super::super::super) struct ValidityAssertionToSnapshot { + framed_places: Vec, + deref_fields: BTreeMap, + framed_range_addresses: Vec, + deref_range_fields: BTreeMap, +} + +impl ValidityAssertionToSnapshot { + pub(in super::super::super::super) fn new( + (deref_fields, deref_range_fields): DerefFields, + ) -> Self { + Self { + framed_places: Vec::new(), + deref_fields: deref_fields + .into_iter() + .map( + |DerefOwned { + place, + field_name, + field_type, + }| { + (place, vir_low::VariableDecl::new(field_name, field_type)) + }, + ) + .collect(), + framed_range_addresses: Vec::new(), + deref_range_fields: deref_range_fields + .into_iter() + .map( + |DerefOwnedRange { + address, + field_name, + field_type, + }| { + (address, vir_low::VariableDecl::new(field_name, field_type)) + }, + ) + .collect(), + } + } +} + +impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for ValidityAssertionToSnapshot { + fn expression_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + expression: &vir_mid::Expression, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + if let Some(field) = self.deref_fields.get(expression) { + // for framed_place in &self.framed_places { + // eprintln!("Framed: {framed_place}"); + // } + assert!( + self.framed_places.contains(expression), + "The place {expression} must be framed" + ); + Ok(field.clone().into()) + } else { + self.expression_to_snapshot_impl(lowerer, expression, expect_math_bool) + } + } + + fn variable_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + variable: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult { + assert!(variable.is_self_variable(), "{variable} must be self"); + Ok(vir_low::VariableDecl { + name: variable.name.clone(), + ty: self.type_to_snapshot(lowerer, &variable.ty)?, + }) + } + + fn labelled_old_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _old: &vir_mid::LabelledOld, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn func_app_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _app: &vir_mid::FuncApp, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn binary_op_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + op: &vir_mid::BinaryOp, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let mut introduced_snap = false; + if op.op_kind == vir_mid::BinaryOpKind::And { + if let box vir_mid::Expression::AccPredicate(expression) = &op.left { + if let vir_mid::Predicate::OwnedNonAliased(predicate) = &*expression.predicate { + self.framed_places.push(predicate.place.clone()); + introduced_snap = true; + } + } + } + let expression = self.binary_op_to_snapshot_impl(lowerer, op, expect_math_bool)?; + if introduced_snap { + self.framed_places.pop(); + } + Ok(expression) + } + + fn acc_predicate_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + acc_predicate: &vir_mid::AccPredicate, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + assert!(expect_math_bool); + let expression = match &*acc_predicate.predicate { + vir_mid::Predicate::OwnedNonAliased(predicate) => { + self.framed_places.push(predicate.place.clone()); + let place = self.expression_to_snapshot(lowerer, &predicate.place, false)?; + self.framed_places.pop(); + lowerer.encode_snapshot_valid_call_for_type(place, predicate.place.get_type())? + } + vir_mid::Predicate::OwnedRange(predicate) => { + self.framed_range_addresses.push(predicate.address.clone()); + let address = self.expression_to_snapshot(lowerer, &predicate.address, false)?; + self.framed_range_addresses.pop(); + lowerer + .encode_snapshot_valid_call_for_type(address, predicate.address.get_type())? + } + vir_mid::Predicate::MemoryBlockHeap(_) + | vir_mid::Predicate::MemoryBlockHeapRange(_) + | vir_mid::Predicate::MemoryBlockHeapDrop(_) => true.into(), + _ => unimplemented!("{acc_predicate}"), + }; + Ok(expression) + } + + fn call_context(&self) -> CallContext { + todo!() + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + todo!() + } + + fn push_bound_variables( + &mut self, + _variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + todo!() + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + todo!() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/builtin_methods/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/builtin_methods/mod.rs index 21ff7d3ab21..e7a4513f16c 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/builtin_methods/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/builtin_methods/mod.rs @@ -1,7 +1,10 @@ use super::common::IntoSnapshotLowerer; use crate::encoder::{ errors::SpannedEncodingResult, - middle::core_proof::lowerer::{FunctionsLowererInterface, Lowerer}, + middle::core_proof::{ + builtin_methods::CallContext, + lowerer::{FunctionsLowererInterface, Lowerer}, + }, }; use vir_crate::{ common::identifier::WithIdentifier, @@ -56,4 +59,46 @@ impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for BuiltinMethodSn // In pure contexts values cannot be mutated, so `old` has no effect. self.expression_to_snapshot(lowerer, &old.base, expect_math_bool) } + + fn acc_predicate_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _predicate: &vir_mid::AccPredicate, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + unreachable!() + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_snapshot: &vir_mid::Expression, + ) -> SpannedEncodingResult { + unimplemented!() + } + + fn call_context(&self) -> CallContext { + CallContext::BuiltinMethod + } + + fn push_bound_variables( + &mut self, + _variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + todo!() + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + todo!() + } + + // fn unfolding_to_snapshot( + // &mut self, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // unfolding: &vir_mid::Unfolding, + // expect_math_bool: bool, + // ) -> SpannedEncodingResult { + // todo!() + // } } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/common/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/common/mod.rs index f88ff2b7ac6..4813507e996 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/common/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/common/mod.rs @@ -3,20 +3,35 @@ use crate::encoder::{ errors::SpannedEncodingResult, high::types::HighTypeEncoderInterface, middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, lifetimes::*, lowerer::DomainsLowererInterface, + places::PlacesInterface, + pointers::PointersInterface, + predicates::{PredicatesMemoryBlockInterface, PredicatesOwnedInterface}, references::ReferencesInterface, - snapshots::{IntoSnapshot, SnapshotDomainsInterface, SnapshotValuesInterface}, + snapshots::{ + IntoSnapshot, SnapshotDomainsInterface, SnapshotValidityInterface, + SnapshotValuesInterface, + }, + type_layouts::TypeLayoutsInterface, types::TypesInterface, }, }; use vir_crate::{ - common::{identifier::WithIdentifier, position::Positioned}, + common::{ + expression::{BinaryOperationHelpers, ExpressionIterator}, + identifier::WithIdentifier, + position::Positioned, + }, low::{self as vir_low}, middle::{self as vir_mid, operations::ty::Typed}, }; -pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { +pub(in super::super::super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v>: + Sized +{ fn expression_vec_to_snapshot( &mut self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, @@ -38,6 +53,15 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { lowerer: &mut Lowerer<'p, 'v, 'tcx>, expression: &vir_mid::Expression, expect_math_bool: bool, + ) -> SpannedEncodingResult { + self.expression_to_snapshot_impl(lowerer, expression, expect_math_bool) + } + + fn expression_to_snapshot_impl( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + expression: &vir_mid::Expression, + expect_math_bool: bool, ) -> SpannedEncodingResult { match expression { vir_mid::Expression::Local(expression) => { @@ -55,6 +79,9 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { vir_mid::Expression::Deref(expression) => { self.deref_to_snapshot(lowerer, expression, expect_math_bool) } + vir_mid::Expression::Final(expression) => { + self.final_to_snapshot(lowerer, expression, expect_math_bool) + } vir_mid::Expression::AddrOf(expression) => { self.addr_of_to_snapshot(lowerer, expression, expect_math_bool) } @@ -75,7 +102,9 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { vir_mid::Expression::Conditional(expression) => { self.conditional_to_snapshot(lowerer, expression, expect_math_bool) } - // vir_mid::Expression::Quantifier(expression) => self.quantifier_to_snapshot(lowerer, expression, expect_math_bool), + vir_mid::Expression::Quantifier(expression) => { + self.quantifier_to_snapshot(lowerer, expression, expect_math_bool) + } // vir_mid::Expression::LetExpr(expression) => self.letexpr_to_snapshot(lowerer, expression, expect_math_bool), vir_mid::Expression::FuncApp(expression) => { self.func_app_to_snapshot(lowerer, expression, expect_math_bool) @@ -84,6 +113,15 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { self.builtin_func_app_to_snapshot(lowerer, expression, expect_math_bool) } // vir_mid::Expression::Downcast(expression) => self.downcast_to_snapshot(lowerer, expression, expect_math_bool), + vir_mid::Expression::AccPredicate(expression) => { + self.acc_predicate_to_snapshot(lowerer, expression, expect_math_bool) + } + vir_mid::Expression::Unfolding(expression) => { + self.unfolding_to_snapshot(lowerer, expression, expect_math_bool) + } + vir_mid::Expression::EvalIn(expression) => { + self.eval_in_to_snapshot(lowerer, expression, expect_math_bool) + } x => unimplemented!("{:?}", x), } } @@ -106,7 +144,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { fn variable_to_snapshot( &mut self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, - local: &vir_mid::VariableDecl, + variable: &vir_mid::VariableDecl, ) -> SpannedEncodingResult; fn local_to_snapshot( @@ -131,7 +169,21 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { for argument in &constructor.arguments { arguments.push(self.expression_to_snapshot(lowerer, argument, false)?); } - lowerer.construct_struct_snapshot(&constructor.ty, arguments, constructor.position) + let struct_snapshot = + lowerer.construct_struct_snapshot(&constructor.ty, arguments, constructor.position)?; + if let vir_mid::Type::Enum(vir_mid::ty::Enum { + variant: Some(_), .. + }) = &constructor.ty + { + let enum_snapshot = lowerer.construct_enum_snapshot( + &constructor.ty, + struct_snapshot, + constructor.position, + )?; + Ok(enum_snapshot) + } else { + Ok(struct_snapshot) + } } fn variant_to_snapshot( @@ -155,6 +207,15 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { lowerer: &mut Lowerer<'p, 'v, 'tcx>, field: &vir_mid::Field, expect_math_bool: bool, + ) -> SpannedEncodingResult { + self.field_to_snapshot_impl(lowerer, field, expect_math_bool) + } + + fn field_to_snapshot_impl( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + field: &vir_mid::Field, + expect_math_bool: bool, ) -> SpannedEncodingResult { let base_snapshot = self.expression_to_snapshot(lowerer, &field.base, expect_math_bool)?; let result = if field.field.is_discriminant() { @@ -187,14 +248,41 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { expect_math_bool: bool, ) -> SpannedEncodingResult { let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, expect_math_bool)?; - let result = lowerer.reference_target_current_snapshot( - deref.base.get_type(), - base_snapshot, - Default::default(), - )?; + let ty = deref.base.get_type(); + let result = if ty.is_reference() { + lowerer.reference_target_current_snapshot(ty, base_snapshot, deref.position)? + } else { + self.pointer_deref_to_snapshot(lowerer, deref, base_snapshot, expect_math_bool)? + }; + self.ensure_bool_expression(lowerer, deref.get_type(), result, expect_math_bool) + } + + fn final_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + deref: &vir_mid::Final, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, expect_math_bool)?; + let ty = deref.base.get_type(); + let result = if ty.is_reference() { + lowerer.reference_target_final_snapshot(ty, base_snapshot, deref.position)? + } else { + unreachable!("Final deref is not supported for non-reference types.") + }; self.ensure_bool_expression(lowerer, deref.get_type(), result, expect_math_bool) } + fn pointer_deref_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _deref: &vir_mid::Deref, + _base_snapshot: vir_low::Expression, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + unreachable!("Should be overriden."); + } + fn addr_of_to_snapshot( &mut self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, @@ -211,7 +299,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { Default::default(), )? } - _ => unimplemented!("ty: {}", addr_of.ty), + _ => unimplemented!("addr_of: {addr_of}\n{addr_of:?}\n ty: {}", addr_of.ty), }; self.ensure_bool_expression(lowerer, &addr_of.ty, result, expect_math_bool) } @@ -316,6 +404,15 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { lowerer: &mut Lowerer<'p, 'v, 'tcx>, op: &vir_mid::BinaryOp, expect_math_bool: bool, + ) -> SpannedEncodingResult { + self.binary_op_to_snapshot_impl(lowerer, op, expect_math_bool) + } + + fn binary_op_to_snapshot_impl( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + op: &vir_mid::BinaryOp, + expect_math_bool: bool, ) -> SpannedEncodingResult { // FIXME: Binary Operations with MPerm should not be handled manually as special cases // They are difficult because binary operations with MPerm and Integer values are allowed. @@ -376,15 +473,26 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { self.expression_to_snapshot(lowerer, &op.right, expect_math_bool_args)?; let arg_type = op.left.get_type().clone().erase_lifetimes(); assert_eq!(arg_type, op.right.get_type().clone().erase_lifetimes()); - let result = lowerer.construct_binary_op_snapshot( - op.op_kind, - ty, - &arg_type, - left_snapshot, - right_snapshot, - op.position, - )?; - self.ensure_bool_expression(lowerer, ty, result, expect_math_bool) + if expect_math_bool && op.op_kind == vir_mid::BinaryOpKind::EqCmp { + // FIXME: Instead of this ad-hoc optimization, have a proper + // optimization pass. + Ok(vir_low::Expression::binary_op( + vir_low::BinaryOpKind::EqCmp, + left_snapshot, + right_snapshot, + op.position, + )) + } else { + let result = lowerer.construct_binary_op_snapshot( + op.op_kind, + ty, + &arg_type, + left_snapshot, + right_snapshot, + op.position, + )?; + self.ensure_bool_expression(lowerer, ty, result, expect_math_bool) + } } fn binary_op_kind_to_snapshot( @@ -422,8 +530,11 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { self.expression_to_snapshot(lowerer, &conditional.then_expr, expect_math_bool)?; let else_expr_snapshot = self.expression_to_snapshot(lowerer, &conditional.else_expr, expect_math_bool)?; - let arg_type = conditional.then_expr.get_type(); - assert_eq!(arg_type, conditional.else_expr.get_type()); + let arg_type = vir_low::operations::ty::Typed::get_type(&then_expr_snapshot); + assert_eq!( + arg_type, + vir_low::operations::ty::Typed::get_type(&else_expr_snapshot) + ); // We do not need to ensure expect_math_bool because we pushed this // responsibility to the arguments. Ok(vir_low::Expression::conditional( @@ -434,6 +545,75 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { )) } + fn quantifier_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + quantifier: &vir_mid::Quantifier, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + assert!(expect_math_bool); + let quantifier_kind = match quantifier.kind { + vir_mid::expression::QuantifierKind::ForAll => { + vir_low::expression::QuantifierKind::ForAll + } + vir_mid::expression::QuantifierKind::Exists => { + vir_low::expression::QuantifierKind::Exists + } + }; + self.push_bound_variables(&quantifier.variables)?; + let body_snapshot = self.expression_to_snapshot(lowerer, &quantifier.body, true)?; + let mut variables = Vec::new(); + let mut variable_validity = Vec::new(); + for variable_mid in &quantifier.variables { + let variable = vir_low::VariableDecl::new( + variable_mid.name.clone(), + self.type_to_snapshot(lowerer, &variable_mid.ty)?, + ); + variables.push(variable.clone()); + let validity = + lowerer.encode_snapshot_valid_call_for_type(variable.into(), &variable_mid.ty)?; + variable_validity.push(validity); + } + let variable_validity = variable_validity.into_iter().conjoin(); + let body = match quantifier.kind { + vir_mid::expression::QuantifierKind::ForAll => { + vir_low::Expression::implies(variable_validity, body_snapshot) + } + vir_mid::expression::QuantifierKind::Exists => { + vir_low::Expression::and(variable_validity, body_snapshot) + } + }; + let triggers = quantifier + .triggers + .iter() + .map(|trigger| { + trigger + .terms + .iter() + .map(|expr| self.expression_to_snapshot(lowerer, expr, true)) + .collect::>>() + .map(vir_low::Trigger::new) + }) + .collect::>>()?; + let result = vir_low::Expression::quantifier( + quantifier_kind, + variables, + triggers, + body, + quantifier.position, + ); + self.pop_bound_variables()?; + // self.ensure_bool_expression(lowerer, &vir_mid::Type::Bool, result, expect_math_bool) + Ok(result) + } + + fn push_bound_variables( + &mut self, + variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()>; + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()>; + fn func_app_to_snapshot( &mut self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, @@ -444,7 +624,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { fn builtin_func_app_to_snapshot( &mut self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, - app: &vir_crate::middle::expression::BuiltinFuncApp, + app: &vir_mid::BuiltinFuncApp, expect_math_bool: bool, ) -> SpannedEncodingResult { use vir_low::expression::ContainerOpKind; @@ -455,26 +635,29 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { .iter() .map(|ty| self.type_to_snapshot(lowerer, ty)) .collect::, _>>()?; - let mut args = - self.expression_vec_to_snapshot(lowerer, &app.arguments, expect_math_bool)?; - if !app.arguments.is_empty() { - let first_arg_type = app.arguments[0].get_type(); - if first_arg_type.is_reference() - && app.function != vir_mid::BuiltinFunc::SnapshotEquality - { - // The first argument is a reference, dereference it. - args[0] = lowerer.reference_target_current_snapshot( - first_arg_type, - args[0].clone(), - app.position, - )?; + let construct_args = |this: &mut Self, lowerer: &mut _| -> Result<_, _> { + let mut args = + this.expression_vec_to_snapshot(lowerer, &app.arguments, expect_math_bool)?; + if !app.arguments.is_empty() { + let first_arg_type = app.arguments[0].get_type(); + if first_arg_type.is_reference() + && app.function != vir_mid::BuiltinFunc::SnapshotEquality + { + // The first argument is a reference, dereference it. + args[0] = lowerer.reference_target_current_snapshot( + first_arg_type, + args[0].clone(), + app.position, + )?; + } } - } + Ok(args) + }; lowerer.ensure_type_definition(&app.return_type)?; - let map = |low_kind| { + let map = |this, lowerer, low_kind| { let map_ty = vir_low::Type::map(ty_args[0].clone(), ty_args[1].clone()); - let args = args.clone(); + let mut args = construct_args(this, lowerer)?; Ok(vir_low::Expression::container_op( low_kind, map_ty, @@ -483,11 +666,12 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { )) }; - let seq = |low_kind| { + let seq = |this, lowerer, low_kind| { + let mut args = construct_args(this, lowerer)?; Ok(vir_low::Expression::container_op( low_kind, vir_low::Type::seq(ty_args[0].clone()), - args.clone(), + args, app.position, )) }; @@ -495,6 +679,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { match app.function { BuiltinFunc::Size => { let return_type = self.type_to_snapshot(lowerer, &app.return_type)?; + let mut args = construct_args(self, lowerer)?; lowerer.create_domain_func_app( "Size", app.get_identifier(), @@ -504,6 +689,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { ) } BuiltinFunc::PaddingSize => { + let mut args = construct_args(self, lowerer)?; assert_eq!(args.len(), 0); let return_type = self.type_to_snapshot(lowerer, &app.return_type)?; lowerer.create_domain_func_app( @@ -514,7 +700,20 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { app.position, ) } + BuiltinFunc::Align => { + let mut args = construct_args(self, lowerer)?; + assert_eq!(args.len(), 0); + let return_type = self.type_to_snapshot(lowerer, &app.return_type)?; + lowerer.create_domain_func_app( + "Align", + app.get_identifier(), + args, + return_type, + app.position, + ) + } BuiltinFunc::Discriminant => { + let mut args = construct_args(self, lowerer)?; assert_eq!(args.len(), 1); let discriminant_call = lowerer.obtain_enum_discriminant( args.pop().unwrap(), @@ -527,10 +726,10 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { app.position, ) } - BuiltinFunc::EmptyMap => map(ContainerOpKind::MapEmpty), - BuiltinFunc::UpdateMap => map(ContainerOpKind::MapUpdate), + BuiltinFunc::EmptyMap => map(self, lowerer, ContainerOpKind::MapEmpty), + BuiltinFunc::UpdateMap => map(self, lowerer, ContainerOpKind::MapUpdate), BuiltinFunc::LookupMap => { - let value = map(ContainerOpKind::MapLookup)?; + let value = map(self, lowerer, ContainerOpKind::MapLookup)?; if app.return_type.is_reference() { lowerer.shared_non_alloc_reference_snapshot_constructor( &app.return_type, @@ -542,16 +741,17 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { } } BuiltinFunc::MapLen => { - let value = map(ContainerOpKind::MapLen)?; + let value = map(self, lowerer, ContainerOpKind::MapLen)?; lowerer.construct_constant_snapshot(app.get_type(), value, app.position) } BuiltinFunc::MapContains => { - let m = map(ContainerOpKind::MapContains)?; + let m = map(self, lowerer, ContainerOpKind::MapContains)?; let m = lowerer.construct_constant_snapshot(app.get_type(), m, app.position)?; self.ensure_bool_expression(lowerer, app.get_type(), m, expect_math_bool) } BuiltinFunc::LookupSeq => { use vir_low::operations::ty::Typed; + let mut args = construct_args(self, lowerer)?; assert!( args[0].get_type().is_seq(), "Expected Sequence type, got {:?}", @@ -580,12 +780,13 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { Ok(value) } } - BuiltinFunc::ConcatSeq => seq(ContainerOpKind::SeqConcat), + BuiltinFunc::ConcatSeq => seq(self, lowerer, ContainerOpKind::SeqConcat), BuiltinFunc::SeqLen => { - let value = seq(ContainerOpKind::SeqLen)?; + let value = seq(self, lowerer, ContainerOpKind::SeqLen)?; lowerer.construct_constant_snapshot(app.get_type(), value, app.position) } BuiltinFunc::LifetimeIncluded => { + let mut args = construct_args(self, lowerer)?; assert_eq!(args.len(), 2); lowerer.encode_lifetime_included()?; Ok(vir_low::Expression::domain_function_call( @@ -596,6 +797,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { )) } BuiltinFunc::LifetimeIntersect => { + let mut args = construct_args(self, lowerer)?; assert!(!args.is_empty()); // FIXME: Fix code duplication. let lifetime_set_type = lowerer.lifetime_set_type()?; @@ -615,6 +817,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { Ok(intersect) } BuiltinFunc::EmptySeq | BuiltinFunc::SingleSeq => { + let mut args = construct_args(self, lowerer)?; Ok(vir_low::Expression::container_op( vir_low::ContainerOpKind::SeqConstructor, vir_low::Type::seq(ty_args[0].clone()), @@ -623,6 +826,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { )) } BuiltinFunc::NewInt => { + let mut args = construct_args(self, lowerer)?; assert_eq!(args.len(), 1); let arg = args.pop().unwrap(); let value = lowerer.obtain_constant_value( @@ -633,6 +837,7 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { lowerer.construct_constant_snapshot(app.get_type(), value, app.position) } BuiltinFunc::Index => { + let mut args = construct_args(self, lowerer)?; assert_eq!(args.len(), 2); // FIXME: Remove duplication with LookupSeq. let index = lowerer.obtain_constant_value( @@ -648,13 +853,14 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { )) } BuiltinFunc::Len => { - assert_eq!(args.len(), 1); + assert_eq!(app.arguments.len(), 1); // FIXME: Remove duplication with SeqLen. - let value = seq(ContainerOpKind::SeqLen)?; + let value = seq(self, lowerer, ContainerOpKind::SeqLen)?; lowerer.construct_constant_snapshot(app.get_type(), value, app.position) } BuiltinFunc::SnapshotEquality => { assert_eq!(app.arguments[0].get_type(), app.arguments[1].get_type()); + let mut args = construct_args(self, lowerer)?; let value = vir_low::Expression::binary_op( vir_low::BinaryOpKind::EqCmp, args[0].clone(), @@ -667,9 +873,275 @@ pub(super) trait IntoSnapshotLowerer<'p, 'v: 'p, 'tcx: 'v> { lowerer.construct_constant_snapshot(&vir_mid::Type::Bool, value, app.position) } } + BuiltinFunc::PtrIsNull => { + let mut args = construct_args(self, lowerer)?; + assert_eq!(args.len(), 1); + let ty = app.arguments[0].get_type(); + let address = lowerer.pointer_address(ty, args[0].clone(), app.position)?; + let null_address = lowerer.address_null(app.position)?; + let equals = vir_low::Expression::equals(address, null_address); + let equals = + lowerer.construct_constant_snapshot(app.get_type(), equals, app.position)?; + self.ensure_bool_expression(lowerer, app.get_type(), equals, expect_math_bool) + } + BuiltinFunc::PtrWrappingOffset => { + let mut args = construct_args(self, lowerer)?; + assert_eq!(args.len(), 2); + let ty = app.arguments[0].get_type(); + let address = lowerer.pointer_address(ty, args[0].clone(), app.position)?; + let vir_mid::Type::Pointer(pointer_type) = ty else { + unreachable!() + }; + let size = lowerer.encode_type_size_expression2( + &pointer_type.target_type, + &*pointer_type.target_type, + )?; + let offset = lowerer.obtain_constant_value( + app.arguments[1].get_type(), + args[1].clone(), + app.position, + )?; + let new_address = lowerer.address_offset(size, address, offset, app.position)?; + lowerer.address_to_pointer(ty, new_address, app.position) + } + BuiltinFunc::IsValid => { + let mut args = construct_args(self, lowerer)?; + assert_eq!(app.arguments.len(), 1); + let argument = args.pop().unwrap(); + let ty = app.arguments[0].get_type(); + lowerer.encode_snapshot_valid_call_for_type(argument, ty) + } + BuiltinFunc::EnsureOwnedPredicate => { + assert_eq!(app.arguments.len(), 1); + fn peel_unfolding<'p, 'v: 'p, 'tcx: 'v>( + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + into_snap_lowerer: &mut impl IntoSnapshotLowerer<'p, 'v, 'tcx>, + place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + match place { + vir_mid::Expression::Unfolding(unfolding) => { + let body = peel_unfolding(lowerer, into_snap_lowerer, &unfolding.body)?; + into_snap_lowerer.unfolding_to_snapshot_with_body( + lowerer, + &unfolding.predicate, + body, + unfolding.position, + true, + ) + } + _ => { + let ty = place.get_type(); + let snap_call = + into_snap_lowerer.owned_non_aliased_snap(lowerer, ty, place)?; + let snapshot = + into_snap_lowerer.expression_to_snapshot(lowerer, place, true)?; + let position = place.position(); + Ok(vir_low::Expression::binary_op( + vir_low::BinaryOpKind::EqCmp, + snap_call, + snapshot, + position, + )) + } + } + } + peel_unfolding(lowerer, self, &app.arguments[0]) + // let argument = &app.arguments[0]; + // let ty = argument.get_type(); + // let snap_call = self.owned_non_aliased_snap(lowerer, ty, argument)?; + // lowerer.wrap_snap_into_bool(ty, snap_call.set_default_position(app.position)) + } + BuiltinFunc::TakeLifetime => { + unimplemented!("TODO: Delete"); + } + BuiltinFunc::ReadByte => { + let mut args = construct_args(self, lowerer)?; + let index = args.pop().unwrap(); + let index_int = lowerer.obtain_constant_value( + app.arguments[1].get_type(), + index, + app.position, + )?; + let bytes = args.pop().unwrap(); + lowerer.encode_read_byte_expression_int(bytes, index_int, app.position) + } + BuiltinFunc::MemoryBlockBytes => { + assert_eq!(app.arguments.len(), 2); + // let mut args = construct_args(self, lowerer)?; + // let size = args.pop().unwrap(); + let size = + self.expression_to_snapshot(lowerer, &app.arguments[1], expect_math_bool)?; + // let pointer_value = args.pop().unwrap(); + // let address = lowerer.pointer_address( + // app.arguments[0].get_type(), + // pointer_value, + // app.position, + // )?; + let address = self.pointer_deref_into_address(lowerer, &app.arguments[0])?; + lowerer.encode_memory_block_bytes_expression(address, size) + } + BuiltinFunc::DerefOwn => { + let mut args = construct_args(self, lowerer)?; + let pointer_type = app.arguments[0].get_type(); + // let address = lowerer.pointer_address( + // pointer_type, + // args[0].clone(), + // app.position, + // )?; + let index = args.pop().unwrap(); + let pointer = args.pop().unwrap(); + self.deref_own(lowerer, pointer_type, pointer, index, app.position) + } + BuiltinFunc::CastMutToConstPointer => { + let mut args = construct_args(self, lowerer)?; + // We currently do not distinguish between mutable and immutable + // pointers so this is a no-op. + Ok(args.pop().unwrap()) + // let address = lowerer.pointer_address( + // app.arguments[0].get_type(), + // args[0].clone(), + // app.position, + // )?; + // lowerer.address_to_pointer(&app.return_type, address, app.position) + } + BuiltinFunc::BeforeExpiry => { + unreachable!("BeforeExpiry should be desugard before"); + } + BuiltinFunc::AfterExpiry => { + unreachable!("AfterExpiry should be desugard before"); + } } } + /// Deref a raw pointer with the specified offset. + fn deref_own( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _pointer_type: &vir_mid::Type, + _pointer: vir_low::Expression, + _index: vir_low::Expression, + _position: vir_low::Position, + ) -> SpannedEncodingResult { + unimplemented!(); + } + + // FIXME: Code duplication. + fn pointer_deref_into_address( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + if let Some(deref_place) = place.get_last_dereferenced_pointer() { + let base_snapshot = self.expression_to_snapshot(lowerer, deref_place, true)?; + let ty = deref_place.get_type(); + lowerer.pointer_address(ty, base_snapshot, place.position()) + } else { + unreachable!() + } + } + + fn acc_predicate_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + acc_predicate: &vir_mid::AccPredicate, + expect_math_bool: bool, + ) -> SpannedEncodingResult; + + // fn unfolding_to_snapshot( + // &mut self, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // unfolding: &vir_mid::Unfolding, + // expect_math_bool: bool, + // ) -> SpannedEncodingResult; + + fn call_context(&self) -> CallContext; + + fn unfolding_to_snapshot_with_body( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + predicate: &vir_mid::Predicate, + body: vir_low::Expression, + position: vir_low::Position, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + assert!(expect_math_bool, "not implemented"); + let predicate = match predicate { + vir_mid::Predicate::OwnedNonAliased(predicate) => { + let ty = predicate.place.get_type(); + lowerer.mark_owned_predicate_as_unfolded(ty)?; + let place = lowerer.encode_expression_as_place(&predicate.place)?; + let address = lowerer.encode_expression_as_place_address(&predicate.place)?; + // let root_address = lowerer.extract_root_address(&predicate.place)?; + // let snapshot = + // self.expression_to_snapshot(lowerer, &predicate.place, expect_math_bool)?; + // predicate.place.to_procedure_snapshot(lowerer)?; // FIXME: This is probably wrong. It should take into account the current old. + lowerer + .owned_non_aliased(self.call_context(), ty, ty, place, address, None, position)? + .unwrap_predicate_access_predicate() + } + _ => unimplemented!("{predicate}"), + }; + let expression = vir_low::Expression::unfolding(predicate, body, position); + Ok(expression) + } + + fn unfolding_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + unfolding: &vir_mid::Unfolding, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let body = self.expression_to_snapshot(lowerer, &unfolding.body, expect_math_bool)?; + self.unfolding_to_snapshot_with_body( + lowerer, + &unfolding.predicate, + body, + unfolding.position, + expect_math_bool, + ) + // let predicate = match &*unfolding.predicate { + // vir_mid::Predicate::OwnedNonAliased(predicate) => { + // let ty = predicate.place.get_type(); + // lowerer.mark_owned_predicate_as_unfolded(ty)?; + // let place = lowerer.encode_expression_as_place(&predicate.place)?; + // let root_address = lowerer.extract_root_address(&predicate.place)?; + // let snapshot = self.expression_to_snapshot(lowerer, &predicate.place, expect_math_bool)?; + // // predicate.place.to_procedure_snapshot(lowerer)?; // FIXME: This is probably wrong. It should take into account the current old. + // lowerer + // .owned_non_aliased( + // self.call_context(), + // ty, + // ty, + // place, + // root_address, + // snapshot, + // None, + // )? + // .unwrap_predicate_access_predicate() + // } + // _ => unimplemented!("{unfolding}"), + // }; + // let body = self.expression_to_snapshot(lowerer, &unfolding.body, expect_math_bool)?; + // let expression = vir_low::Expression::unfolding(predicate, body, unfolding.position); + // Ok(expression) + } + + fn eval_in_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _eval_in: &vir_mid::EvalIn, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + unimplemented!("FIXME: Make this abstract."); + } + + fn owned_non_aliased_snap( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ty: &vir_mid::Type, + pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult; + fn type_to_snapshot( &mut self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/context_independent/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/context_independent/mod.rs index 4b17e40976f..c0781310274 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/context_independent/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/context_independent/mod.rs @@ -2,7 +2,10 @@ //! the context. Currently, the only example is types. use super::common::IntoSnapshotLowerer; -use crate::encoder::{errors::SpannedEncodingResult, middle::core_proof::lowerer::Lowerer}; +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{builtin_methods::CallContext, lowerer::Lowerer}, +}; use vir_crate::{ low::{self as vir_low}, middle::{self as vir_mid}, @@ -41,4 +44,46 @@ impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for ContextIndepend ) -> SpannedEncodingResult { unreachable!("requested context dependent encoding"); } + + fn acc_predicate_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _predicate: &vir_mid::AccPredicate, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + unreachable!("requested context dependent encoding"); + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_snapshot: &vir_mid::Expression, + ) -> SpannedEncodingResult { + unimplemented!() + } + + // fn unfolding_to_snapshot( + // &mut self, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // unfolding: &vir_mid::Unfolding, + // expect_math_bool: bool, + // ) -> SpannedEncodingResult { + // todo!() + // } + + fn call_context(&self) -> CallContext { + todo!() + } + + fn push_bound_variables( + &mut self, + _variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + todo!() + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + todo!() + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/mod.rs new file mode 100644 index 00000000000..fa659df8eb2 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/mod.rs @@ -0,0 +1,10 @@ +/// Expressions that are framed and used in pure contexts. For example, pure +/// function bodies. +mod pure_framed; +/// Expressions to be used in procedure bodies. For example, arguments of +/// builtin methods. +mod procedure_bodies; + +pub(in super::super::super) use self::{ + procedure_bodies::PlaceToSnapshot, pure_framed::FramedExpressionToSnapshot, +}; diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/procedure_bodies.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/procedure_bodies.rs new file mode 100644 index 00000000000..e2bb56af111 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/procedure_bodies.rs @@ -0,0 +1,167 @@ +// FIXME: Rename the module. + +use super::super::PredicateKind; +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, + lowerer::Lowerer, + places::PlacesInterface, + predicates::PredicatesOwnedInterface, + snapshots::{IntoSnapshotLowerer, SnapshotVariablesInterface}, + }, +}; + +use vir_crate::{ + common::position::Positioned, + low::{self as vir_low}, + middle::{self as vir_mid, operations::ty::Typed}, +}; + +pub(in super::super::super::super::super) struct PlaceToSnapshot { + old_label: Option, + predicate_kind: PredicateKind, +} + +impl PlaceToSnapshot { + pub(in super::super::super::super) fn for_place(predicate_kind: PredicateKind) -> Self { + Self { + old_label: None, + predicate_kind, + } + } + + fn snap_call<'p, 'v, 'tcx>( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ty: &vir_mid::Type, + pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + let place = lowerer.encode_expression_as_place(pointer_place)?; + let address = lowerer.encode_expression_as_place_address(pointer_place)?; + match &self.predicate_kind { + PredicateKind::Owned => lowerer.owned_non_aliased_snap( + CallContext::Procedure, + ty, + ty, + place, + address, + pointer_place.position(), + ), + PredicateKind::FracRef { lifetime } => { + let TODO_target_slice_len = None; + lowerer.frac_ref_snap( + CallContext::Procedure, + ty, + ty, + place, + address, + lifetime.clone(), + TODO_target_slice_len, + pointer_place.position(), + ) + } + PredicateKind::UniqueRef { lifetime, is_final } => { + let TODO_target_slice_len = None; + lowerer.unique_ref_snap( + CallContext::Procedure, + ty, + ty, + place, + address, + lifetime.clone(), + TODO_target_slice_len, + *is_final, + pointer_place.position(), + ) + } + } + } +} + +impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for PlaceToSnapshot { + fn expression_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + expression: &vir_mid::Expression, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + assert!(expression.is_place(),); + let ty = expression.get_type(); + self.snap_call(lowerer, ty, expression) + } + + fn variable_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + variable: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult { + if let Some(label) = &self.old_label { + lowerer.snapshot_variable_version_at_label(variable, label) + } else { + lowerer.current_snapshot_variable_version(variable) + } + } + + fn labelled_old_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _old: &vir_mid::LabelledOld, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn func_app_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _app: &vir_mid::FuncApp, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn acc_predicate_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _acc_predicate: &vir_mid::AccPredicate, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn call_context(&self) -> CallContext { + todo!() + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + todo!() + } + + fn pointer_deref_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _deref: &vir_mid::Deref, + _base_snapshot: vir_low::Expression, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + unreachable!("Should be overriden."); + } + + fn push_bound_variables( + &mut self, + _variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + todo!() + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + todo!() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/pure_framed.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/pure_framed.rs new file mode 100644 index 00000000000..6b9073f86f1 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/expressions/pure_framed.rs @@ -0,0 +1,174 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + high::types::HighTypeEncoderInterface, + middle::core_proof::{ + builtin_methods::CallContext, + footprint::FootprintInterface, + lowerer::{FunctionsLowererInterface, Lowerer}, + pointers::PointersInterface, + snapshots::IntoSnapshotLowerer, + }, +}; + +use vir_crate::{ + common::{identifier::WithIdentifier, position::Positioned}, + low::{self as vir_low}, + middle::{self as vir_mid, operations::ty::Typed}, +}; + +pub(in super::super::super::super::super) struct FramedExpressionToSnapshot<'a> { + framing_variables: &'a [vir_mid::VariableDecl], +} + +impl<'a> FramedExpressionToSnapshot<'a> { + pub(in super::super::super::super::super) fn for_function_body( + framing_variables: &'a [vir_mid::VariableDecl], + ) -> Self { + Self { framing_variables } + } + + /// Find a base of type struct that has an invariant. + fn obtain_invariant<'e>( + &mut self, + lowerer: &mut Lowerer, + expression: &'e vir_mid::Expression, + ) -> SpannedEncodingResult<( + &'e vir_mid::Expression, + vir_mid::Expression, + Vec, + )> { + let ty = expression.get_type(); + if ty.is_struct() { + let type_decl = lowerer.encoder.get_type_decl_mid(ty)?; + if let vir_mid::TypeDecl::Struct(vir_mid::type_decl::Struct { + structural_invariant: Some(invariant), + .. + }) = type_decl + { + let self_place = vir_mid::VariableDecl::self_variable(ty.clone()); + Ok((expression, self_place.into(), invariant)) + } else { + unimplemented!("TODO: A proper error message that only permissions from non-nested structs are supported."); + } + } else { + let (base_place, parent, invariant) = self.obtain_invariant( + lowerer, + expression + .get_parent_ref() + .expect("TODO: A proper error message that the permission has to be framed."), + )?; + Ok((base_place, expression.with_new_parent(parent), invariant)) + } + } +} + +impl<'a, 'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> + for FramedExpressionToSnapshot<'a> +{ + fn variable_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + variable: &vir_mid::VariableDecl, + ) -> SpannedEncodingResult { + Ok(vir_low::VariableDecl { + name: variable.name.clone(), + ty: self.type_to_snapshot(lowerer, &variable.ty)?, + }) + } + + // FIXME: Code duplication with + // prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/mod.rs + fn labelled_old_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + old: &vir_mid::LabelledOld, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + // In pure contexts values cannot be mutated, so `old` has no effect. + self.expression_to_snapshot(lowerer, &old.base, expect_math_bool) + } + + // FIXME: Code duplication with + // prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/mod.rs + fn func_app_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + app: &vir_mid::FuncApp, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let arguments = + self.expression_vec_to_snapshot(lowerer, &app.arguments, expect_math_bool)?; + let return_type = self.type_to_snapshot(lowerer, &app.return_type)?; + let func_app = lowerer.call_pure_function_in_pure_context( + app.get_identifier(), + arguments, + return_type, + app.position, + )?; + let result = vir_low::Expression::DomainFuncApp(func_app); + self.ensure_bool_expression(lowerer, &app.return_type, result, expect_math_bool) + } + + fn acc_predicate_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _acc_predicate: &vir_mid::AccPredicate, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + todo!() + } + + fn call_context(&self) -> CallContext { + todo!() + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + todo!() + } + + fn pointer_deref_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + deref: &vir_mid::Deref, + _base_snapshot: vir_low::Expression, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + let (base_place, framed_place, invariant) = self.obtain_invariant(lowerer, &deref.base)?; + let framed_place = vir_mid::Expression::deref_no_pos(framed_place, deref.ty.clone()); + let (deref_fields, deref_range_fields) = + lowerer.structural_invariant_to_deref_fields(&invariant)?; + let base_snapshot = self.expression_to_snapshot(lowerer, base_place, expect_math_bool)?; + // for (deref_place, name, ty) in deref_fields { + for deref_field in deref_fields { + if deref_field.place == framed_place { + return lowerer.pointer_target_as_snapshot_field( + base_place.get_type(), + &deref_field.field_name, + deref_field.field_type, + base_snapshot, + deref.position, + ); + } + } + for deref_range_field in deref_range_fields { + unimplemented!("TODO: {}", deref_range_field.address); + } + unimplemented!("TODO: A proper error message that failed to find a framing place.") + } + + fn push_bound_variables( + &mut self, + _variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + todo!() + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + todo!() + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/mod.rs index 282b2ffede8..b1d9d7d8694 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/mod.rs @@ -1,22 +1,46 @@ -//! The traits for converting expressions into snapshots: -//! -//! + `procedure` contains the traits for converting in procedure contexts where -//! we need to use SSA form and `caller_for` for calling pure functions. -//! + `pure` contains the traits for converting in pure contexts such as axioms -//! and pure function definitions where we do not use neither SSA nor -//! `caller_for`. -//! + `builtin_methods` contains the traits for converting in builtin-method -//! contexts where we do not use SSA, but use `caller_for`. +//! The traits for converting expressions into snapshots. +/// Contains the traits for converting in builtin-method contexts where we do +/// not use SSA, but use `caller_for`. +/// +/// FIXME: This probably should be removed. mod builtin_methods; +/// The trait that provides the general skeleton for converting expressions into +/// snapshots. mod common; +/// Contains the traits for converting elements into the snapshots where the +/// context does not matter. Currently, the only example is types. mod context_independent; +/// Contains the traits for converting in procedure contexts where we need to +/// use SSA form and `caller_for` for calling pure functions. +/// +/// FIXME: This probably should be removed. mod procedure; +/// Contains the traits for converting in pure contexts such as axioms and pure +/// function definitions where we do not use neither SSA nor `caller_for`. +/// +/// FIXME: This probably should be removed. mod pure; +/// Contains structs for converting assertions (potentially containing +/// accessibility predicates) to snapshots. Both SSA and non-SSA forms are +/// supported. +mod assertions; +/// Contains structs for converting expressions to snapshots. +mod expressions; +mod utils; pub(in super::super) use self::{ + assertions::{ + AssertionToSnapshotConstructor, PredicateKind, SelfFramingAssertionEncoderState, + SelfFramingAssertionToSnapshot, ValidityAssertionToSnapshot, + }, builtin_methods::IntoBuiltinMethodSnapshot, + common::IntoSnapshotLowerer, context_independent::IntoSnapshot, - procedure::{IntoProcedureBoolExpression, IntoProcedureFinalSnapshot, IntoProcedureSnapshot}, + expressions::{FramedExpressionToSnapshot, PlaceToSnapshot}, + procedure::{ + IntoProcedureAssertion, IntoProcedureBoolExpression, IntoProcedureFinalSnapshot, + IntoProcedureSnapshot, + }, pure::{IntoPureBoolExpression, IntoPureSnapshot}, }; diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/procedure/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/procedure/mod.rs index 776b1e8e7c5..6f377fcceb0 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/procedure/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/procedure/mod.rs @@ -2,11 +2,18 @@ //! procedure bodies. Most important difference from `pure` is that this //! encoding uses SSA. -use super::common::IntoSnapshotLowerer; +use super::{ + common::IntoSnapshotLowerer, utils::bound_variable_stack::BoundVariableStack, PredicateKind, +}; use crate::encoder::{ errors::SpannedEncodingResult, middle::core_proof::{ + addresses::AddressesInterface, + builtin_methods::CallContext, lowerer::{FunctionsLowererInterface, Lowerer}, + places::PlacesInterface, + pointers::PointersInterface, + predicates::{PredicatesMemoryBlockInterface, PredicatesOwnedInterface}, references::ReferencesInterface, snapshots::SnapshotVariablesInterface, }, @@ -20,21 +27,64 @@ use vir_crate::{ mod traits; pub(in super::super::super) use self::traits::{ - IntoProcedureBoolExpression, IntoProcedureFinalSnapshot, IntoProcedureSnapshot, + IntoProcedureAssertion, IntoProcedureBoolExpression, IntoProcedureFinalSnapshot, + IntoProcedureSnapshot, }; -#[derive(Default)] -struct ProcedureSnapshot { +pub(in super::super::super::super) struct ProcedureSnapshot { old_label: Option, deref_to_final: bool, + is_assertion: bool, + in_heap_assertions: Vec, + predicate_kind: PredicateKind, + bound_variable_stack: BoundVariableStack, +} + +impl ProcedureSnapshot { + pub(in super::super) fn new_for_owned() -> Self { + Self { + old_label: None, + deref_to_final: false, + is_assertion: false, + in_heap_assertions: Vec::new(), + predicate_kind: PredicateKind::Owned, + bound_variable_stack: Default::default(), + } + } } impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for ProcedureSnapshot { + fn expression_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + expression: &vir_mid::Expression, + expect_math_bool: bool, + ) -> SpannedEncodingResult { + if !lowerer.use_heap_variable()? + && expression.is_place() + && expression.get_last_dereferenced_pointer().is_some() + { + // let address = lowerer.encode_expression_as_place_address(expression)?; + // let place = lowerer.encode_expression_as_place(expression)?; + // let root_address = lowerer.extract_root_address(expression)?; + let ty = expression.get_type(); + // return lowerer.owned_non_aliased_snap(CallContext::Procedure, ty, ty, place, root_address); + return self.owned_non_aliased_snap(lowerer, ty, expression); + } + self.expression_to_snapshot_impl(lowerer, expression, expect_math_bool) + } + fn variable_to_snapshot( &mut self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, variable: &vir_mid::VariableDecl, ) -> SpannedEncodingResult { + if self.bound_variable_stack.contains(variable) { + return Ok(vir_low::VariableDecl::new( + variable.name.clone(), + self.type_to_snapshot(lowerer, &variable.ty)?, + )); + } if let Some(label) = &self.old_label { lowerer.snapshot_variable_version_at_label(variable, label) } else { @@ -87,17 +137,266 @@ impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for ProcedureSnapsh lowerer.reference_target_final_snapshot( deref.base.get_type(), base_snapshot, - Default::default(), + deref.position, )? } else { let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, expect_math_bool)?; - lowerer.reference_target_current_snapshot( - deref.base.get_type(), - base_snapshot, - Default::default(), - )? + if deref.base.get_type().is_reference() { + lowerer.reference_target_current_snapshot( + deref.base.get_type(), + base_snapshot, + deref.position, + )? + } else { + lowerer.pointer_target_snapshot( + deref.base.get_type(), + &self.old_label, + base_snapshot, + deref.position, + )? + } }; self.ensure_bool_expression(lowerer, deref.get_type(), result, expect_math_bool) } + + fn acc_predicate_to_snapshot( + &mut self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + acc_predicate: &vir_mid::AccPredicate, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + assert!(self.is_assertion); + // fn in_heap<'p, 'v, 'tcx>( + // old_label: &Option, + // place: &vir_mid::Expression, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // ) -> SpannedEncodingResult { + // let in_heap = if let Some(pointer_place) = place.get_last_dereferenced_pointer() { + // let pointer = pointer_place.to_procedure_snapshot(lowerer)?; + // let address = + // lowerer.pointer_address(pointer_place.get_type(), pointer, place.position())?; + // let heap = lowerer.heap_variable_version_at_label(old_label)?; + // vir_low::Expression::container_op_no_pos( + // vir_low::ContainerOpKind::MapContains, + // heap.ty.clone(), + // vec![heap.into(), address], + // ) + // } else { + // unimplemented!("TODO: Proper error message: {:?}", place); + // }; + // Ok(in_heap) + // } + let expression = match &*acc_predicate.predicate { + vir_mid::Predicate::OwnedNonAliased(predicate) => { + let _ty = predicate.place.get_type(); + let _place = lowerer.encode_expression_as_place(&predicate.place)?; + let _root_address = lowerer.extract_root_address(&predicate.place)?; + let _snapshot = predicate.place.to_procedure_snapshot(lowerer)?; // FIXME: This is probably wrong. It should take into account the current old. + // if lowerer.use_heap_variable()? { + // let in_heap = in_heap(&self.old_label, &predicate.place, lowerer)?; + // self.in_heap_assertions.push(in_heap); + // } + // let acc = + unimplemented!(); + // lowerer.owned_aliased( + // CallContext::Procedure, + // ty, + // ty, + // place, + // root_address, + // snapshot, + // None, + // )? + // ; + // vir_low::Expression::and(in_heap, acc) + } + vir_mid::Predicate::OwnedRange(_predicate) => { + unimplemented!(); + // let ty = predicate.address.get_type(); + // let address = predicate.address.to_procedure_snapshot(lowerer)?; + // let start_index = predicate.start_index.to_procedure_snapshot(lowerer)?; + // let end_index = predicate.end_index.to_procedure_snapshot(lowerer)?; + // lowerer.owned_aliased_range( + // CallContext::Procedure, + // ty, + // ty, + // address, + // start_index, + // end_index, + // None, + // )? + } + vir_mid::Predicate::MemoryBlockHeap(predicate) => { + let place = lowerer.encode_expression_as_place_address(&predicate.address)?; + let size = predicate.size.to_procedure_snapshot(lowerer)?; + // if lowerer.use_heap_variable()? { + // let in_heap = in_heap(&self.old_label, &predicate.address, lowerer)?; + // self.in_heap_assertions.push(in_heap); + // } + // let acc = + lowerer.encode_memory_block_stack_acc(place, size, acc_predicate.position)? + //; + // vir_low::Expression::and(in_heap, acc) + } + vir_mid::Predicate::MemoryBlockHeapRange(_predicate) => { + unimplemented!(); + // let pointer_value = predicate.address.to_procedure_snapshot(lowerer)?; + // let address = lowerer.pointer_address( + // predicate.address.get_type(), + // pointer_value, + // predicate.position, + // )?; + // let size = predicate.size.to_procedure_snapshot(lowerer)?; + // let start_index = predicate.start_index.to_procedure_snapshot(lowerer)?; + // let end_index = predicate.end_index.to_procedure_snapshot(lowerer)?; + // lowerer.encode_memory_block_range_acc( + // address, + // size, + // start_index, + // end_index, + // acc_predicate.position, + // )? + } + vir_mid::Predicate::MemoryBlockHeapDrop(predicate) => { + let place = lowerer.encode_expression_as_place_address(&predicate.address)?; // FIXME: This looks very wrong. + let size = predicate.size.to_procedure_snapshot(lowerer)?; + // if lowerer.use_heap_variable()? { + // let in_heap = in_heap(&self.old_label, &predicate.address, lowerer)?; + // self.in_heap_assertions.push(in_heap); + // } + // let acc = + lowerer.encode_memory_block_heap_drop_acc(place, size, acc_predicate.position)? + // ; + // vir_low::Expression::and(in_heap, acc) + } + _ => unimplemented!("{acc_predicate}"), + }; + Ok(expression) + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_place: &vir_mid::Expression, + ) -> SpannedEncodingResult { + unimplemented!(); + // let place = lowerer.encode_expression_as_place(pointer_place)?; + // let root_address = lowerer.extract_root_address(pointer_place)?; + // match &self.predicate_kind { + // PredicateKind::Owned => lowerer.owned_non_aliased_snap( + // CallContext::Procedure, + // ty, + // ty, + // place, + // root_address, + // pointer_place.position(), + // ), + // PredicateKind::FracRef { lifetime } => todo!(), + // PredicateKind::UniqueRef { lifetime, is_final } => { + // let TODO_target_slice_len = None; + // lowerer.unique_ref_snap( + // CallContext::Procedure, + // ty, + // ty, + // place, + // root_address, + // lifetime.clone(), + // TODO_target_slice_len, + // *is_final, + // ) + // } + // } + // if let Some(reference_place) = pointer_place.get_first_dereferenced_reference() { + // let vir_mid::Type::Reference(reference_type) = reference_place.get_type() else { + // unreachable!() + // }; + // let TODO_target_slice_len = None; + // let lifetime = lowerer + // .encode_lifetime_const_into_procedure_variable(reference_type.lifetime.clone())?; + // match reference_type.uniqueness { + // vir_mid::ty::Uniqueness::Unique => lowerer.unique_ref_snap( + // CallContext::Procedure, + // ty, + // ty, + // place, + // root_address, + // lifetime.into(), + // TODO_target_slice_len, + // self.deref_to_final, + // ), + // vir_mid::ty::Uniqueness::Shared => lowerer.frac_ref_snap( + // CallContext::Procedure, + // ty, + // ty, + // place, + // root_address, + // lifetime.into(), + // TODO_target_slice_len, + // ), + // } + // } else { + // lowerer.owned_non_aliased_snap( + // CallContext::Procedure, + // ty, + // ty, + // place, + // root_address, + // pointer_place.position(), + // ) + // } + // // TODO: Check whether the place is behind a shared/mutable reference and use the appropriate function + // eprintln!("pointer_place: {}", pointer_place); + // eprintln!("pointer_place: {:?}", pointer_place); + } + + fn call_context(&self) -> CallContext { + CallContext::Procedure + } + + fn push_bound_variables( + &mut self, + variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + self.bound_variable_stack.push(variables); + Ok(()) + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + self.bound_variable_stack.pop(); + Ok(()) + } + + // fn unfolding_to_snapshot( + // &mut self, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // unfolding: &vir_mid::Unfolding, + // expect_math_bool: bool, + // ) -> SpannedEncodingResult { + // let predicate = match &*unfolding.predicate { + // vir_mid::Predicate::OwnedNonAliased(predicate) => { + // let ty = predicate.place.get_type(); + // lowerer.mark_owned_predicate_as_unfolded(ty)?; + // let place = lowerer.encode_expression_as_place(&predicate.place)?; + // let root_address = lowerer.extract_root_address(&predicate.place)?; + // let snapshot = predicate.place.to_procedure_snapshot(lowerer)?; // FIXME: This is probably wrong. It should take into account the current old. + // lowerer + // .owned_non_aliased( + // CallContext::Procedure, + // ty, + // ty, + // place, + // root_address, + // snapshot, + // None, + // )? + // .unwrap_predicate_access_predicate() + // } + // _ => unimplemented!("{unfolding}"), + // }; + // let body = self.expression_to_snapshot(lowerer, &unfolding.body, expect_math_bool)?; + // let expression = vir_low::Expression::unfolding(predicate, body, unfolding.position); + // Ok(expression) + // } } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/procedure/traits.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/procedure/traits.rs index 30a50fbb9a1..76cd89c4145 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/procedure/traits.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/procedure/traits.rs @@ -6,6 +6,7 @@ use crate::encoder::{ middle::core_proof::{lowerer::Lowerer, snapshots::into_snapshot::common::IntoSnapshotLowerer}, }; use vir_crate::{ + common::expression::ExpressionIterator, low::{self as vir_low}, middle::{self as vir_mid}, }; @@ -25,7 +26,35 @@ impl IntoProcedureBoolExpression for vir_mid::Expression { &self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, ) -> SpannedEncodingResult { - ProcedureSnapshot::default().expression_to_snapshot(lowerer, self, true) + ProcedureSnapshot::new_for_owned().expression_to_snapshot(lowerer, self, true) + } +} + +/// Converts `self` into assertion that evaluates to a Viper Bool. +pub(in super::super::super::super) trait IntoProcedureAssertion { + type Target; + fn to_procedure_assertion<'p, 'v: 'p, 'tcx: 'v>( + &self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult; +} + +impl IntoProcedureAssertion for vir_mid::Expression { + type Target = vir_low::Expression; + fn to_procedure_assertion<'p, 'v: 'p, 'tcx: 'v>( + &self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult { + let mut snapshot_encoder = ProcedureSnapshot { + is_assertion: true, + ..ProcedureSnapshot::new_for_owned() + }; + let expression = snapshot_encoder.expression_to_snapshot(lowerer, self, true)?; + Ok(snapshot_encoder + .in_heap_assertions + .into_iter() + .chain(std::iter::once(expression)) + .conjoin()) } } @@ -43,7 +72,7 @@ impl IntoProcedureSnapshot for vir_mid::VariableDecl { &self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, ) -> SpannedEncodingResult { - ProcedureSnapshot::default().variable_to_snapshot(lowerer, self) + ProcedureSnapshot::new_for_owned().variable_to_snapshot(lowerer, self) } } @@ -53,7 +82,7 @@ impl IntoProcedureSnapshot for vir_mid::Expression { &self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, ) -> SpannedEncodingResult { - ProcedureSnapshot::default().expression_to_snapshot(lowerer, self, false) + ProcedureSnapshot::new_for_owned().expression_to_snapshot(lowerer, self, false) } } @@ -87,7 +116,7 @@ impl IntoProcedureFinalSnapshot for vir_mid::Expression { ) -> SpannedEncodingResult { let mut snapshot_encoder = ProcedureSnapshot { deref_to_final: true, - ..ProcedureSnapshot::default() + ..ProcedureSnapshot::new_for_owned() }; snapshot_encoder.expression_to_snapshot(lowerer, self, false) } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/mod.rs index aa9561a9468..ac263aa9ac1 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/mod.rs @@ -4,7 +4,10 @@ use super::common::IntoSnapshotLowerer; use crate::encoder::{ errors::SpannedEncodingResult, - middle::core_proof::lowerer::{FunctionsLowererInterface, Lowerer}, + middle::core_proof::{ + builtin_methods::CallContext, + lowerer::{FunctionsLowererInterface, Lowerer}, + }, }; use vir_crate::{ common::identifier::WithIdentifier, @@ -16,7 +19,11 @@ mod traits; pub(in super::super::super) use self::traits::{IntoPureBoolExpression, IntoPureSnapshot}; -struct PureSnapshot; +#[derive(Default)] +struct PureSnapshot { + /// Assume that all pointer accesses are safe. + assume_pointers_to_be_framed: bool, +} impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for PureSnapshot { fn variable_to_snapshot( @@ -58,4 +65,131 @@ impl<'p, 'v: 'p, 'tcx: 'v> IntoSnapshotLowerer<'p, 'v, 'tcx> for PureSnapshot { // In pure contexts values cannot be mutated, so `old` has no effect. self.expression_to_snapshot(lowerer, &old.base, expect_math_bool) } + + fn pointer_deref_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + deref: &vir_mid::Deref, + base_snapshot: vir_low::Expression, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + // FIXME: Delete. + assert!(self.assume_pointers_to_be_framed); + eprintln!("deref: {deref}"); + eprintln!("base_snapshot: {base_snapshot}"); + unimplemented!(); + } + + // fn deref_to_snapshot( + // &mut self, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // deref: &vir_mid::Deref, + // expect_math_bool: bool, + // ) -> SpannedEncodingResult { + // let base_snapshot = self.expression_to_snapshot(lowerer, &deref.base, expect_math_bool)?; + // let ty = deref.base.get_type(); + // let result = if ty.is_reference() { + // lowerer.reference_target_current_snapshot(ty, base_snapshot, deref.position)? + // } else { + // unreachable!(); + // // unimplemented!("TODO: to double-check that this is actually used (and in a correct way)"); + // // This most likely should be unreachable. In axioms we should use snapshot variables + // // instead. + // // let heap = vir_low::VariableDecl::new("pure_heap$", lowerer.heap_type()?); + // // lowerer.pointer_target_snapshot_in_heap( + // // deref.base.get_type(), + // // heap, + // // base_snapshot, + // // deref.position, + // // )? + // // lowerer.pointer_target_snapshot( + // // deref.base.get_type(), + // // &None, + // // base_snapshot, + // // deref.position, + // // )? + // }; + // self.ensure_bool_expression(lowerer, deref.get_type(), result, expect_math_bool) + // } + + // FIXME: Mark as unreachable. + fn acc_predicate_to_snapshot( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _acc_predicate: &vir_mid::AccPredicate, + _expect_math_bool: bool, + ) -> SpannedEncodingResult { + unimplemented!("FIXME: Delete"); + // assert!(self.is_assertion); + // let expression = match &*acc_predicate.predicate { + // vir_mid::Predicate::OwnedNonAliased(predicate) => { + // eprintln!("pure predicate: {}", predicate); + // let ty = predicate.place.get_type(); + // let place = lowerer.encode_expression_as_place(&predicate.place)?; + // // let root_address = lowerer.extract_root_address(&predicate.place)?; + // let root_address = true.into(); + // // let snapshot = predicate.place.to_pure_snapshot(lowerer)?; + // let snapshot = true.into(); + // let acc = lowerer.owned_aliased( + // CallContext::Procedure, + // ty, + // ty, + // place, + // root_address, + // snapshot, + // None, + // )?; + // eprintln!(" → {}", acc); + // acc + // } + // vir_mid::Predicate::MemoryBlockHeap(predicate) => { + // // let place = lowerer.encode_expression_as_place_address(&predicate.address)?; + // let place = true.into(); + // let size = predicate.size.to_pure_snapshot(lowerer)?; + // lowerer.encode_memory_block_stack_acc(place, size, acc_predicate.position)? + // } + // vir_mid::Predicate::MemoryBlockHeapDrop(predicate) => { + // // let place = lowerer.encode_expression_as_place_address(&predicate.address)?; + // let place = true.into(); + // // let size = predicate.size.to_pure_snapshot(lowerer)?; + // let size = true.into(); + // lowerer.encode_memory_block_heap_drop_acc(place, size, acc_predicate.position)? + // } + // _ => unimplemented!("{acc_predicate}"), + // }; + // Ok(expression) + } + + fn owned_non_aliased_snap( + &mut self, + _lowerer: &mut Lowerer<'p, 'v, 'tcx>, + _ty: &vir_mid::Type, + _pointer_snapshot: &vir_mid::Expression, + ) -> SpannedEncodingResult { + unimplemented!() + } + + // fn unfolding_to_snapshot( + // &mut self, + // lowerer: &mut Lowerer<'p, 'v, 'tcx>, + // unfolding: &vir_mid::Unfolding, + // expect_math_bool: bool, + // ) -> SpannedEncodingResult { + // todo!() + // } + + fn call_context(&self) -> CallContext { + todo!() + } + + fn push_bound_variables( + &mut self, + _variables: &[vir_mid::VariableDecl], + ) -> SpannedEncodingResult<()> { + todo!() + } + + fn pop_bound_variables(&mut self) -> SpannedEncodingResult<()> { + todo!() + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/traits.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/traits.rs index 3cda221b325..c06e5b9c6e1 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/traits.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/pure/traits.rs @@ -28,7 +28,7 @@ impl IntoPureBoolExpression for vir_mid::Expression { &self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, ) -> SpannedEncodingResult { - PureSnapshot.expression_to_snapshot(lowerer, self, true) + PureSnapshot::default().expression_to_snapshot(lowerer, self, true) } } @@ -38,7 +38,31 @@ impl IntoPureBoolExpression for Vec { &self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, ) -> SpannedEncodingResult { - PureSnapshot.expression_vec_to_snapshot(lowerer, self, true) + PureSnapshot::default().expression_vec_to_snapshot(lowerer, self, true) + } +} + +/// Converts `self` into expression that evaluates to a snapshot. It assumes +/// that all pointers can be safely dereferenced. +pub(in super::super::super::super) trait IntoFramedPureSnapshot { + type Target; + fn to_framed_pure_snapshot<'p, 'v: 'p, 'tcx: 'v>( + &self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult; +} + +impl IntoFramedPureSnapshot for vir_mid::Expression { + type Target = vir_low::Expression; + fn to_framed_pure_snapshot<'p, 'v: 'p, 'tcx: 'v>( + &self, + lowerer: &mut Lowerer<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult { + let mut snapshot_encoder = PureSnapshot { + assume_pointers_to_be_framed: true, + ..PureSnapshot::default() + }; + snapshot_encoder.expression_to_snapshot(lowerer, self, true) } } @@ -57,7 +81,7 @@ impl IntoPureSnapshot for vir_mid::Expression { &self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, ) -> SpannedEncodingResult { - PureSnapshot.expression_to_snapshot(lowerer, self, false) + PureSnapshot::default().expression_to_snapshot(lowerer, self, false) } } @@ -69,7 +93,7 @@ impl IntoPureSnapshot for Vec { ) -> SpannedEncodingResult { let mut variables = Vec::new(); for variable in self { - variables.push(PureSnapshot.variable_to_snapshot(lowerer, variable)?); + variables.push(PureSnapshot::default().variable_to_snapshot(lowerer, variable)?); } Ok(variables) } @@ -81,7 +105,7 @@ impl IntoPureSnapshot for vir_mid::VariableDecl { &self, lowerer: &mut Lowerer<'p, 'v, 'tcx>, ) -> SpannedEncodingResult { - PureSnapshot.variable_to_snapshot(lowerer, self) + PureSnapshot::default().variable_to_snapshot(lowerer, self) } } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/utils/bound_variable_stack.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/utils/bound_variable_stack.rs new file mode 100644 index 00000000000..b6945fa582c --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/utils/bound_variable_stack.rs @@ -0,0 +1,35 @@ +use rustc_hash::FxHashSet; +use vir_crate::middle::{self as vir_mid}; + +#[derive(Default)] +pub(in super::super) struct BoundVariableStack { + stack: Vec>, +} + +impl BoundVariableStack { + pub(in super::super) fn contains(&self, variable: &vir_mid::VariableDecl) -> bool { + self.stack.iter().any(|set| set.contains(&variable.name)) + } + + pub(in super::super) fn push(&mut self, variables: &[vir_mid::VariableDecl]) { + self.stack.push( + variables + .iter() + .map(|variable| variable.name.clone()) + .collect(), + ) + } + + pub(in super::super) fn pop(&mut self) { + assert!(self.stack.pop().is_some()); + } +} + +impl Drop for BoundVariableStack { + fn drop(&mut self) { + // Check when not panicking. + if !std::thread::panicking() { + assert!(self.stack.is_empty()); + } + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/utils/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/utils/mod.rs new file mode 100644 index 00000000000..8b4ea127c7d --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/into_snapshot/utils/mod.rs @@ -0,0 +1 @@ +pub(super) mod bound_variable_stack; diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/mod.rs index 51806b98828..524c25a13b9 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/mod.rs @@ -9,13 +9,16 @@ mod values; mod variables; pub(super) use self::{ - adts::SnapshotAdtsInterface, + adts::{SnapshotAdtsInterface, SnapshotDomainsInfo}, builtin_functions::BuiltinFunctionsInterface, bytes::SnapshotBytesInterface, domains::SnapshotDomainsInterface, into_snapshot::{ - IntoBuiltinMethodSnapshot, IntoProcedureBoolExpression, IntoProcedureFinalSnapshot, + AssertionToSnapshotConstructor, FramedExpressionToSnapshot, IntoBuiltinMethodSnapshot, + IntoProcedureAssertion, IntoProcedureBoolExpression, IntoProcedureFinalSnapshot, IntoProcedureSnapshot, IntoPureBoolExpression, IntoPureSnapshot, IntoSnapshot, + IntoSnapshotLowerer, PlaceToSnapshot, PredicateKind, SelfFramingAssertionToSnapshot, + ValidityAssertionToSnapshot, }, state::SnapshotsState, validity::{valid_call, valid_call2, SnapshotValidityInterface}, diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/state.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/state.rs index 3d7591b02b9..2709580ba88 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/state.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/state.rs @@ -1,5 +1,4 @@ -use super::variables::{AllVariablesMap, VariableVersionMap}; - +use super::{adts::SnapshotDomainsInfo, into_snapshot::SelfFramingAssertionEncoderState}; use rustc_hash::{FxHashMap, FxHashSet}; use std::collections::BTreeMap; use vir_crate::{ @@ -9,16 +8,24 @@ use vir_crate::{ #[derive(Default)] pub(in super::super) struct SnapshotsState { + pub(super) snapshot_domains_info: SnapshotDomainsInfo, /// Used for decoding domain names into original types. pub(super) domain_types: BTreeMap, /// The list of types for which `to_bytes` was encoded. pub(super) encoded_to_bytes: FxHashSet, /// The list of types for which sequence_repeat_constructor was encoded. pub(super) encoded_sequence_repeat_constructor: FxHashSet, - pub(super) all_variables: AllVariablesMap, - pub(super) variables: BTreeMap, - pub(super) variables_at_label: BTreeMap, - pub(super) current_variables: Option, + pub(super) ssa_state: vir_low::ssa::SSAState, + // pub(super) all_variables: AllVariablesMap, + // pub(super) variables: BTreeMap, + // pub(super) variables_at_label: BTreeMap, + // pub(super) current_variables: Option, /// Mapping from low types to their domain names. pub(super) type_domains: FxHashMap, + pub(super) self_framing_assertion_encoder_state: SelfFramingAssertionEncoderState, +} +impl SnapshotsState { + pub(in super::super) fn destruct(self) -> SnapshotDomainsInfo { + self.snapshot_domains_info + } } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/validity/interface.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/validity/interface.rs index 70cc436c562..6ab29932790 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/validity/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/validity/interface.rs @@ -54,6 +54,12 @@ pub(in super::super::super) trait SnapshotValidityInterface { &mut self, domain_name: &str, parameters: Vec, + ) -> SpannedEncodingResult<()>; + fn encode_validity_axioms_struct_with_invariant( + &mut self, + domain_name: &str, + parameters: Vec, + parameters_with_validity: usize, invariant: vir_low::Expression, ) -> SpannedEncodingResult<()>; fn encode_validity_axioms_struct_alternative_constructor( @@ -61,6 +67,7 @@ pub(in super::super::super) trait SnapshotValidityInterface { domain_name: &str, variant_name: &str, parameters: Vec, + parameters_with_validity: usize, invariant: vir_low::Expression, ) -> SpannedEncodingResult<()>; /// `variants` is `(variant_name, variant_domain, discriminant)`. @@ -120,31 +127,56 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotValidityInterface for Lowerer<'p, 'v, 'tcx> { ) -> SpannedEncodingResult<()> { use vir_low::macros::*; let parameters = vars! { value: {parameter_type}}; - self.encode_validity_axioms_struct(domain_name, parameters, invariant) + let parameters_with_validity = parameters.len(); + self.encode_validity_axioms_struct_with_invariant( + domain_name, + parameters, + parameters_with_validity, + invariant, + ) } fn encode_validity_axioms_struct( &mut self, domain_name: &str, parameters: Vec, + ) -> SpannedEncodingResult<()> { + let parameters_with_validity = parameters.len(); + self.encode_validity_axioms_struct_with_invariant( + domain_name, + parameters, + parameters_with_validity, + true.into(), + ) + } + fn encode_validity_axioms_struct_with_invariant( + &mut self, + domain_name: &str, + parameters: Vec, + parameters_with_validity: usize, invariant: vir_low::Expression, ) -> SpannedEncodingResult<()> { self.encode_validity_axioms_struct_alternative_constructor( domain_name, "", parameters, + parameters_with_validity, invariant, ) } + /// `parameters_with_validity` – how many of `parameters` should have a + /// conjoined validity call. For all Rust types without permissions in their + /// structural invariants, `parameters_with_validity == parameters.len()`. fn encode_validity_axioms_struct_alternative_constructor( &mut self, domain_name: &str, variant_name: &str, parameters: Vec, + parameters_with_validity: usize, invariant: vir_low::Expression, ) -> SpannedEncodingResult<()> { use vir_low::macros::*; let mut valid_parameters = Vec::new(); - for parameter in ¶meters { + for parameter in parameters.iter().take(parameters_with_validity) { if let Some(domain_name) = self.get_non_primitive_domain(¶meter.ty) { let domain_name = domain_name.to_string(); valid_parameters @@ -159,7 +191,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotValidityInterface for Lowerer<'p, 'v, 'tcx> { .map(|parameter| parameter.clone().into()) .collect(), )?; - let valid_constructor = self.encode_snapshot_valid_call(domain_name, constructor_call)?; + let valid_constructor = + self.encode_snapshot_valid_call(domain_name, constructor_call.clone())?; if parameters.is_empty() { let axiom = vir_low::DomainAxiomDecl { comment: None, @@ -181,8 +214,11 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotValidityInterface for Lowerer<'p, 'v, 'tcx> { // parameters, the bottom-up and top-down axioms are equivalent. let mut top_down_validity_expression = validity_expression.clone(); var_decls! { snapshot: {vir_low::Type::domain(domain_name.to_string())}}; + let snapshot_expression = snapshot.clone().into(); + top_down_validity_expression = + top_down_validity_expression.replace_self(&snapshot_expression); let valid_constructor = - self.encode_snapshot_valid_call(domain_name, snapshot.clone().into())?; + self.encode_snapshot_valid_call(domain_name, snapshot_expression)?; let mut triggers = Vec::new(); for parameter in ¶meters { if self.get_non_primitive_domain(¶meter.ty).is_some() { @@ -214,6 +250,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotValidityInterface for Lowerer<'p, 'v, 'tcx> { }; self.declare_axiom(domain_name, axiom_top_down)?; } + let bottom_up_validity_expression = validity_expression.replace_self(&constructor_call); let axiom_bottom_up_body = { let mut trigger = vec![valid_constructor.clone()]; trigger.extend(valid_parameters.clone()); @@ -221,7 +258,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotValidityInterface for Lowerer<'p, 'v, 'tcx> { parameters, vec![vir_low::Trigger::new(trigger)], expr! { - [ valid_constructor ] == [ validity_expression ] + [ valid_constructor ] == [ bottom_up_validity_expression ] }, ) }; diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/values/interface.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/values/interface.rs index 98af319413c..c751376d32f 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/values/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/values/interface.rs @@ -23,6 +23,14 @@ pub(in super::super::super) trait SnapshotValuesInterface { argument: vir_low::Expression, position: vir_mid::Position, ) -> SpannedEncodingResult; + fn obtain_parameter_snapshot( + &mut self, + base_type: &vir_mid::Type, + parameter_name: &str, + parameter_type: vir_low::Type, + base_snapshot: vir_low::Expression, + position: vir_mid::Position, + ) -> SpannedEncodingResult; fn obtain_struct_field_snapshot( &mut self, base_type: &vir_mid::Type, @@ -118,19 +126,46 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotValuesInterface for Lowerer<'p, 'v, 'tcx> { position, ) } - fn obtain_struct_field_snapshot( + fn obtain_parameter_snapshot( &mut self, base_type: &vir_mid::Type, - field: &vir_mid::FieldDecl, + parameter_name: &str, + parameter_type: vir_low::Type, base_snapshot: vir_low::Expression, position: vir_mid::Position, ) -> SpannedEncodingResult { let domain_name = self.encode_snapshot_domain_name(base_type)?; - let return_type = field.ty.to_snapshot(self)?; + let return_type = parameter_type; Ok(self - .snapshot_destructor_struct_call(&domain_name, &field.name, return_type, base_snapshot)? + .snapshot_destructor_struct_call( + &domain_name, + parameter_name, + return_type, + base_snapshot, + )? .set_default_position(position)) } + fn obtain_struct_field_snapshot( + &mut self, + base_type: &vir_mid::Type, + field: &vir_mid::FieldDecl, + base_snapshot: vir_low::Expression, + position: vir_mid::Position, + ) -> SpannedEncodingResult { + let parameter_type = field.ty.to_snapshot(self)?; + self.obtain_parameter_snapshot( + base_type, + &field.name, + parameter_type, + base_snapshot, + position, + ) + // let domain_name = self.encode_snapshot_domain_name(base_type)?; + // let return_type = field.ty.to_snapshot(self)?; + // Ok(self + // .snapshot_destructor_struct_call(&domain_name, &field.name, return_type, base_snapshot)? + // .set_default_position(position)) + } fn obtain_enum_variant_snapshot( &mut self, base_type: &vir_mid::Type, @@ -209,7 +244,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotValuesInterface for Lowerer<'p, 'v, 'tcx> { vir_mid::Type::Reference(_) => self.address_type()?, x => unimplemented!("{:?}", x), }; - vir_low::operations::ty::Typed::set_type(&mut argument, low_type); + if !ty.is_bool() { + vir_low::operations::ty::Typed::set_type(&mut argument, low_type); + } Ok(self .snapshot_constructor_constant_call(&domain_name, vec![argument])? .set_default_position(position)) diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/interface.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/interface.rs index 8f63e6eec50..ddc6988cf65 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/interface.rs @@ -1,8 +1,11 @@ use crate::encoder::{ - errors::{ErrorCtxt, SpannedEncodingResult}, + errors::SpannedEncodingResult, high::types::HighTypeEncoderInterface, middle::core_proof::{ + addresses::AddressesInterface, + heap::HeapInterface, lowerer::{Lowerer, VariablesLowererInterface}, + pointers::PointersInterface, references::ReferencesInterface, snapshots::{ IntoProcedureSnapshot, IntoSnapshot, SnapshotValidityInterface, SnapshotValuesInterface, @@ -10,7 +13,6 @@ use crate::encoder::{ type_layouts::TypeLayoutsInterface, types::TypesInterface, }, - mir::errors::ErrorInterface, }; use std::collections::BTreeMap; @@ -19,36 +21,38 @@ use vir_crate::{ middle::{self as vir_mid, operations::ty::Typed}, }; -use super::VariableVersionMap; +// trait Private { +// fn create_snapshot_variable( +// &mut self, +// name: &str, +// ty: &vir_mid::Type, +// version: u64, +// ) -> SpannedEncodingResult; +// #[allow(clippy::ptr_arg)] // Clippy false positive. +// /// Note: if `new_snapshot_root` is `Some`, the current encoding assumes +// /// that the `place` is not behind a raw pointer. +// fn snapshot_copy_except( +// &mut self, +// statements: &mut Vec, +// base: vir_mid::VariableDecl, +// // old_snapshot_root: vir_low::Expression, +// // new_snapshot_root: vir_low::Expression, +// place: &vir_mid::Expression, +// position: vir_low::Position, +// ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)>; +// } -trait Private { - fn create_snapshot_variable( - &mut self, - name: &str, - ty: &vir_mid::Type, - version: u64, - ) -> SpannedEncodingResult; - #[allow(clippy::ptr_arg)] // Clippy false positive. - fn snapshot_copy_except( - &mut self, - statements: &mut Vec, - old_snapshot_root: vir_low::VariableDecl, - new_snapshot_root: vir_low::VariableDecl, - place: &vir_mid::Expression, - position: vir_low::Position, - ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)>; -} - -impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { +impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { fn create_snapshot_variable( &mut self, name: &str, ty: &vir_mid::Type, version: u64, ) -> SpannedEncodingResult { - let name = format!("{name}$snapshot${version}"); + // let name = format!("{}$snapshot${}", name, version); let ty = ty.to_snapshot(self)?; - self.create_variable(name, ty) + // self.create_variable(name, ty) + self.create_snapshot_variable_low(name, ty, version) } /// Copy all values of the old snapshot into the new snapshot, except the /// ones that belong to `place`. @@ -57,27 +61,72 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { fn snapshot_copy_except( &mut self, statements: &mut Vec, - old_snapshot_root: vir_low::VariableDecl, - new_snapshot_root: vir_low::VariableDecl, + base: vir_mid::VariableDecl, + // old_snapshot_root: vir_low::Expression, + // new_snapshot_root: vir_low::Expression, place: &vir_mid::Expression, position: vir_low::Position, ) -> SpannedEncodingResult<(vir_low::Expression, vir_low::Expression)> { use vir_low::macros::*; if let Some(parent) = place.get_parent_ref() { - let (old_snapshot, new_snapshot) = self.snapshot_copy_except( - statements, - old_snapshot_root, - new_snapshot_root, - parent, - position, - )?; let parent_type = parent.get_type(); + let (old_snapshot, new_snapshot) = + if let vir_mid::Type::Pointer(pointer_type) = parent_type { + let fresh_heap_chunk = self.fresh_heap_chunk(position)?; + let heap_chunk = self.heap_chunk_to_snapshot( + &pointer_type.target_type, + fresh_heap_chunk.clone().into(), + position, + )?; + if self.use_heap_variable()? { + let old_snapshot = parent.to_procedure_snapshot(self)?; // FIXME: This is most likely wrong. + let old_target_snapshot = self.pointer_target_snapshot( + parent.get_type(), + &None, + old_snapshot.clone(), + position, + )?; + let old_heap = self.heap_variable_version_at_label(&None)?; + + // Note: All `old_*` need to be computed before the heap version + // is incremented. + let new_heap = self.new_heap_variable_version(position)?; + let address = + self.pointer_address(parent.get_type(), old_snapshot, position)?; + statements.push(vir_low::Statement::assign( + new_heap, + self.heap_update( + old_heap.into(), + address, + fresh_heap_chunk.into(), + position, + )?, + // vir_low::Expression::container_op( + // vir_low::ContainerOpKind::MapUpdate, + // self.heap_type()?, + // vec![old_heap.into(), address, fresh_heap_chunk.into()], + // position, + // ), + position, + )); + return Ok((old_target_snapshot, heap_chunk)); + } else { + return Ok((heap_chunk.clone(), heap_chunk)); + } + } else { + self.snapshot_copy_except( + statements, base, + // old_snapshot_root, + // new_snapshot_root, + parent, position, + )? + }; + let type_decl = self.encoder.get_type_decl_mid(parent_type)?; match &type_decl { vir_mid::TypeDecl::Bool | vir_mid::TypeDecl::Int(_) - | vir_mid::TypeDecl::Float(_) - | vir_mid::TypeDecl::Pointer(_) => { + | vir_mid::TypeDecl::Float(_) => { unreachable!("place: {}", place); } vir_mid::TypeDecl::Trusted(_) | vir_mid::TypeDecl::TypeVar(_) => { @@ -231,6 +280,38 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { unimplemented!("Place: {}", place); } } + vir_mid::TypeDecl::Pointer(_decl) => { + unreachable!("Should be handled by the caller."); + // let fresh_heap_chunk = self.fresh_heap_chunk()?; + // let heap_chunk = self.heap_chunk_to_snapshot( + // &decl.target_type, + // fresh_heap_chunk.clone().into(), + // position, + // )?; + // let old_heap = self.heap_variable_version_at_label(&None)?; + // let new_heap = self.new_heap_variable_version(position)?; + // let address = + // self.pointer_address(parent_type, old_snapshot.clone(), position)?; + // statements.push(vir_low::Statement::assign( + // new_heap, + // vir_low::Expression::container_op( + // vir_low::ContainerOpKind::MapUpdate, + // self.heap_type()?, + // vec![old_heap.into(), address, fresh_heap_chunk.into()], + // position, + // ), + // position, + // )); + // // statements.push(vir_low::Statement::assume( + // // vir_low::Expression::equals( + // // heap_chunk.clone(), + + // // ) + // // )); + // let old_target_snapshot = + // self.pointer_target_snapshot(parent_type, &None, old_snapshot, position)?; + // Ok((old_target_snapshot, heap_chunk)) + } vir_mid::TypeDecl::Sequence(_) => unimplemented!("ty: {}", type_decl), vir_mid::TypeDecl::Map(_) => unimplemented!("ty: {}", type_decl), vir_mid::TypeDecl::Never => unimplemented!("ty: {}", type_decl), @@ -238,6 +319,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { vir_mid::TypeDecl::Unsupported(_) => unimplemented!("ty: {}", type_decl), } } else { + let old_snapshot_root = base.to_procedure_snapshot(self)?; + let new_snapshot_root = self.new_snapshot_variable_version(&base, position)?; // We reached the root. Nothing to do here. Ok((old_snapshot_root.into(), new_snapshot_root.into())) } @@ -245,6 +328,12 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { } pub(in super::super::super) trait SnapshotVariablesInterface { + fn create_snapshot_variable_low( + &mut self, + name: &str, + ty: vir_low::Type, + version: u64, + ) -> SpannedEncodingResult; fn new_snapshot_variable_version( &mut self, variable: &vir_mid::VariableDecl, @@ -263,21 +352,40 @@ pub(in super::super::super) trait SnapshotVariablesInterface { variable: &vir_mid::VariableDecl, label: &str, ) -> SpannedEncodingResult; + fn use_heap_variable(&self) -> SpannedEncodingResult; + fn heap_variable_name(&self) -> SpannedEncodingResult<&'static str>; + fn new_heap_variable_version( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult; + fn heap_variable_version_at_label( + &mut self, + old_label: &Option, + ) -> SpannedEncodingResult; + fn address_variable_version_at_label( + &mut self, + variable_name: &str, + old_label: &Option, + ) -> SpannedEncodingResult; + fn fresh_heap_chunk( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult; fn encode_snapshot_havoc( &mut self, statements: &mut Vec, target: &vir_mid::Expression, position: vir_low::Position, - new_snapshot: Option, - ) -> SpannedEncodingResult<()>; + // new_snapshot: Option, + ) -> SpannedEncodingResult; fn encode_snapshot_update_with_new_snapshot( &mut self, statements: &mut Vec, target: &vir_mid::Expression, value: vir_low::Expression, position: vir_low::Position, - new_snapshot: Option, - ) -> SpannedEncodingResult<()>; + // new_snapshot: Option, + ) -> SpannedEncodingResult; #[allow(clippy::ptr_arg)] // Clippy false positive. fn encode_snapshot_update( &mut self, @@ -292,8 +400,15 @@ pub(in super::super::super) trait SnapshotVariablesInterface { predecessors: &BTreeMap>, basic_block_edges: &mut BTreeMap< vir_mid::BasicBlockId, - BTreeMap>, + BTreeMap< + vir_mid::BasicBlockId, + Vec<(String, vir_low::Type, vir_low::Position, u64, u64)>, + >, >, + // basic_block_edges: &mut BTreeMap< + // vir_mid::BasicBlockId, + // BTreeMap>, + // >, ) -> SpannedEncodingResult<()>; fn unset_current_block_for_snapshots( &mut self, @@ -303,32 +418,51 @@ pub(in super::super::super) trait SnapshotVariablesInterface { } impl<'p, 'v: 'p, 'tcx: 'v> SnapshotVariablesInterface for Lowerer<'p, 'v, 'tcx> { + fn create_snapshot_variable_low( + &mut self, + name: &str, + ty: vir_low::Type, + version: u64, + ) -> SpannedEncodingResult { + let name = format!("{name}$snapshot${version}"); + self.create_variable(name, ty) + } fn new_snapshot_variable_version( &mut self, variable: &vir_mid::VariableDecl, position: vir_low::Position, ) -> SpannedEncodingResult { - let new_version = self - .snapshots_state - .all_variables - .new_version_or_default(variable, position); - self.snapshots_state - .current_variables - .as_mut() - .unwrap() - .set(variable.name.clone(), new_version); - self.create_snapshot_variable(&variable.name, &variable.ty, new_version) + let ty = variable.ty.to_snapshot(self)?; + // let new_version = self.snapshots_state.all_variables.new_version_or_default( + // &variable.name, + // &ty, + // position, + // ); + // self.snapshots_state + // .current_variables + // .as_mut() + // .unwrap() + // .set(variable.name.clone(), new_version); + let new_version = + self.snapshots_state + .ssa_state + .new_variable_version(&variable.name, &ty, position); + self.create_snapshot_variable_low(&variable.name, ty, new_version) } fn current_snapshot_variable_version( &mut self, variable: &vir_mid::VariableDecl, ) -> SpannedEncodingResult { + // let version = self + // .snapshots_state + // .current_variables + // .as_ref() + // .unwrap() + // .get_or_default(&variable.name); let version = self .snapshots_state - .current_variables - .as_ref() - .unwrap() - .get_or_default(&variable.name); + .ssa_state + .current_variable_version(&variable.name); self.create_snapshot_variable(&variable.name, &variable.ty, version) } fn initial_snapshot_variable_version( @@ -342,45 +476,203 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotVariablesInterface for Lowerer<'p, 'v, 'tcx> variable: &vir_mid::VariableDecl, label: &str, ) -> SpannedEncodingResult { + // let version = self + // .snapshots_state + // .variables_at_label + // .get(label) + // .unwrap_or_else(|| panic!("not found label {}", label)) + // .get_or_default(&variable.name); let version = self .snapshots_state - .variables_at_label - .get(label) - .unwrap_or_else(|| panic!("not found label {label}")) - .get_or_default(&variable.name); + .ssa_state + .variable_version_at_label(&variable.name, label); self.create_snapshot_variable(&variable.name, &variable.ty, version) } + fn use_heap_variable(&self) -> SpannedEncodingResult { + // Ok(self.check_mode.unwrap().is_purification_group()) + // FIXME: Rename to unsafe_cell_values. + Ok(false) // FIXME: For now use only the heap-dependent proofs. + } + fn heap_variable_name(&self) -> SpannedEncodingResult<&'static str> { + assert!( + self.use_heap_variable()?, + "The heap variable is not used when the check mode is Both" + ); + Ok("heap$") + } + fn new_heap_variable_version( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult { + // let name = "heap$"; + let name = self.heap_variable_name()?; + let ty = self.heap_type()?; + let new_version = self + .snapshots_state + .ssa_state + .new_variable_version(name, &ty, position); + // let new_version = self + // .snapshots_state + // .all_variables + // .new_version_or_default(name, &ty, position); + // self.snapshots_state + // .current_variables + // .as_mut() + // .unwrap() + // .set(name.to_string(), new_version); + self.create_snapshot_variable_low(name, ty, new_version) + } + fn heap_variable_version_at_label( + &mut self, + old_label: &Option, + ) -> SpannedEncodingResult { + // let name = "heap$"; + let name = self.heap_variable_name()?; + let version = self + .snapshots_state + .ssa_state + .variable_version_at_maybe_label(name, old_label); + // let version = if let Some(label) = old_label { + // self.snapshots_state + // .variables_at_label + // .get(label) + // .unwrap_or_else(|| panic!("not found label {}", label)) + // .get_or_default(name) + // } else { + // self.snapshots_state + // .current_variables + // .as_ref() + // .unwrap() + // .get_or_default(name) + // }; + let ty = self.heap_type()?; + // let name = format!("{}${}", name, version); + // self.create_variable(name, ty) + self.create_snapshot_variable_low(name, ty, version) + } + fn address_variable_version_at_label( + &mut self, + variable_name: &str, + old_label: &Option, + ) -> SpannedEncodingResult { + let name = format!("{variable_name}$address"); + let version = self + .snapshots_state + .ssa_state + .variable_version_at_maybe_label(&name, old_label); + // let version = if let Some(label) = old_label { + // self.snapshots_state + // .variables_at_label + // .get(label) + // .unwrap_or_else(|| panic!("not found label {}", label)) + // .get_or_default(&name) + // } else { + // self.snapshots_state + // .current_variables + // .as_ref() + // .unwrap() + // .get_or_default(&name) + // }; + let ty = self.address_type()?; + self.create_snapshot_variable_low(&name, ty, version) + } + fn fresh_heap_chunk( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let name = "heap_chunk$"; + let ty = self.heap_chunk_type()?; + let new_version = self + .snapshots_state + .ssa_state + .new_variable_version(name, &ty, position); + // let new_version = self + // .snapshots_state + // .all_variables + // .new_version_or_default(name, &ty, position); + // self.snapshots_state + // .current_variables + // .as_mut() + // .unwrap() + // .set(name.to_string(), new_version); + // let name = format!("{}${}", name, new_version); + // self.create_variable(name, ty) + self.create_snapshot_variable_low(name, ty, new_version) + } fn encode_snapshot_havoc( &mut self, statements: &mut Vec, target: &vir_mid::Expression, position: vir_low::Position, - new_snapshot: Option, - ) -> SpannedEncodingResult<()> { + // new_snapshot_root: Option, + ) -> SpannedEncodingResult { + // let base = target.get_base(); + // self.ensure_type_definition(&base.ty)?; + // let old_snapshot = base.to_procedure_snapshot(self)?; + // let new_snapshot = if let Some(new_snapshot) = new_snapshot { + // new_snapshot + // } else { + // self.new_snapshot_variable_version(&base, position)? + // }; + // self.snapshot_copy_except(statements, old_snapshot, new_snapshot, target, position)?; + // Ok(()) let base = target.get_base(); self.ensure_type_definition(&base.ty)?; - let old_snapshot = base.to_procedure_snapshot(self)?; - let new_snapshot = if let Some(new_snapshot) = new_snapshot { - new_snapshot - } else { - self.new_snapshot_variable_version(&base, position)? - }; - self.snapshot_copy_except(statements, old_snapshot, new_snapshot, target, position)?; - Ok(()) + + // if let Some(pointer_place) = target.get_last_dereferenced_pointer() { + // let pointer_type = pointer_place.get_type().clone().unwrap_pointer(); + // let fresh_heap_chunk = self.fresh_heap_chunk()?; + // let heap_chunk = self.heap_chunk_to_snapshot( + // &pointer_type.target_type, + // fresh_heap_chunk.clone().into(), + // position, + // )?; + // let old_heap = self.heap_variable_version_at_label(&None)?; + // let new_heap = self.new_heap_variable_version(position)?; + // let address = + // self.pointer_address(pointer_place.get_type(), old_snapshot.clone().into(), position)?; + // statements.push(vir_low::Statement::assign( + // new_heap, + // vir_low::Expression::container_op( + // vir_low::ContainerOpKind::MapUpdate, + // self.heap_type()?, + // vec![old_heap.into(), address, fresh_heap_chunk.into()], + // position, + // ), + // position, + // )); + // let old_target_snapshot = + // self.pointer_target_snapshot(pointer_place.get_type(), &None, old_snapshot.into(), position)?; + // // Ok((old_target_snapshot, heap_chunk) + // let (_old_snapshot, new_snapshot) = + // self.snapshot_copy_except(statements, old_target_snapshot, heap_chunk, pointer_place, position)?; + // Ok(new_snapshot) + // } else { + + // let (_old_snapshot, new_snapshot) = + // self.snapshot_copy_except(statements, old_snapshot, new_snapshot, target, position)?; + let (_old_snapshot, new_snapshot) = + self.snapshot_copy_except(statements, base, target, position)?; + Ok(new_snapshot) + // } } + /// `new_snapshot_root` is used when we want to use a specific variable + /// version as the root of the new snapshot. fn encode_snapshot_update_with_new_snapshot( &mut self, statements: &mut Vec, target: &vir_mid::Expression, value: vir_low::Expression, position: vir_low::Position, - new_snapshot: Option, - ) -> SpannedEncodingResult<()> { + // new_snapshot_root: Option, + ) -> SpannedEncodingResult { use vir_low::macros::*; - self.encode_snapshot_havoc(statements, target, position, new_snapshot)?; - statements - .push(stmtp! { position => assume ([target.to_procedure_snapshot(self)?] == [value]) }); - Ok(()) + // self.encode_snapshot_havoc(statements, target, position, new_snapshot)?; + // statements + // .push(stmtp! { position => assume ([target.to_procedure_snapshot(self)?] == [value]) }); + let new_snapshot = self.encode_snapshot_havoc(statements, target, position)?; + statements.push(stmtp! { position => assume ([new_snapshot.clone()] == [value]) }); + Ok(new_snapshot) } fn encode_snapshot_update( &mut self, @@ -389,7 +681,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotVariablesInterface for Lowerer<'p, 'v, 'tcx> value: vir_low::Expression, position: vir_low::Position, ) -> SpannedEncodingResult<()> { - self.encode_snapshot_update_with_new_snapshot(statements, target, value, position, None) + self.encode_snapshot_update_with_new_snapshot(statements, target, value, position)?; + Ok(()) } /// `basic_block_edges` are statements to be executed then going from one /// block to another. @@ -399,73 +692,88 @@ impl<'p, 'v: 'p, 'tcx: 'v> SnapshotVariablesInterface for Lowerer<'p, 'v, 'tcx> predecessors: &BTreeMap>, basic_block_edges: &mut BTreeMap< vir_mid::BasicBlockId, - BTreeMap>, + BTreeMap< + vir_mid::BasicBlockId, + Vec<(String, vir_low::Type, vir_low::Position, u64, u64)>, + >, >, + // basic_block_edges: &mut BTreeMap< + // vir_mid::BasicBlockId, + // BTreeMap>, + // >, ) -> SpannedEncodingResult<()> { - let predecessor_labels = &predecessors[label]; - let mut new_map = VariableVersionMap::default(); - for variable in self.snapshots_state.all_variables.names_clone() { - let predecessor_maps = predecessor_labels - .iter() - .map(|label| &self.snapshots_state.variables[label]) - .collect::>(); - let first_version = predecessor_maps[0].get_or_default(&variable); - let different = predecessor_maps - .iter() - .any(|map| map.get_or_default(&variable) != first_version); - if different { - let new_version = self.snapshots_state.all_variables.new_version(&variable); - let ty = self - .snapshots_state - .all_variables - .get_type(&variable) - .clone(); - let new_variable = self.create_snapshot_variable(&variable, &ty, new_version)?; - for predecessor_label in predecessor_labels { - let old_version = - self.snapshots_state.variables[predecessor_label].get_or_default(&variable); - let statements = basic_block_edges - .entry(predecessor_label.clone()) - .or_default() - .entry(label.clone()) - .or_default(); - let old_variable = - self.create_snapshot_variable(&variable, &ty, old_version)?; - let position = self.encoder.change_error_context( - // FIXME: Get a more precise span. - self.snapshots_state.all_variables.get_position(&variable), - ErrorCtxt::Unexpected, - ); - let statement = vir_low::macros::stmtp! { position => assume (new_variable == old_variable) }; - statements.push(statement); - } - new_map.set(variable, new_version); - } else { - new_map.set(variable, first_version); - } - } - self.snapshots_state.current_variables = Some(new_map); + self.snapshots_state.ssa_state.prepare_new_current_block( + label, + predecessors, + basic_block_edges, + ); + // let predecessor_labels = &predecessors[label]; + // let mut new_map = VariableVersionMap::default(); + // for variable in self.snapshots_state.all_variables.names_clone() { + // let predecessor_maps = predecessor_labels + // .iter() + // .map(|label| &self.snapshots_state.variables[label]) + // .collect::>(); + // let first_version = predecessor_maps[0].get_or_default(&variable); + // let different = predecessor_maps + // .iter() + // .any(|map| map.get_or_default(&variable) != first_version); + // if different { + // let new_version = self.snapshots_state.all_variables.new_version(&variable); + // let ty = self + // .snapshots_state + // .all_variables + // .get_type(&variable) + // .clone(); + // let new_variable = + // self.create_snapshot_variable_low(&variable, ty.clone(), new_version)?; + // for predecessor_label in predecessor_labels { + // let old_version = + // self.snapshots_state.variables[predecessor_label].get_or_default(&variable); + // let statements = basic_block_edges + // .entry(predecessor_label.clone()) + // .or_default() + // .entry(label.clone()) + // .or_default(); + // let old_variable = + // self.create_snapshot_variable_low(&variable, ty.clone(), old_version)?; + // let position = self.encoder.change_error_context( + // // FIXME: Get a more precise span. + // self.snapshots_state.all_variables.get_position(&variable), + // ErrorCtxt::Unexpected, + // ); + // let statement = vir_low::macros::stmtp! { position => assume (new_variable == old_variable) }; + // statements.push(statement); + // } + // new_map.set(variable, new_version); + // } else { + // new_map.set(variable, first_version); + // } + // } + // self.snapshots_state.current_variables = Some(new_map); Ok(()) } fn unset_current_block_for_snapshots( &mut self, label: vir_mid::BasicBlockId, ) -> SpannedEncodingResult<()> { - let current_variables = self.snapshots_state.current_variables.take().unwrap(); - assert!(self - .snapshots_state - .variables - .insert(label, current_variables) - .is_none()); + self.snapshots_state.ssa_state.finish_current_block(label); + // let current_variables = self.snapshots_state.current_variables.take().unwrap(); + // assert!(self + // .snapshots_state + // .variables + // .insert(label, current_variables) + // .is_none()); Ok(()) } fn save_old_label(&mut self, label: String) -> SpannedEncodingResult<()> { - let current_variables = self.snapshots_state.current_variables.clone().unwrap(); - assert!(self - .snapshots_state - .variables_at_label - .insert(label, current_variables) - .is_none()); + self.snapshots_state.ssa_state.save_state_at_label(label); + // let current_variables = self.snapshots_state.current_variables.clone().unwrap(); + // assert!(self + // .snapshots_state + // .variables_at_label + // .insert(label, current_variables) + // .is_none()); Ok(()) } } diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/mod.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/mod.rs index eb61a1f4135..32086242768 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/mod.rs @@ -4,4 +4,3 @@ mod interface; mod state; pub(in super::super) use self::interface::SnapshotVariablesInterface; -pub(super) use self::state::{AllVariablesMap, VariableVersionMap}; diff --git a/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/state.rs b/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/state.rs index f60795708a3..7b757ae4c3e 100644 --- a/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/state.rs +++ b/prusti-viper/src/encoder/middle/core_proof/snapshots/variables/state.rs @@ -1,11 +1,8 @@ use std::collections::BTreeMap; -use vir_crate::{ - low::{self as vir_low}, - middle::{self as vir_mid}, -}; +use vir_crate::low::{self as vir_low}; #[derive(Default, Clone)] -pub(in super::super) struct VariableVersionMap { +pub(in super::super::super) struct VariableVersionMap { /// Mapping from variable names to their versions. variable_versions: BTreeMap, } @@ -31,9 +28,9 @@ impl VariableVersionMap { } #[derive(Default)] -pub(in super::super) struct AllVariablesMap { +pub(in super::super::super) struct AllVariablesMap { versions: BTreeMap, - types: BTreeMap, + types: BTreeMap, positions: BTreeMap, } @@ -41,7 +38,7 @@ impl AllVariablesMap { pub(super) fn names_clone(&self) -> Vec { self.versions.keys().cloned().collect() } - pub(super) fn get_type(&self, variable: &str) -> &vir_mid::Type { + pub(super) fn get_type(&self, variable: &str) -> &vir_low::Type { &self.types[variable] } pub(super) fn get_position(&self, variable: &str) -> vir_low::Position { @@ -54,18 +51,18 @@ impl AllVariablesMap { } pub(super) fn new_version_or_default( &mut self, - variable: &vir_mid::VariableDecl, + variable: &str, + ty: &vir_low::Type, position: vir_low::Position, ) -> u64 { - if self.versions.contains_key(&variable.name) { - let version = self.versions.get_mut(&variable.name).unwrap(); + if self.versions.contains_key(variable) { + let version = self.versions.get_mut(variable).unwrap(); *version += 1; *version } else { - self.versions.insert(variable.name.clone(), 1); - self.types - .insert(variable.name.clone(), variable.ty.clone()); - self.positions.insert(variable.name.clone(), position); + self.versions.insert(variable.to_string(), 1); + self.types.insert(variable.to_string(), ty.clone()); + self.positions.insert(variable.to_string(), position); 1 } } diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/effects/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/effects/mod.rs new file mode 100644 index 00000000000..bdbfdfbb47a --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/effects/mod.rs @@ -0,0 +1,538 @@ +use super::{ + permission_mask::{ + PermissionMaskKind, PermissionMaskKindAliasedBool, + PermissionMaskKindAliasedFractionalBoundedPerm, PermissionMaskOperations, + PredicatePermissionMaskKind, TPermissionMaskOperations, + }, + HeapEncoder, +}; +use crate::encoder::errors::SpannedEncodingResult; +use vir_crate::{ + common::expression::{BinaryOperationHelpers, SyntacticEvaluation}, + low::{self as vir_low}, +}; + +impl<'p, 'v: 'p, 'tcx: 'v> HeapEncoder<'p, 'v, 'tcx> { + /// Note: this method assumes that it is called only as a top level assert + /// because it performs creating a new permission mask and rolling it back. + /// + /// Note: this method also evaluates accessibility predicates in + /// `expression_evaluation_state_label`. + pub(super) fn encode_expression_assert( + &mut self, + statements: &mut Vec, + expression: vir_low::Expression, + position: vir_low::Position, + expression_evaluation_state_label: Option, + ) -> SpannedEncodingResult<()> { + assert!(!position.is_default(), "expression: {expression}"); + if expression.is_pure() { + let expression = self.encode_pure_expression( + statements, + expression, + expression_evaluation_state_label, + position, + )?; + statements.push(vir_low::Statement::assert(expression, position)); + } else { + let check_point = self.fresh_label(); + self.ssa_state.save_state_at_label(check_point.clone()); + let evaluation_state = if let Some(label) = &expression_evaluation_state_label { + // This call is needed because we want to evaluate the + // accessibility predicates in the specified state. + self.ssa_state.change_state_to_label(label); + label + } else { + &check_point + }; + self.encode_expression_exhale(statements, expression, position, evaluation_state)?; + self.ssa_state.change_state_to_label(&check_point); + } + Ok(()) + } + + /// This method is similar to `encode_expression_assert` but it is intended + /// for asserting function preconditions. The key difference between + /// asserting function preconditions and regular assert statements is that + /// in function preconditions we ignore the permission amounts used in the + /// accessibility predicates: we only check that the permission amounts are + /// positive. + pub(super) fn encode_function_precondition_assert( + &mut self, + statements: &mut Vec, + expression: vir_low::Expression, + position: vir_low::Position, + expression_evaluation_state_label: Option, + ) -> SpannedEncodingResult<()> { + assert!(!position.is_default(), "expression: {expression}"); + if expression.is_pure() { + let expression = self.encode_pure_expression( + statements, + expression, + expression_evaluation_state_label, + position, + )?; + statements.push(vir_low::Statement::assert(expression, position)); + } else { + let check_point = self.fresh_label(); + self.ssa_state.save_state_at_label(check_point.clone()); + let evaluation_state = if let Some(label) = &expression_evaluation_state_label { + // This call is needed because we want to evaluate the + // accessibility predicates in the specified state. + self.ssa_state.change_state_to_label(label); + label + } else { + &check_point + }; + self.encode_function_precondition_assert_rec( + statements, + expression, + position, + evaluation_state, + )?; + self.ssa_state.change_state_to_label(&check_point); + } + Ok(()) + } + + pub(super) fn encode_function_precondition_assert_rec( + &mut self, + statements: &mut Vec, + expression: vir_low::Expression, + position: vir_low::Position, + expression_evaluation_state_label: &str, + ) -> SpannedEncodingResult<()> { + assert!(!position.is_default(), "expression: {expression}"); + if expression.is_pure() { + let expression = self.encode_pure_expression( + statements, + expression, + Some(expression_evaluation_state_label.to_string()), + position, + )?; + statements.push(vir_low::Statement::assert(expression, position)); + } else { + match expression { + vir_low::Expression::PredicateAccessPredicate(expression) => { + // FIXME: evaluate predicate arguments in expression_evaluation_state_label + match self.get_predicate_permission_mask_kind(&expression.name)? { + PredicatePermissionMaskKind::AliasedWholeBool + | PredicatePermissionMaskKind::AliasedFractionalBool => { + let operations = + PermissionMaskOperations::::new( + self, + statements, + &expression, + Some(expression_evaluation_state_label.to_string()), + position, + )?; + self.encode_function_precondition_assert_rec_predicate( + statements, + &expression, + position, + operations, + )? + } + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm => { + let operations = PermissionMaskOperations::< + PermissionMaskKindAliasedFractionalBoundedPerm, + >::new( + self, + statements, + &expression, + Some(expression_evaluation_state_label.to_string()), + position, + )?; + self.encode_function_precondition_assert_rec_predicate( + statements, + &expression, + position, + operations, + )? + } + } + } + vir_low::Expression::Unfolding(_) => todo!(), + vir_low::Expression::LabelledOld(_) => todo!(), + vir_low::Expression::BinaryOp(expression) => match expression.op_kind { + vir_low::BinaryOpKind::And => { + self.encode_function_precondition_assert_rec( + statements, + *expression.left, + position, + expression_evaluation_state_label, + )?; + self.encode_function_precondition_assert_rec( + statements, + *expression.right, + position, + expression_evaluation_state_label, + )?; + } + vir_low::BinaryOpKind::Implies if expression.left.is_true() => { + self.encode_function_precondition_assert_rec( + statements, + *expression.right, + position, + expression_evaluation_state_label, + )?; + } + vir_low::BinaryOpKind::Implies => { + unimplemented!(); + } + _ => unreachable!("expression: {}", expression), + }, + vir_low::Expression::Conditional(_) => todo!(), + vir_low::Expression::FuncApp(_) => todo!(), + vir_low::Expression::DomainFuncApp(_) => todo!(), + _ => { + unimplemented!("expression: {:?}", expression); + } + } + } + Ok(()) + } + + fn encode_function_precondition_assert_rec_predicate( + &mut self, + statements: &mut Vec, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + position: vir_low::Position, + operations: PermissionMaskOperations, + ) -> SpannedEncodingResult<()> + where + PermissionMaskOperations: TPermissionMaskOperations, + { + statements.push(vir_low::Statement::comment(format!( + "assert-function-precondition-predicate {predicate}" + ))); + // assert perm

(r1, r2, v_old) >= p + statements.push(vir_low::Statement::assert( + operations.perm_old_positive(), + position, // FIXME: use position of expression.permission with proper ErrorCtxt. + )); + Ok(()) + } + + pub(super) fn encode_expression_exhale( + &mut self, + statements: &mut Vec, + expression: vir_low::Expression, + position: vir_low::Position, + expression_evaluation_state_label: &str, + ) -> SpannedEncodingResult<()> { + assert!(!position.is_default(), "expression: {expression}"); + if expression.is_pure() { + let expression = self.encode_pure_expression( + statements, + expression, + Some(expression_evaluation_state_label.to_string()), + position, + )?; + statements.push(vir_low::Statement::assert(expression, position)); + } else { + match expression { + vir_low::Expression::PredicateAccessPredicate(expression) => { + // FIXME: evaluate predicate arguments in expression_evaluation_state_label + match self.get_predicate_permission_mask_kind(&expression.name)? { + PredicatePermissionMaskKind::AliasedWholeBool + | PredicatePermissionMaskKind::AliasedFractionalBool => { + let operations = + PermissionMaskOperations::::new( + self, + statements, + &expression, + Some(expression_evaluation_state_label.to_string()), + position, + )?; + self.encode_expression_exhale_predicate( + statements, + &expression, + position, + Some(expression_evaluation_state_label.to_string()), + operations, + )? + } + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm => { + let operations = PermissionMaskOperations::< + PermissionMaskKindAliasedFractionalBoundedPerm, + >::new( + self, + statements, + &expression, + Some(expression_evaluation_state_label.to_string()), + position, + )?; + self.encode_expression_exhale_predicate( + statements, + &expression, + position, + Some(expression_evaluation_state_label.to_string()), + operations, + )? + } + } + } + vir_low::Expression::Unfolding(_) => todo!(), + vir_low::Expression::LabelledOld(_) => todo!(), + vir_low::Expression::BinaryOp(expression) => match expression.op_kind { + vir_low::BinaryOpKind::And => { + self.encode_expression_exhale( + statements, + *expression.left, + position, + expression_evaluation_state_label, + )?; + self.encode_expression_exhale( + statements, + *expression.right, + position, + expression_evaluation_state_label, + )?; + } + vir_low::BinaryOpKind::Implies if expression.left.is_true() => { + self.encode_expression_exhale( + statements, + *expression.right, + position, + expression_evaluation_state_label, + )?; + } + vir_low::BinaryOpKind::Implies => { + unimplemented!("Merge the heap versions in the commented out code below."); + // let guard = self.encode_pure_expression( + // statements, + // *expression.left, + // Some(expression_evaluation_state_label.to_string()), + // position, + // )?; + // let mut body = Vec::new(); + // self.encode_expression_exhale( + // &mut body, + // *expression.right, + // position, + // expression_evaluation_state_label, + // )?; + // // FIXME: Permission mask and heap versions need to be + // // unified after the branch merge. + // statements.push(vir_low::Statement::conditional( + // guard, + // body, + // Vec::new(), + // position, + // )) + } + _ => unreachable!("expression: {}", expression), + }, + vir_low::Expression::Conditional(_) => todo!(), + vir_low::Expression::FuncApp(_) => todo!(), + vir_low::Expression::DomainFuncApp(_) => todo!(), + _ => { + unimplemented!("expression: {:?}", expression); + } + } + } + Ok(()) + } + + fn encode_expression_exhale_predicate( + &mut self, + statements: &mut Vec, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + position: vir_low::Position, + expression_evaluation_state_label: Option, + operations: PermissionMaskOperations, + ) -> SpannedEncodingResult<()> + where + PermissionMaskOperations: TPermissionMaskOperations, + { + statements.push(vir_low::Statement::comment(format!( + "exhale-predicate {predicate}" + ))); + // assert perm

(r1, r2, v_old) >= p + statements.push(vir_low::Statement::assert( + operations.perm_old_greater_equals(&predicate.permission), + position, // FIXME: use position of expression.permission with proper ErrorCtxt. + )); + let perm_new_value = operations.perm_old_sub(&predicate.permission); + // assume perm

(r1, r2, v_new) == perm

(r1, r2, v_old) - p + statements.push(vir_low::Statement::assume( + vir_low::Expression::equals(operations.perm_new(), perm_new_value.clone()), + position, // FIXME: use position of expression.permission with proper ErrorCtxt. + )); + // assume forall arg1: Ref, arg2: Ref :: + // {perm

(arg1, arg2, v_new)} + // !(r1 == arg1 && r2 == arg2) ==> + // perm

(arg1, arg2, v_new) == perm

(arg1, arg2, v_old) + self.encode_perm_unchanged_quantifier( + statements, + predicate, + operations.old_permission_mask_version(), + operations.new_permission_mask_version(), + position, + expression_evaluation_state_label, + perm_new_value, + )?; + // assume forall arg1: Ref, arg2: Ref :: + // {heap

(arg1, arg2, vh_new)} + // perm

(arg1, arg2, v_new) > 0 ==> + // heap

(arg1, arg2, vh_new) == heap

(arg1, arg2, vh_old) + self.encode_heap_unchanged_quantifier( + statements, + predicate, + operations.new_permission_mask_version(), + position, + )?; + Ok(()) + } + + pub(super) fn encode_expression_inhale( + &mut self, + statements: &mut Vec, + expression: vir_low::Expression, + position: vir_low::Position, + expression_evaluation_state_label: Option, + ) -> SpannedEncodingResult<()> { + if expression.is_pure() { + let expression = self.encode_pure_expression( + statements, + expression, + expression_evaluation_state_label, + position, + )?; + statements.push(vir_low::Statement::assume(expression, position)); + } else { + match expression { + vir_low::Expression::PredicateAccessPredicate(expression) => { + match self.get_predicate_permission_mask_kind(&expression.name)? { + PredicatePermissionMaskKind::AliasedWholeBool + | PredicatePermissionMaskKind::AliasedFractionalBool => { + let operations = + PermissionMaskOperations::::new( + self, + statements, + &expression, + expression_evaluation_state_label.clone(), + position, + )?; + self.encode_expression_inhale_predicate( + statements, + &expression, + position, + expression_evaluation_state_label, + operations, + )? + } + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm => { + let operations = PermissionMaskOperations::< + PermissionMaskKindAliasedFractionalBoundedPerm, + >::new( + self, + statements, + &expression, + expression_evaluation_state_label.clone(), + position, + )?; + self.encode_expression_inhale_predicate( + statements, + &expression, + position, + expression_evaluation_state_label, + operations, + )? + } + } + } + vir_low::Expression::Unfolding(_) => todo!(), + vir_low::Expression::LabelledOld(_) => todo!(), + vir_low::Expression::BinaryOp(expression) => match expression.op_kind { + vir_low::BinaryOpKind::And => { + self.encode_expression_inhale( + statements, + *expression.left, + position, + expression_evaluation_state_label.clone(), + )?; + self.encode_expression_inhale( + statements, + *expression.right, + position, + expression_evaluation_state_label, + )?; + } + vir_low::BinaryOpKind::Implies => { + let guard = self.encode_pure_expression( + statements, + *expression.left, + expression_evaluation_state_label.clone(), + position, + )?; + let mut body = Vec::new(); + self.encode_expression_inhale( + &mut body, + *expression.right, + position, + expression_evaluation_state_label, + )?; + statements.push(vir_low::Statement::conditional( + guard, + body, + Vec::new(), + position, + )) + } + _ => unreachable!("expression: {}", expression), + }, + vir_low::Expression::Conditional(_) => todo!(), + vir_low::Expression::FuncApp(_) => todo!(), + vir_low::Expression::DomainFuncApp(_) => todo!(), + _ => { + unimplemented!("expression: {:?}", expression); + } + } + } + Ok(()) + } + + fn encode_expression_inhale_predicate( + &mut self, + statements: &mut Vec, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + position: vir_low::Position, + expression_evaluation_state_label: Option, + operations: PermissionMaskOperations, + ) -> SpannedEncodingResult<()> + where + PermissionMaskOperations: TPermissionMaskOperations, + { + statements.push(vir_low::Statement::comment(format!( + "inhale-predicate {predicate}" + ))); + if operations.can_assume_old_permission_is_none(&predicate.permission) { + statements.push(vir_low::Statement::assume( + operations.perm_old_equal_none(), + position, // FIXME: use position of expression.permission with proper ErrorCtxt. + )); + } + let perm_new_value = operations.perm_old_add(&predicate.permission); + // assume perm

(r1, r2, v_new) == perm

(r1, r2, v_old) + p + statements.push(vir_low::Statement::assume( + vir_low::Expression::equals(operations.perm_new(), perm_new_value.clone()), + position, // FIXME: use position of expression.permission with proper ErrorCtxt. + )); + // assume forall arg1: Ref, arg2: Ref :: + // {perm

(arg1, arg2, v_new)} + // !(r1 == arg1 && r2 == arg2) ==> + // perm

(arg1, arg2, v_new) == perm

(arg1, arg2, v_old) + self.encode_perm_unchanged_quantifier( + statements, + predicate, + operations.old_permission_mask_version(), + operations.new_permission_mask_version(), + position, + expression_evaluation_state_label, + perm_new_value, + )?; + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/heap/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/heap/mod.rs new file mode 100644 index 00000000000..9f99aa84493 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/heap/mod.rs @@ -0,0 +1,150 @@ +use super::HeapEncoder; +use crate::encoder::errors::SpannedEncodingResult; +use vir_crate::{ + common::expression::{BinaryOperationHelpers, QuantifierHelpers}, + low::{self as vir_low}, +}; + +impl<'p, 'v: 'p, 'tcx: 'v> HeapEncoder<'p, 'v, 'tcx> { + fn heap_version_type(&self) -> vir_low::Type { + vir_low::Type::domain("HeapVersion".to_string()) + } + + pub(super) fn heap_function_name(&self, predicate_name: &str) -> String { + format!("heap${predicate_name}") + } + + pub(super) fn heap_call( + &mut self, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + mut arguments: Vec, + heap_version: vir_low::Expression, + ) -> SpannedEncodingResult> { + let call = + if let Some(snapshot_type) = self.get_snapshot_type_for_predicate(&predicate.name) { + let heap_function_name = self.heap_function_name(&predicate.name); + arguments.push(heap_version); + Some(vir_low::Expression::domain_function_call( + "HeapFunctions", + heap_function_name, + arguments, + snapshot_type, + )) + } else { + None + }; + Ok(call) + } + + pub(super) fn heap_call_for_predicate_def( + &mut self, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + heap_version: vir_low::Expression, + ) -> SpannedEncodingResult> { + let arguments = self.get_predicate_parameters_as_arguments(&predicate.name)?; + self.heap_call(predicate, arguments, heap_version) + } + + pub(super) fn encode_heap_unchanged_quantifier( + &mut self, + statements: &mut Vec, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + new_permission_mask: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + let heap_version_old = self.get_current_heap_version_for(&predicate.name)?; + if let Some(heap_old) = self.heap_call_for_predicate_def(predicate, heap_version_old)? { + let heap_version_new = self.get_new_heap_version_for(&predicate.name, position)?; + let heap_new = self + .heap_call_for_predicate_def(predicate, heap_version_new)? + .unwrap(); + let predicate_parameters = self.get_predicate_parameters(&predicate.name).to_owned(); + let triggers = vec![vir_low::Trigger::new(vec![heap_new.clone()])]; + let guard = self + .positive_permission_mask_call_for_predicate_def(predicate, new_permission_mask)?; + let body = vir_low::Expression::implies( + guard, + vir_low::Expression::equals(heap_old, heap_new), + ); + statements.push(vir_low::Statement::assume( + vir_low::Expression::forall(predicate_parameters, triggers, body), + position, + )); + } + Ok(()) + } + + pub(super) fn get_current_heap_version_for( + &mut self, + predicate_name: &str, + ) -> SpannedEncodingResult { + let variable_name = self.heap_names.get(predicate_name).unwrap(); + let version = self.ssa_state.current_variable_version(variable_name); + let ty = self.heap_version_type(); + Ok(self + .new_variables + .create_variable(variable_name, ty, version)? + .into()) + } + + fn get_new_heap_version_for( + &mut self, + predicate_name: &str, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let variable_name = self.heap_names.get(predicate_name).unwrap(); + let ty = self.heap_version_type(); + let version = self + .ssa_state + .new_variable_version(variable_name, &ty, position); + Ok(self + .new_variables + .create_variable(variable_name, ty, version)? + .into()) + } + + pub(super) fn get_heap_version_at_label( + &mut self, + predicate_name: &str, + label: &str, + ) -> SpannedEncodingResult { + let variable_name = self.heap_names.get(predicate_name).unwrap(); + let version = self + .ssa_state + .variable_version_at_label(variable_name, label); + let ty = self.heap_version_type(); + Ok(self + .new_variables + .create_variable(variable_name, ty, version)? + .into()) + } + + pub(super) fn generate_heap_domains( + &self, + domains: &mut Vec, + ) -> SpannedEncodingResult<()> { + let heap_version_domain = + vir_low::DomainDecl::new("HeapVersion", Vec::new(), Vec::new(), Vec::new()); + domains.push(heap_version_domain); + let mut functions = Vec::new(); + for predicate in self.predicates.iter_decls() { + if let Some(snapshot_type) = self.get_snapshot_type_for_predicate(&predicate.name) { + let mut parameters = predicate.parameters.clone(); + parameters.push(vir_low::VariableDecl::new( + "version", + self.heap_version_type(), + )); + functions.push(vir_low::DomainFunctionDecl::new( + self.heap_function_name(&predicate.name), + false, + parameters, + snapshot_type, + )); + } + } + let heap_domain = + vir_low::DomainDecl::new("HeapFunctions", functions, Vec::new(), Vec::new()); + domains.push(heap_domain); + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/mod.rs new file mode 100644 index 00000000000..c1170ff5ddc --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/mod.rs @@ -0,0 +1,137 @@ +mod statements; +mod pure_expressions; +mod heap; +mod effects; +mod predicates; +mod permission_mask; + +use self::predicates::Predicates; +use super::variable_declarations::VariableDeclarations; +use crate::encoder::{ + errors::SpannedEncodingResult, middle::core_proof::predicates::OwnedPredicateInfo, Encoder, +}; +use rustc_hash::{FxHashMap, FxHashSet}; +use std::collections::BTreeMap; +use vir_crate::low::{self as vir_low}; + +pub(super) struct HeapEncoder<'p, 'v: 'p, 'tcx: 'v> { + encoder: &'p mut Encoder<'v, 'tcx>, + new_variables: VariableDeclarations, + predicates: Predicates<'p>, + functions: FxHashMap, + ssa_state: vir_low::ssa::SSAState, + permission_mask_names: FxHashMap, + heap_names: FxHashMap, + /// A counter used for generating fresh labels. + fresh_label_counter: u64, +} + +impl<'p, 'v: 'p, 'tcx: 'v> HeapEncoder<'p, 'v, 'tcx> { + pub(super) fn new( + encoder: &'p mut Encoder<'v, 'tcx>, + predicates: &'p [vir_low::PredicateDecl], + predicate_info: BTreeMap, + functions: &'p [vir_low::FunctionDecl], + ) -> Self { + Self { + encoder, + new_variables: Default::default(), + permission_mask_names: predicates + .iter() + .map(|predicate| { + let mask_name = format!("perm${}", predicate.name); + (predicate.name.clone(), mask_name) + }) + .collect(), + heap_names: predicates + .iter() + .map(|predicate| { + let heap_name = format!("heap${}", predicate.name); + (predicate.name.clone(), heap_name) + }) + .collect(), + predicates: Predicates::new(predicates, predicate_info), + functions: functions + .iter() + .map(|function| (function.name.clone(), function)) + .collect(), + ssa_state: Default::default(), + fresh_label_counter: 0, + } + } + + pub(super) fn reset(&mut self) { + self.new_variables = Default::default(); + self.ssa_state = Default::default(); + self.fresh_label_counter = 0; + } + + pub(super) fn encode_statement( + &mut self, + statements: &mut Vec, + statement: vir_low::Statement, + ) -> SpannedEncodingResult<()> { + self.encode_statement_internal(statements, statement) + } + + pub(super) fn prepare_new_current_block( + &mut self, + label: &vir_low::Label, + predecessors: &BTreeMap>, + basic_block_edges: &mut BTreeMap< + vir_low::Label, + BTreeMap>, + >, + ) -> SpannedEncodingResult<()> { + self.ssa_state + .prepare_new_current_block(label, predecessors, basic_block_edges); + Ok(()) + } + + pub(super) fn finish_current_block( + &mut self, + label: vir_low::Label, + ) -> SpannedEncodingResult<()> { + self.ssa_state.finish_current_block(label); + Ok(()) + } + + pub(super) fn generate_init_permissions_to_zero( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult> { + self.generate_init_permissions_to_zero_internal(position) + } + + pub(super) fn generate_necessary_domains( + &self, + ) -> SpannedEncodingResult> { + let mut domains = Vec::new(); + self.generate_permission_mask_domains(&mut domains)?; + self.generate_heap_domains(&mut domains)?; + Ok(domains) + } + + pub(super) fn create_variable( + &mut self, + variable_name: &str, + ty: vir_low::Type, + version: u64, + ) -> SpannedEncodingResult { + self.new_variables + .create_variable(variable_name, ty, version) + } + + pub(super) fn take_variables(&mut self) -> FxHashSet { + self.new_variables.take_variables() + } + + pub(super) fn encoder(&mut self) -> &mut Encoder<'v, 'tcx> { + self.encoder + } + + fn fresh_label(&mut self) -> String { + self.fresh_label_counter += 1; + format!("heap_label${}", self.fresh_label_counter) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/permission_mask/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/permission_mask/mod.rs new file mode 100644 index 00000000000..0cf27d21fb2 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/permission_mask/mod.rs @@ -0,0 +1,318 @@ +mod operations; + +use super::HeapEncoder; +use crate::encoder::errors::SpannedEncodingResult; +use vir_crate::{ + common::expression::{BinaryOperationHelpers, ExpressionIterator, QuantifierHelpers}, + low::{self as vir_low}, +}; + +pub(super) use self::operations::{ + PermissionMaskKind, PermissionMaskKindAliasedBool, + PermissionMaskKindAliasedFractionalBoundedPerm, PermissionMaskOperations, + TPermissionMaskOperations, +}; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub(super) enum PredicatePermissionMaskKind { + /// The permission amounts can be either full or none. + AliasedWholeBool, + /// The permission amounts can be fractional, but we are always guaranteed + /// to operate on the same amount. Therefore, we do not need to perform + /// arithmetic operations on permissions and can use a boolean permission + /// mask with a third parameter that specifies the permission amount that we + /// are currently tracking. + AliasedFractionalBool, + /// The permission amounts can be fractional and we need to perform + /// arithmetic operations on them. However, the permission amount is bounded + /// by `write` and, therefore, when inhaling `write` we can assume that the + /// current amount is `none`. + AliasedFractionalBoundedPerm, +} + +impl<'p, 'v: 'p, 'tcx: 'v> HeapEncoder<'p, 'v, 'tcx> { + fn perm_version_type(&self) -> vir_low::Type { + vir_low::Type::domain("PermVersion".to_string()) + } + + fn mask_function_return_type(&self, kind: PredicatePermissionMaskKind) -> vir_low::Type { + match kind { + PredicatePermissionMaskKind::AliasedWholeBool + | PredicatePermissionMaskKind::AliasedFractionalBool => vir_low::Type::Bool, + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm => vir_low::Type::Perm, + } + } + + fn no_permission(&self, kind: PredicatePermissionMaskKind) -> vir_low::Expression { + match kind { + PredicatePermissionMaskKind::AliasedWholeBool + | PredicatePermissionMaskKind::AliasedFractionalBool => false.into(), + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm => { + vir_low::Expression::no_permission() + } + } + } + + fn permission_amount_parameter( + &self, + kind: PredicatePermissionMaskKind, + ) -> Option { + match kind { + PredicatePermissionMaskKind::AliasedFractionalBool => Some(vir_low::VariableDecl::new( + "permission_amount", + vir_low::Type::Perm, + )), + PredicatePermissionMaskKind::AliasedWholeBool + | PredicatePermissionMaskKind::AliasedFractionalBoundedPerm => None, + } + } + + fn permission_mask_function_name(&self, predicate_name: &str) -> String { + format!("perm${predicate_name}") + } + + pub(super) fn get_current_permission_mask_for( + &mut self, + predicate_name: &str, + ) -> SpannedEncodingResult { + let variable_name = self.permission_mask_names.get(predicate_name).unwrap(); + let version = self.ssa_state.current_variable_version(variable_name); + let ty = self.perm_version_type(); + Ok(self + .new_variables + .create_variable(variable_name, ty, version)? + .into()) + } + + pub(super) fn get_new_permission_mask_for( + &mut self, + predicate_name: &str, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let variable_name = self.permission_mask_names.get(predicate_name).unwrap(); + let ty = self.perm_version_type(); + let version = self + .ssa_state + .new_variable_version(variable_name, &ty, position); + Ok(self + .new_variables + .create_variable(variable_name, ty, version)? + .into()) + } + + pub(super) fn permission_mask_call( + &mut self, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + mut arguments: Vec, + permission_mask_version: vir_low::Expression, + ) -> SpannedEncodingResult { + let perm_function_name = self.permission_mask_function_name(&predicate.name); + arguments.push(permission_mask_version); + let kind = self.get_predicate_permission_mask_kind(&predicate.name)?; + if kind == PredicatePermissionMaskKind::AliasedFractionalBool { + arguments.push((*predicate.permission).clone()); + } + let return_type = self.mask_function_return_type(kind); + Ok(vir_low::Expression::domain_function_call( + "PermFunctions", + perm_function_name, + arguments, + return_type, + )) + } + + pub(super) fn permission_mask_call_for_predicate_use( + &mut self, + statements: &mut Vec, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + permission_mask: vir_low::Expression, + expression_evaluation_state_label: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let arguments = self.purify_predicate_arguments( + statements, + predicate, + expression_evaluation_state_label, + position, + )?; + self.permission_mask_call(predicate, arguments, permission_mask) + } + + pub(super) fn permission_mask_call_for_predicate_def( + &mut self, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + permission_mask: vir_low::Expression, + ) -> SpannedEncodingResult { + let arguments = self.get_predicate_parameters_as_arguments(&predicate.name)?; + self.permission_mask_call(predicate, arguments, permission_mask) + } + + pub(super) fn positive_permission_mask_call_for_predicate_def( + &mut self, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + permission_mask: vir_low::Expression, + ) -> SpannedEncodingResult { + let perm_call = self.permission_mask_call_for_predicate_def(predicate, permission_mask)?; + let kind = self.get_predicate_permission_mask_kind(&predicate.name)?; + let positivity_check = match kind { + PredicatePermissionMaskKind::AliasedWholeBool + | PredicatePermissionMaskKind::AliasedFractionalBool => perm_call, + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm => { + vir_low::Expression::greater_than(perm_call, vir_low::Expression::no_permission()) + } + }; + Ok(positivity_check) + } + + pub(super) fn encode_perm_unchanged_quantifier( + &mut self, + statements: &mut Vec, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + old_permission_mask_version: vir_low::Expression, + new_permission_mask_version: vir_low::Expression, + position: vir_low::Position, + expression_evaluation_state_label: Option, + perm_new_value: vir_low::Expression, + ) -> SpannedEncodingResult<()> { + let perm_new = + self.permission_mask_call_for_predicate_def(predicate, new_permission_mask_version)?; + let perm_old = + self.permission_mask_call_for_predicate_def(predicate, old_permission_mask_version)?; + let predicate_parameters = self.get_predicate_parameters(&predicate.name).to_owned(); + let predicate_arguments = self.get_predicate_parameters_as_arguments(&predicate.name)?; + let arguments = self.purify_predicate_arguments( + statements, + predicate, + expression_evaluation_state_label, + position, + )?; + let triggers = vec![vir_low::Trigger::new(vec![perm_new.clone()])]; + let guard = predicate_arguments + .into_iter() + .zip(arguments) + .map(|(parameter, argument)| vir_low::Expression::equals(parameter, argument)) + .conjoin(); + let body = vir_low::Expression::equals( + perm_new, + vir_low::Expression::conditional_no_pos(guard, perm_new_value, perm_old), + ); + statements.push(vir_low::Statement::assume( + vir_low::Expression::forall(predicate_parameters, triggers, body), + position, + )); + Ok(()) + } + + pub(super) fn generate_permission_mask_domains( + &self, + domains: &mut Vec, + ) -> SpannedEncodingResult<()> { + let perm_version_domain = + vir_low::DomainDecl::new("PermVersion", Vec::new(), Vec::new(), Vec::new()); + domains.push(perm_version_domain); + let mut functions = Vec::new(); + let mut axioms = Vec::new(); + for predicate in self.predicates.iter_decls() { + let mut parameters = predicate.parameters.clone(); + parameters.push(vir_low::VariableDecl::new( + "version", + self.perm_version_type(), + )); + let function_name = self.permission_mask_function_name(&predicate.name); + let kind = self.get_predicate_permission_mask_kind(&predicate.name)?; + parameters.extend(self.permission_amount_parameter(kind)); + let return_type = self.mask_function_return_type(kind); + let function = vir_low::DomainFunctionDecl::new( + function_name.clone(), + false, + parameters.clone(), + return_type, + ); + functions.push(function); + if matches!( + kind, + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm + ) { + let function_call = vir_low::Expression::domain_func_app_no_pos( + "PermFunctions".to_string(), + function_name.clone(), + parameters + .clone() + .into_iter() + .map(|parameter| parameter.into()) + .collect(), + parameters.clone(), + vir_low::Type::Perm, + ); + use vir_low::macros::*; + let body = vir_low::Expression::forall( + parameters, + vec![vir_low::Trigger::new(vec![function_call.clone()])], + expr! { + ([vir_low::Expression::no_permission()] <= [function_call.clone()]) && + ([function_call] <= [vir_low::Expression::full_permission()]) + }, + ); + let axiom = + vir_low::DomainAxiomDecl::new(None, format!("{function_name}$bounds"), body); + axioms.push(axiom); + } + } + let perm_domain = vir_low::DomainDecl::new("PermFunctions", functions, axioms, Vec::new()); + domains.push(perm_domain); + Ok(()) + } + + pub(super) fn generate_init_permissions_to_zero_internal( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult> { + assert!(!position.is_default()); + let mut statements = Vec::new(); + for predicate in self.predicates.iter_decls() { + let initial_permission_mask_name = + self.permission_mask_names.get(&predicate.name).unwrap(); + let initial_permission_mask_version = self + .ssa_state + .initial_variable_version(initial_permission_mask_name); + let initial_permission_mask_ty = self.perm_version_type(); + let initial_permission_mask: vir_low::Expression = self + .new_variables + .create_variable( + initial_permission_mask_name, + initial_permission_mask_ty, + initial_permission_mask_version, + )? + .into(); + let kind = self.get_predicate_permission_mask_kind(&predicate.name)?; + let mut arguments: Vec<_> = predicate + .parameters + .iter() + .map(|parameter| parameter.clone().into()) + .collect(); + arguments.push(initial_permission_mask.clone()); + arguments.extend( + self.permission_amount_parameter(kind) + .map(|parameter| parameter.into()), + ); + let perm_function_name = self.permission_mask_function_name(&predicate.name); + let return_type = self.mask_function_return_type(kind); + let perm = vir_low::Expression::domain_function_call( + "PermFunctions", + perm_function_name.clone(), + arguments, + return_type, + ); + let no_permission = self.no_permission(kind); + let triggers = vec![vir_low::Trigger::new(vec![perm.clone()])]; + let body = vir_low::Expression::equals(perm, no_permission); + let mut parameters = predicate.parameters.clone(); + parameters.extend(self.permission_amount_parameter(kind)); + statements.push(vir_low::Statement::assume( + vir_low::Expression::forall(parameters, triggers, body), + position, + )); + } + Ok(statements) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/permission_mask/operations.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/permission_mask/operations.rs new file mode 100644 index 00000000000..179c0c7c564 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/permission_mask/operations.rs @@ -0,0 +1,169 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::transformations::custom_heap_encoding::heap_encoder::HeapEncoder, +}; +use vir_crate::{ + common::expression::BinaryOperationHelpers, + low::{self as vir_low}, +}; + +pub(in super::super) trait PermissionMaskKind {} +pub(in super::super) struct PermissionMaskKindAliasedFractionalBoundedPerm {} +impl PermissionMaskKind for PermissionMaskKindAliasedFractionalBoundedPerm {} +pub(in super::super) struct PermissionMaskKindAliasedBool {} +impl PermissionMaskKind for PermissionMaskKindAliasedBool {} + +pub(in super::super) struct PermissionMaskOperations { + _kind: std::marker::PhantomData, + old_permission_mask_version: vir_low::Expression, + new_permission_mask_version: vir_low::Expression, + perm_old: vir_low::Expression, + perm_new: vir_low::Expression, +} + +impl PermissionMaskOperations { + pub(in super::super) fn new<'p, 'v: 'p, 'tcx: 'v>( + heap_encoder: &mut HeapEncoder<'p, 'v, 'tcx>, + statements: &mut Vec, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + expression_evaluation_state_label: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let old_permission_mask_version = + heap_encoder.get_current_permission_mask_for(&predicate.name)?; + let new_permission_mask_version = + heap_encoder.get_new_permission_mask_for(&predicate.name, position)?; + let perm_old = heap_encoder.permission_mask_call_for_predicate_use( + statements, + predicate, + old_permission_mask_version.clone(), + expression_evaluation_state_label.clone(), + position, + )?; + let perm_new = heap_encoder.permission_mask_call_for_predicate_use( + statements, + predicate, + new_permission_mask_version.clone(), + expression_evaluation_state_label, + position, + )?; + Ok(Self { + _kind: std::marker::PhantomData, + old_permission_mask_version, + new_permission_mask_version, + perm_old, + perm_new, + }) + } + + pub(in super::super) fn perm_new(&self) -> vir_low::Expression { + self.perm_new.clone() + } + + pub(in super::super) fn old_permission_mask_version(&self) -> vir_low::Expression { + self.old_permission_mask_version.clone() + } + + pub(in super::super) fn new_permission_mask_version(&self) -> vir_low::Expression { + self.new_permission_mask_version.clone() + } +} + +pub(in super::super) trait TPermissionMaskOperations { + fn perm_old_greater_equals( + &self, + permission_amount: &vir_low::Expression, + ) -> vir_low::Expression; + + fn perm_old_positive(&self) -> vir_low::Expression; + + fn perm_old_sub(&self, permission_amount: &vir_low::Expression) -> vir_low::Expression; + + fn perm_old_add(&self, permission_amount: &vir_low::Expression) -> vir_low::Expression; + + fn perm_old_equal_none(&self) -> vir_low::Expression; + + fn can_assume_old_permission_is_none(&self, permission_amount: &vir_low::Expression) -> bool; +} + +impl TPermissionMaskOperations + for PermissionMaskOperations +{ + fn perm_old_greater_equals( + &self, + permission_amount: &vir_low::Expression, + ) -> vir_low::Expression { + vir_low::Expression::greater_equals(self.perm_old.clone(), permission_amount.clone()) + } + + fn perm_old_positive(&self) -> vir_low::Expression { + vir_low::Expression::greater_equals( + self.perm_old.clone(), + vir_low::Expression::no_permission(), + ) + } + + fn perm_old_sub(&self, permission_amount: &vir_low::Expression) -> vir_low::Expression { + if permission_amount.is_full_permission() { + vir_low::Expression::no_permission() + } else { + vir_low::Expression::perm_binary_op_no_pos( + vir_low::ast::expression::PermBinaryOpKind::Sub, + self.perm_old.clone(), + permission_amount.clone(), + ) + } + } + + fn perm_old_add(&self, permission_amount: &vir_low::Expression) -> vir_low::Expression { + if permission_amount.is_full_permission() { + vir_low::Expression::full_permission() + } else { + vir_low::Expression::perm_binary_op_no_pos( + vir_low::ast::expression::PermBinaryOpKind::Add, + self.perm_old.clone(), + permission_amount.clone(), + ) + } + } + + fn perm_old_equal_none(&self) -> vir_low::Expression { + vir_low::Expression::equals(self.perm_old.clone(), vir_low::Expression::no_permission()) + } + + fn can_assume_old_permission_is_none(&self, permission_amount: &vir_low::Expression) -> bool { + permission_amount.is_full_permission() + } +} + +impl TPermissionMaskOperations for PermissionMaskOperations { + fn perm_old_greater_equals( + &self, + permission_amount: &vir_low::Expression, + ) -> vir_low::Expression { + assert!(permission_amount.is_full_permission()); + self.perm_old.clone() + } + + fn perm_old_positive(&self) -> vir_low::Expression { + self.perm_old.clone() + } + + fn perm_old_sub(&self, permission_amount: &vir_low::Expression) -> vir_low::Expression { + assert!(permission_amount.is_full_permission()); + false.into() + } + + fn perm_old_add(&self, permission_amount: &vir_low::Expression) -> vir_low::Expression { + assert!(permission_amount.is_full_permission()); + true.into() + } + + fn perm_old_equal_none(&self) -> vir_low::Expression { + vir_low::Expression::equals(self.perm_old.clone(), false.into()) + } + + fn can_assume_old_permission_is_none(&self, _: &vir_low::Expression) -> bool { + true + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/predicates.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/predicates.rs new file mode 100644 index 00000000000..416b59775ef --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/predicates.rs @@ -0,0 +1,165 @@ +use super::{permission_mask::PredicatePermissionMaskKind, HeapEncoder}; +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::predicates::{OwnedPredicateInfo, SnapshotFunctionInfo}, +}; +use rustc_hash::FxHashMap; +use std::collections::BTreeMap; +use vir_crate::low::{self as vir_low}; + +pub(super) struct Predicates<'p> { + predicate_decls: FxHashMap, + snapshot_functions_to_predicates: BTreeMap, + predicates_to_snapshot_types: BTreeMap, +} + +impl<'p> Predicates<'p> { + pub(super) fn new( + predicate_decls: &'p [vir_low::PredicateDecl], + predicate_info: BTreeMap, + ) -> Self { + let mut snapshot_functions_to_predicates = BTreeMap::new(); + let mut predicates_to_snapshot_types = BTreeMap::new(); + for ( + predicate_name, + OwnedPredicateInfo { + current_snapshot_function: SnapshotFunctionInfo { function_name, .. }, + snapshot_type, + .. + }, + ) in predicate_info + { + snapshot_functions_to_predicates.insert(function_name, predicate_name.clone()); + predicates_to_snapshot_types.insert(predicate_name, snapshot_type); + } + Self { + predicate_decls: predicate_decls + .iter() + .map(|predicate| (predicate.name.clone(), predicate)) + .collect(), + snapshot_functions_to_predicates, + predicates_to_snapshot_types, + } + } + + pub(super) fn iter_decls(&self) -> impl Iterator { + self.predicate_decls.values().cloned() + } +} + +impl<'p, 'v: 'p, 'tcx: 'v> HeapEncoder<'p, 'v, 'tcx> { + pub(super) fn get_predicate_decl( + &self, + predicate_name: &str, + ) -> SpannedEncodingResult<&'p vir_low::PredicateDecl> { + let decl = self + .predicates + .predicate_decls + .get(predicate_name) + .cloned() + .unwrap(); + Ok(decl) + } + + pub(super) fn get_predicate_parameters( + &self, + predicate_name: &str, + ) -> &[vir_low::VariableDecl] { + self.predicates + .predicate_decls + .get(predicate_name) + .unwrap() + .parameters + .as_slice() + } + + pub(super) fn get_predicate_parameters_as_arguments( + &mut self, + predicate_name: &str, + ) -> SpannedEncodingResult> { + let predicate_parameters = self.get_predicate_parameters(predicate_name).to_owned(); + Ok(predicate_parameters + .iter() + .map(|parameter| parameter.clone().into()) + .collect()) + } + + pub(super) fn get_predicate_name_for_function<'a>( + &'a self, + function_name: &str, + ) -> SpannedEncodingResult { + let function = self.functions[function_name]; + let predicate_name = match function.kind { + vir_low::FunctionKind::MemoryBlockBytes => todo!(), + vir_low::FunctionKind::CallerFor => todo!(), + vir_low::FunctionKind::SnapRange => unreachable!(), + vir_low::FunctionKind::Snap => { + self.predicates.snapshot_functions_to_predicates[function_name].clone() + } + }; + Ok(predicate_name) + } + + pub(super) fn get_snapshot_type_for_predicate( + &self, + predicate_name: &str, + ) -> Option { + let predicate = self.predicates.predicate_decls[predicate_name]; + match predicate.kind { + vir_low::PredicateKind::MemoryBlock => { + use vir_low::macros::*; + Some(ty!(Bytes)) + } + vir_low::PredicateKind::Owned => Some( + self.predicates + .predicates_to_snapshot_types + .get(predicate_name) + .unwrap_or_else(|| unreachable!("predicate not found: {}", predicate_name)) + .clone(), + ), + vir_low::PredicateKind::LifetimeToken + | vir_low::PredicateKind::WithoutSnapshotWhole + | vir_low::PredicateKind::WithoutSnapshotWholeNonAliased + | vir_low::PredicateKind::WithoutSnapshotFrac => None, + } + } + + pub(super) fn purify_predicate_arguments( + &mut self, + statements: &mut Vec, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + expression_evaluation_state_label: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult> { + let mut arguments = Vec::new(); + for argument in &predicate.arguments { + arguments.push(self.encode_pure_expression( + statements, + argument.clone(), + expression_evaluation_state_label.clone(), + position, + )?); + } + Ok(arguments) + } + + pub(super) fn get_predicate_permission_mask_kind( + &self, + predicate_name: &str, + ) -> SpannedEncodingResult { + let predicate_decl = self.get_predicate_decl(predicate_name)?; + let mask_kind = match predicate_decl.kind { + vir_low::PredicateKind::MemoryBlock | vir_low::PredicateKind::Owned => { + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm + } + vir_low::PredicateKind::WithoutSnapshotFrac | vir_low::PredicateKind::LifetimeToken => { + PredicatePermissionMaskKind::AliasedFractionalBoundedPerm + } + vir_low::PredicateKind::WithoutSnapshotWhole + | vir_low::PredicateKind::WithoutSnapshotWholeNonAliased => { + PredicatePermissionMaskKind::AliasedWholeBool + } + }; + Ok(mask_kind) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/pure_expressions.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/pure_expressions.rs new file mode 100644 index 00000000000..f1c44a7fbaf --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/pure_expressions.rs @@ -0,0 +1,147 @@ +use super::HeapEncoder; +use crate::encoder::errors::{SpannedEncodingError, SpannedEncodingResult}; +use vir_crate::{ + common::expression::{BinaryOperationHelpers, ExpressionIterator, UnaryOperationHelpers}, + low::{self as vir_low, expression::visitors::ExpressionFallibleFolder}, +}; + +impl<'p, 'v: 'p, 'tcx: 'v> HeapEncoder<'p, 'v, 'tcx> { + pub(super) fn encode_pure_expression( + &mut self, + statements: &mut Vec, + expression: vir_low::Expression, + expression_evaluation_state_label: Option, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let mut purifier = Purifier { + expression_evaluation_state_label, + heap_encoder: self, + statements, + path_condition: Vec::new(), + position, + }; + purifier.fallible_fold_expression(expression) + } +} + +struct Purifier<'e, 'p, 'v: 'p, 'tcx: 'v> { + /// The state in which we should evaluate the heap expressions. If `None`, + /// takes the latest heap. + expression_evaluation_state_label: Option, + heap_encoder: &'e mut HeapEncoder<'p, 'v, 'tcx>, + statements: &'e mut Vec, + path_condition: Vec, + position: vir_low::Position, +} + +impl<'e, 'p, 'v: 'p, 'tcx: 'v> ExpressionFallibleFolder for Purifier<'e, 'p, 'v, 'tcx> { + type Error = SpannedEncodingError; + + fn fallible_fold_func_app_enum( + &mut self, + func_app: vir_low::expression::FuncApp, + ) -> Result { + let function = self.heap_encoder.functions[&func_app.function_name]; + assert_eq!(function.parameters.len(), func_app.arguments.len()); + let mut arguments = func_app + .arguments + .into_iter() + .map(|argument| self.fallible_fold_expression(argument)) + .collect::, _>>()?; + let path_condition = self.path_condition.iter().cloned().conjoin(); + let replacements = function.parameters.iter().zip(arguments.iter()).collect(); + let pres = function + .pres + .iter() + .cloned() + .conjoin() + .substitute_variables(&replacements); + let pres = self.fallible_fold_expression(pres)?; + let assert_precondition = vir_low::Expression::implies(path_condition, pres); + self.heap_encoder.encode_function_precondition_assert( + self.statements, + assert_precondition, + self.position, + self.expression_evaluation_state_label.clone(), + )?; + match function.kind { + vir_low::FunctionKind::MemoryBlockBytes => todo!(), + vir_low::FunctionKind::CallerFor => todo!(), + vir_low::FunctionKind::SnapRange => todo!(), + vir_low::FunctionKind::Snap => { + let predicate_name = self + .heap_encoder + .get_predicate_name_for_function(&func_app.function_name)?; + let heap_version = if let Some(expression_evaluation_state_label) = + &self.expression_evaluation_state_label + { + self.heap_encoder.get_heap_version_at_label( + &predicate_name, + expression_evaluation_state_label, + )? + } else { + self.heap_encoder + .get_current_heap_version_for(&predicate_name)? + }; + arguments.push(heap_version); + let heap_function_name = self.heap_encoder.heap_function_name(&predicate_name); + let return_type = self + .heap_encoder + .get_snapshot_type_for_predicate(&predicate_name) + .unwrap(); + Ok(vir_low::Expression::domain_function_call( + "HeapFunctions", + heap_function_name, + arguments, + return_type, + )) + } + } + } + + fn fallible_fold_binary_op( + &mut self, + mut binary_op: vir_low::expression::BinaryOp, + ) -> Result { + binary_op.left = self.fallible_fold_expression_boxed(binary_op.left)?; + if binary_op.op_kind == vir_low::BinaryOpKind::Implies { + self.path_condition.push((*binary_op.left).clone()); + } + binary_op.right = self.fallible_fold_expression_boxed(binary_op.right)?; + if binary_op.op_kind == vir_low::BinaryOpKind::Implies { + self.path_condition.pop(); + } + Ok(binary_op) + } + + fn fallible_fold_conditional( + &mut self, + mut conditional: vir_low::expression::Conditional, + ) -> Result { + conditional.guard = self.fallible_fold_expression_boxed(conditional.guard)?; + self.path_condition.push((*conditional.guard).clone()); + conditional.then_expr = self.fallible_fold_expression_boxed(conditional.then_expr)?; + self.path_condition.pop(); + self.path_condition + .push(vir_low::Expression::not((*conditional.guard).clone())); + conditional.else_expr = self.fallible_fold_expression_boxed(conditional.else_expr)?; + self.path_condition.pop(); + Ok(conditional) + } + + fn fallible_fold_labelled_old_enum( + &mut self, + mut labelled_old: vir_low::expression::LabelledOld, + ) -> Result { + std::mem::swap( + &mut labelled_old.label, + &mut self.expression_evaluation_state_label, + ); + let mut labelled_old = self.fallible_fold_labelled_old(labelled_old)?; + std::mem::swap( + &mut labelled_old.label, + &mut self.expression_evaluation_state_label, + ); + Ok(vir_low::Expression::LabelledOld(labelled_old)) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/statements.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/statements.rs new file mode 100644 index 00000000000..2add48c3cde --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/statements.rs @@ -0,0 +1,73 @@ +use super::HeapEncoder; +use crate::encoder::errors::SpannedEncodingResult; +use vir_crate::low::{self as vir_low}; + +impl<'p, 'v: 'p, 'tcx: 'v> HeapEncoder<'p, 'v, 'tcx> { + pub(super) fn encode_statement_internal( + &mut self, + statements: &mut Vec, + statement: vir_low::Statement, + ) -> SpannedEncodingResult<()> { + match statement { + vir_low::Statement::Comment(_) + | vir_low::Statement::LogEvent(_) + | vir_low::Statement::Assign(_) => { + statements.push(statement); + } + vir_low::Statement::Label(statement) => { + self.ssa_state.save_state_at_label(statement.label.clone()); + statements.push(vir_low::Statement::Label(statement)); + } + vir_low::Statement::Assume(statement) => { + assert!(statement.expression.is_pure()); + let expression = self.encode_pure_expression( + statements, + statement.expression, + None, + statement.position, + )?; + statements.push(vir_low::Statement::assume(expression, statement.position)); + } + vir_low::Statement::Assert(statement) => { + self.encode_expression_assert( + statements, + statement.expression, + statement.position, + None, + )?; + } + vir_low::Statement::Inhale(statement) => { + statements.push(vir_low::Statement::comment(format!("{statement}"))); + self.encode_expression_inhale( + statements, + statement.expression, + statement.position, + None, + )?; + } + vir_low::Statement::Exhale(statement) => { + statements.push(vir_low::Statement::comment(format!("{statement}"))); + let evaluation_state = self.fresh_label(); + self.ssa_state.save_state_at_label(evaluation_state.clone()); + self.encode_expression_exhale( + statements, + statement.expression, + statement.position, + &evaluation_state, + )?; + } + vir_low::Statement::Fold(_) => todo!(), + vir_low::Statement::Unfold(_) => todo!(), + vir_low::Statement::ApplyMagicWand(_) => { + unimplemented!("magic wands are not supported yet"); + } + vir_low::Statement::MethodCall(statement) => { + unreachable!("method call: {}", statement); + } + vir_low::Statement::Conditional(conditional) => { + unreachable!("conditional: {}", conditional); + } + } + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/mod.rs new file mode 100644 index 00000000000..eb1b564a457 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/mod.rs @@ -0,0 +1,110 @@ +//! This module contains a custom heap encoding that can be used instead of the +//! Viper builtin heap encoding. This module depends on `ErrorManager` and, +//! therefore, has to be in the `prusti-viper` crate. + +mod heap_encoder; +mod variable_declarations; + +use self::heap_encoder::HeapEncoder; +use crate::encoder::{ + errors::{ErrorCtxt, SpannedEncodingResult}, + middle::core_proof::predicates::OwnedPredicateInfo, + mir::errors::ErrorInterface, + Encoder, +}; +use std::collections::BTreeMap; +use vir_crate::{ + common::cfg::Cfg, + low::{self as vir_low}, +}; + +pub(in super::super) fn custom_heap_encoding<'p, 'v: 'p, 'tcx: 'v>( + encoder: &'p mut Encoder<'v, 'tcx>, + program: &mut vir_low::Program, + predicate_info: BTreeMap, +) -> SpannedEncodingResult<()> { + let mut procedures = Vec::new(); + let mut heap_encoder = HeapEncoder::new( + encoder, + &program.predicates, + predicate_info, + &program.functions, + ); + for procedure in std::mem::take(&mut program.procedures) { + heap_encoder.reset(); + let procedure = custom_heap_encoding_for_procedure(&mut heap_encoder, procedure)?; + procedures.push(procedure); + } + program.procedures = procedures; + program + .domains + .extend(heap_encoder.generate_necessary_domains()?); + Ok(()) +} + +fn custom_heap_encoding_for_procedure<'p, 'v: 'p, 'tcx: 'v>( + heap_encoder: &mut HeapEncoder<'p, 'v, 'tcx>, + mut procedure: vir_low::ProcedureDecl, +) -> SpannedEncodingResult { + let predecessors = procedure.predecessors_owned(); + let traversal_order = procedure.get_topological_sort(); + let mut basic_block_edges = BTreeMap::new(); + for label in &traversal_order { + heap_encoder.prepare_new_current_block(label, &predecessors, &mut basic_block_edges)?; + let mut statements = Vec::new(); + let block = procedure.basic_blocks.get_mut(label).unwrap(); + for statement in std::mem::take(&mut block.statements) { + heap_encoder.encode_statement(&mut statements, statement)?; + } + block.statements = statements; + heap_encoder.finish_current_block(label.clone())?; + } + for label in traversal_order { + if let Some(intermediate_blocks) = basic_block_edges.remove(&label) { + let mut block = procedure.basic_blocks.remove(&label).unwrap(); + for (successor_label, equalities) in intermediate_blocks { + let intermediate_block_label = vir_low::Label::new(format!( + "label__from__{}__to__{}", + label.name, successor_label.name + )); + block + .successor + .replace_label(&successor_label, intermediate_block_label.clone()); + let mut successor_statements = Vec::new(); + for (variable_name, ty, position, old_version, new_version) in equalities { + let new_variable = + heap_encoder.create_variable(&variable_name, ty.clone(), new_version)?; + let old_variable = + heap_encoder.create_variable(&variable_name, ty.clone(), old_version)?; + let position = heap_encoder.encoder().change_error_context( + // FIXME: Get a more precise span. + position, + ErrorCtxt::Unexpected, + ); + let statement = vir_low::macros::stmtp! { + position => assume (new_variable == old_variable) + }; + successor_statements.push(statement); + } + procedure.basic_blocks.insert( + intermediate_block_label, + vir_low::BasicBlock { + statements: successor_statements, + successor: vir_low::Successor::Goto(successor_label), + }, + ); + } + procedure.basic_blocks.insert(label, block); + } + } + let init_permissions_to_zero = + heap_encoder.generate_init_permissions_to_zero(procedure.position)?; + procedure.locals.extend(heap_encoder.take_variables()); + procedure + .basic_blocks + .get_mut(&procedure.entry) + .unwrap() + .statements + .splice(0..0, init_permissions_to_zero); + Ok(procedure) +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/variable_declarations.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/variable_declarations.rs new file mode 100644 index 00000000000..2eab1a1737a --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/variable_declarations.rs @@ -0,0 +1,26 @@ +use crate::encoder::errors::SpannedEncodingResult; +use rustc_hash::FxHashSet; + +use vir_crate::low::{self as vir_low}; + +#[derive(Default)] +pub(super) struct VariableDeclarations { + variables: FxHashSet, +} + +impl VariableDeclarations { + pub(super) fn create_variable( + &mut self, + variable_name: &str, + ty: vir_low::Type, + version: u64, + ) -> SpannedEncodingResult { + let variable = vir_low::VariableDecl::new(format!("{variable_name}_{version}"), ty); + self.variables.insert(variable.clone()); + Ok(variable) + } + + pub(super) fn take_variables(&mut self) -> FxHashSet { + std::mem::take(&mut self.variables) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_conditionals.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_conditionals.rs new file mode 100644 index 00000000000..851244b0eae --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_conditionals.rs @@ -0,0 +1,92 @@ +use vir_crate::{ + common::{expression::UnaryOperationHelpers, graphviz::ToGraphviz}, + low::{self as vir_low}, +}; + +pub(crate) fn desugar_conditionals( + source_filename: &str, + mut program: vir_low::Program, +) -> vir_low::Program { + let mut procedures = Vec::new(); + for procedure in std::mem::take(&mut program.procedures) { + let procedure = desugar_conditionals_in_procedure(procedure); + if prusti_common::config::dump_debug_info() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_low_after_desugar_conditionals", + format!("{}.{}.dot", source_filename, procedure.name), + |writer| procedure.to_graphviz(writer).unwrap(), + ); + } + procedures.push(procedure); + } + program.procedures = procedures; + program +} + +fn new_label(prefix: &str, label_counter: &mut usize) -> vir_low::Label { + let label = format!("{prefix}__{label_counter}"); + *label_counter += 1; + vir_low::Label::new(label) +} + +fn desugar_conditionals_in_procedure( + mut procedure: vir_low::ProcedureDecl, +) -> vir_low::ProcedureDecl { + let mut work_queue: Vec<_> = procedure.basic_blocks.keys().cloned().collect(); + let mut label_counter = 0; + while let Some(current_label) = work_queue.pop() { + let block = procedure.basic_blocks.get_mut(¤t_label).unwrap(); + if let Some(conditional_position) = block + .statements + .iter() + .position(|statement| matches!(statement, vir_low::Statement::Conditional(_))) + { + let remaining_statements = block.statements.split_off(conditional_position + 1); + let vir_low::Statement::Conditional(conditional) = block.statements.pop().unwrap() else { + unreachable!(); + }; + let remaining_block_label = new_label("remaining_block_label", &mut label_counter); + let then_block_label = new_label("then_block_label", &mut label_counter); + let else_block_label = new_label("else_block_label", &mut label_counter); + let then_block = vir_low::BasicBlock { + statements: conditional.then_branch, + successor: vir_low::Successor::Goto(remaining_block_label.clone()), + }; + + let mut targets = vec![(conditional.guard.clone(), then_block_label.clone())]; + let negated_guard = vir_low::Expression::not(conditional.guard.clone()); + let else_block = if conditional.else_branch.is_empty() { + targets.push((negated_guard, remaining_block_label.clone())); + None + } else { + let else_block = vir_low::BasicBlock { + statements: conditional.else_branch, + successor: vir_low::Successor::Goto(remaining_block_label.clone()), + }; + targets.push((negated_guard, else_block_label.clone())); + Some(else_block) + }; + let new_successor = vir_low::Successor::GotoSwitch(targets); + let original_successor = std::mem::replace(&mut block.successor, new_successor); + let remaining_block = vir_low::BasicBlock { + statements: remaining_statements, + successor: original_successor, + }; + work_queue.push(remaining_block_label.clone()); + work_queue.push(then_block_label.clone()); + procedure + .basic_blocks + .insert(then_block_label.clone(), then_block); + if let Some(else_block) = else_block { + work_queue.push(else_block_label.clone()); + procedure + .basic_blocks + .insert(else_block_label.clone(), else_block); + } + procedure + .basic_blocks + .insert(remaining_block_label.clone(), remaining_block); + } + } + procedure +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_fold_unfold.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_fold_unfold.rs new file mode 100644 index 00000000000..c2b41be7152 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_fold_unfold.rs @@ -0,0 +1,275 @@ +use crate::encoder::middle::core_proof::predicates::OwnedPredicateInfo; +use rustc_hash::FxHashMap; +use std::collections::BTreeMap; +use vir_crate::{ + common::graphviz::ToGraphviz, + low::{self as vir_low, expression::visitors::ExpressionFolder}, +}; + +pub(in super::super) fn desugar_fold_unfold( + source_filename: &str, + mut program: vir_low::Program, + predicates_info: &BTreeMap, +) -> vir_low::Program { + let predicate_decls = program + .predicates + .iter() + .map(|predicate| (predicate.name.clone(), predicate)) + .collect(); + let function_decls = program + .functions + .iter() + .map(|function| (function.name.clone(), function)) + .collect(); + for procedure in std::mem::take(&mut program.procedures) { + if prusti_common::config::dump_debug_info() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_low_before_desugar_fold_unfold", + format!("{}.{}.dot", source_filename, procedure.name), + |writer| procedure.to_graphviz(writer).unwrap(), + ); + } + let new_procedure = desugar_fold_unfold_in_procedure( + procedure, + &predicate_decls, + &function_decls, + predicates_info, + ); + if prusti_common::config::dump_debug_info() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_low_after_desugar_fold_unfold", + format!("{}.{}.dot", source_filename, new_procedure.name), + |writer| new_procedure.to_graphviz(writer).unwrap(), + ); + } + program.procedures.push(new_procedure); + } + program +} + +fn desugar_fold_unfold_in_procedure( + mut procedure: vir_low::ProcedureDecl, + predicate_decls: &BTreeMap, + function_decls: &BTreeMap, + predicates_info: &BTreeMap, +) -> vir_low::ProcedureDecl { + let mut label_counter = 0; + for block in procedure.basic_blocks.values_mut() { + for statement in std::mem::take(&mut block.statements) { + match statement { + vir_low::Statement::Fold(statement) => { + block + .statements + .push(vir_low::Statement::comment(format!("{statement}"))); + let old_label = new_label( + &mut block.statements, + &mut procedure.custom_labels, + &mut label_counter, + statement.position, + ); + let predicate = extract_predicate(&statement.expression); + let predicate_info = predicates_info.get(&predicate.name).unwrap(); + let predicate_decl = predicate_decls.get(&predicate.name).unwrap(); + let body = get_predicate_body(&statement.expression, predicate_decls); + block + .statements + .push(vir_low::Statement::exhale(body, statement.position)); + block.statements.push(vir_low::Statement::inhale( + statement.expression.clone(), + statement.position, + )); + let (result, snapshot_call) = construct_snapshot_call_replacement( + &statement.expression, + predicates_info, + function_decls, + ); + let new_state_label = new_label( + &mut block.statements, + &mut procedure.custom_labels, + &mut label_counter, + statement.position, + ); + let snapshot_call = vir_low::Expression::labelled_old( + Some(new_state_label), + snapshot_call, + statement.position, + ); + let mut replacements = create_parameter_replacements( + &predicate_decl.parameters, + &predicate.arguments, + ); + replacements.insert(&result, &snapshot_call); + let mut snapshot_postcondition = Vec::new(); + for assertion in &predicate_info.current_snapshot_function.postconditions { + snapshot_postcondition + .push(assertion.clone().substitute_variables(&replacements)); + } + for assertion in &predicate_info.current_snapshot_function.body { + let assertion = wrap_heap_dependent_calls_in_old( + assertion.clone(), + &old_label, + statement.position, + ); + snapshot_postcondition + .push(assertion.clone().substitute_variables(&replacements)); + } + + for assertion in snapshot_postcondition { + block + .statements + .push(vir_low::Statement::inhale(assertion, statement.position)); + } + } + vir_low::Statement::Unfold(statement) => { + block + .statements + .push(vir_low::Statement::comment(format!("{statement}"))); + let old_label = new_label( + &mut block.statements, + &mut procedure.custom_labels, + &mut label_counter, + statement.position, + ); + let body = get_predicate_body(&statement.expression, predicate_decls); + let predicate = extract_predicate(&statement.expression); + let predicate_info = predicates_info.get(&predicate.name).unwrap(); + let (result, snapshot_call) = construct_snapshot_call_replacement( + &statement.expression, + predicates_info, + function_decls, + ); + let snapshot_call = vir_low::Expression::labelled_old( + Some(old_label), + snapshot_call, + statement.position, + ); + let predicate_decl = predicate_decls.get(&predicate.name).unwrap(); + let mut replacements = create_parameter_replacements( + &predicate_decl.parameters, + &predicate.arguments, + ); + replacements.insert(&result, &snapshot_call); + let mut snapshot_postcondition = Vec::new(); + for assertion in &predicate_info.current_snapshot_function.postconditions { + snapshot_postcondition + .push(assertion.clone().substitute_variables(&replacements)); + } + for assertion in &predicate_info.current_snapshot_function.body { + snapshot_postcondition + .push(assertion.clone().substitute_variables(&replacements)); + } + block.statements.push(vir_low::Statement::exhale( + statement.expression, + statement.position, + )); + block + .statements + .push(vir_low::Statement::inhale(body, statement.position)); + for assertion in snapshot_postcondition { + block + .statements + .push(vir_low::Statement::inhale(assertion, statement.position)); + } + } + _ => { + block.statements.push(statement); + } + }; + } + } + procedure +} + +fn wrap_heap_dependent_calls_in_old( + expression: vir_low::Expression, + old_label: &str, + position: vir_low::Position, +) -> vir_low::Expression { + struct Wrapper<'a> { + old_label: &'a str, + position: vir_low::Position, + } + impl<'a> ExpressionFolder for Wrapper<'a> { + fn fold_func_app_enum( + &mut self, + func_app: vir_low::expression::FuncApp, + ) -> vir_low::Expression { + let func_app = self.fold_func_app(func_app); + let expression = vir_low::Expression::FuncApp(func_app); + vir_low::Expression::labelled_old( + Some(self.old_label.to_string()), + expression, + self.position, + ) + } + } + let mut wrapper = Wrapper { + old_label, + position, + }; + wrapper.fold_expression(expression) +} + +fn new_label( + statements: &mut Vec, + custom_labels: &mut Vec, + label_counter: &mut u32, + position: vir_low::Position, +) -> String { + let old_label = format!("fold_unfold_label${label_counter}"); + custom_labels.push(vir_low::Label::new(old_label.clone())); + *label_counter += 1; + statements.push(vir_low::Statement::label(old_label.clone(), position)); + old_label +} + +fn extract_predicate( + expression: &vir_low::Expression, +) -> &vir_low::ast::expression::PredicateAccessPredicate { + let vir_low::Expression::PredicateAccessPredicate(predicate) = &expression else { + unreachable!("{expression}") + }; + predicate +} + +fn create_parameter_replacements<'a>( + parameters: &'a [vir_low::VariableDecl], + arguments: &'a [vir_low::Expression], +) -> FxHashMap<&'a vir_low::VariableDecl, &'a vir_low::Expression> { + assert_eq!(arguments.len(), parameters.len()); + parameters.iter().zip(arguments.iter()).collect() +} + +fn get_predicate_body( + expression: &vir_low::Expression, + predicate_decls: &BTreeMap, +) -> vir_low::Expression { + let predicate = extract_predicate(expression); + let predicate_decl = predicate_decls.get(&predicate.name).unwrap(); + let body = predicate_decl.body.as_ref().unwrap().clone(); + let replacements = + create_parameter_replacements(&predicate_decl.parameters, &predicate.arguments); + body.substitute_variables(&replacements) +} + +fn construct_snapshot_call_replacement( + expression: &vir_low::Expression, + predicates_info: &BTreeMap, + function_decls: &BTreeMap, +) -> (vir_low::VariableDecl, vir_low::Expression) { + let predicate = extract_predicate(expression); + let predicate_info = predicates_info.get(&predicate.name).unwrap(); + let snapshot_function_name = &predicate_info.current_snapshot_function.function_name; + let function_decl = function_decls.get(snapshot_function_name).unwrap(); + assert!( + function_decl.body.is_none(), + "Snapshot functions are expected to be bodyless?" + ); + let call = vir_low::Expression::function_call( + snapshot_function_name.clone(), + predicate.arguments.clone(), + function_decl.return_type.clone(), + ); + let result = function_decl.result_variable(); + (result, call) +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_implications.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_implications.rs new file mode 100644 index 00000000000..6e4aa9b223e --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_implications.rs @@ -0,0 +1,147 @@ +use vir_crate::{ + common::graphviz::ToGraphviz, + low::{self as vir_low, operations::ty::Typed}, +}; + +pub(crate) fn desugar_implications( + source_filename: &str, + mut program: vir_low::Program, +) -> vir_low::Program { + let mut procedures = Vec::new(); + for procedure in std::mem::take(&mut program.procedures) { + let procedure = desugar_implications_in_procedure(procedure); + if prusti_common::config::dump_debug_info() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_low_after_desugar_implications", + format!("{}.{}.dot", source_filename, procedure.name), + |writer| procedure.to_graphviz(writer).unwrap(), + ); + } + procedures.push(procedure); + } + program.procedures = procedures; + program +} + +fn desugar_implications_in_procedure( + mut procedure: vir_low::ProcedureDecl, +) -> vir_low::ProcedureDecl { + for block in procedure.basic_blocks.values_mut() { + for statement in std::mem::take(&mut block.statements) { + match statement { + vir_low::Statement::Assume(statement) => { + desugar_expression( + &mut block.statements, + statement.expression, + statement.position, + &mut vir_low::Statement::assume, + ); + } + vir_low::Statement::Assert(statement) => { + desugar_expression( + &mut block.statements, + statement.expression, + statement.position, + &mut vir_low::Statement::assert, + ); + } + vir_low::Statement::Inhale(statement) => { + desugar_expression( + &mut block.statements, + statement.expression, + statement.position, + &mut vir_low::Statement::inhale, + ); + } + // vir_low::Statement::Exhale(statement) => { + // + // We currently do not desugar exhale because that is more + // complicated (requires to introduce old labels to preserve + // semantics) and we do not expect it to be useful. + // + // } + _ => { + block.statements.push(statement); + } + } + } + } + procedure +} + +fn desugar_expression( + statements: &mut Vec, + expression: vir_low::Expression, + position: vir_low::Position, + statement_constructor: &mut impl FnMut(vir_low::Expression, vir_low::Position) -> vir_low::Statement, +) { + match expression { + vir_low::Expression::BinaryOp(binary_op_expression) + if matches!( + binary_op_expression.op_kind, + vir_low::BinaryOpKind::And | vir_low::BinaryOpKind::Implies + ) => + { + match binary_op_expression.op_kind { + vir_low::BinaryOpKind::And => { + desugar_expression( + statements, + *binary_op_expression.left, + position, + statement_constructor, + ); + desugar_expression( + statements, + *binary_op_expression.right, + position, + statement_constructor, + ); + } + vir_low::BinaryOpKind::Implies => { + let mut then_statements = Vec::new(); + desugar_expression( + &mut then_statements, + *binary_op_expression.right, + position, + statement_constructor, + ); + statements.push(vir_low::Statement::conditional( + *binary_op_expression.left, + then_statements, + Vec::new(), + position, + )); + } + _ => { + unreachable!(); + } + } + } + vir_low::Expression::Conditional(conditional_expression) => { + assert_eq!(conditional_expression.get_type(), &vir_low::Type::Bool); + let mut then_statements = Vec::new(); + desugar_expression( + &mut then_statements, + *conditional_expression.then_expr, + position, + statement_constructor, + ); + let mut else_statements = Vec::new(); + desugar_expression( + &mut else_statements, + *conditional_expression.else_expr, + position, + statement_constructor, + ); + statements.push(vir_low::Statement::conditional( + *conditional_expression.guard, + then_statements, + else_statements, + position, + )); + } + _ => { + statements.push(statement_constructor(expression, position)); + } + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_method_calls.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_method_calls.rs new file mode 100644 index 00000000000..18dc5cd8db6 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/desugar_method_calls.rs @@ -0,0 +1,120 @@ +use rustc_hash::FxHashMap; +use vir_crate::{ + common::{expression::ExpressionIterator, graphviz::ToGraphviz}, + low::{self as vir_low}, +}; + +pub(in super::super) fn desugar_method_calls( + source_filename: &str, + mut program: vir_low::Program, +) -> vir_low::Program { + let mut procedures = Vec::new(); + let methods: FxHashMap<_, _> = program + .methods + .iter() + .map(|procedure| (&procedure.name, procedure)) + .collect(); + for procedure in std::mem::take(&mut program.procedures) { + let procedure = desugar_method_calls_for_procedure(source_filename, &methods, procedure); + procedures.push(procedure); + } + program.procedures = procedures; + program +} + +pub(in super::super) fn desugar_method_calls_for_procedure( + source_filename: &str, + methods: &FxHashMap<&String, &vir_low::MethodDecl>, + mut procedure: vir_low::ProcedureDecl, +) -> vir_low::ProcedureDecl { + let mut label_counter = 0; + for block in procedure.basic_blocks.values_mut() { + block.statements = desugar_method_calls_for_statements( + methods, + &mut label_counter, + &mut procedure.custom_labels, + std::mem::take(&mut block.statements), + ); + } + if prusti_common::config::dump_debug_info() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_low_after_desugar_method_calls", + format!("{}.{}.dot", source_filename, procedure.name), + |writer| procedure.to_graphviz(writer).unwrap(), + ); + } + procedure +} + +pub(in super::super) fn desugar_method_calls_for_statements( + methods: &FxHashMap<&String, &vir_low::MethodDecl>, + label_counter: &mut usize, + custom_labels: &mut Vec, + original_statements: Vec, +) -> Vec { + let mut statements = Vec::new(); + for statement in original_statements { + match statement { + vir_low::Statement::MethodCall(statement) => { + statements.push(vir_low::Statement::comment(format!("{statement}"))); + let old_label = format!("method_call_label${label_counter}"); + custom_labels.push(vir_low::Label::new(old_label.clone())); + *label_counter += 1; + statements.push(vir_low::Statement::label( + old_label.clone(), + statement.position, + )); + let method = methods[&statement.method_name]; + let arguments: Vec<_> = statement + .arguments + .iter() + .map(|argument| { + vir_low::Expression::labelled_old( + Some(old_label.clone()), + argument.clone(), + statement.position, + ) + }) + .collect(); + let mut replacements = method.parameters.iter().zip(arguments.iter()).collect(); + let assertion = method + .pres + .clone() + .into_iter() + .conjoin() + .substitute_variables(&replacements) + .remove_unnecessary_old(); + statements.push(vir_low::Statement::exhale(assertion, statement.position)); + replacements.extend(method.targets.iter().zip(statement.targets.iter())); + let assertion = method + .posts + .clone() + .into_iter() + .conjoin() + .substitute_variables(&replacements) + .set_old_label(&old_label) + .remove_unnecessary_old(); + statements.push(vir_low::Statement::inhale(assertion, statement.position)); + } + vir_low::Statement::Conditional(mut statement) => { + statement.then_branch = desugar_method_calls_for_statements( + methods, + label_counter, + custom_labels, + std::mem::take(&mut statement.then_branch), + ); + statement.else_branch = desugar_method_calls_for_statements( + methods, + label_counter, + custom_labels, + std::mem::take(&mut statement.else_branch), + ); + statements.push(vir_low::Statement::Conditional(statement)); + } + _ => { + statements.push(statement); + } + } + } + statements +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/expand_quantifiers.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/expand_quantifiers.rs new file mode 100644 index 00000000000..540dbabfedc --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/expand_quantifiers.rs @@ -0,0 +1,114 @@ +use vir_crate::{ + common::graphviz::ToGraphviz, + low::{self as vir_low}, +}; + +pub(crate) fn expand_quantifiers( + source_filename: &str, + mut program: vir_low::Program, +) -> vir_low::Program { + let mut procedures = Vec::new(); + for procedure in std::mem::take(&mut program.procedures) { + let procedure = expand_quantifiers_in_procedure(procedure); + if prusti_common::config::dump_debug_info() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_low_after_expand_quantifiers", + format!("{}.{}.dot", source_filename, procedure.name), + |writer| procedure.to_graphviz(writer).unwrap(), + ); + } + procedures.push(procedure); + } + program.procedures = procedures; + program +} + +fn expand_quantifiers_in_procedure( + mut procedure: vir_low::ProcedureDecl, +) -> vir_low::ProcedureDecl { + let mut counter = 0; + for block in procedure.basic_blocks.values_mut() { + for statement in std::mem::take(&mut block.statements) { + match statement { + vir_low::Statement::Assert(statement) => { + desugar_expression( + &mut counter, + &mut procedure.locals, + &mut block.statements, + statement.expression, + statement.position, + &mut vir_low::Statement::assert, + ); + } + vir_low::Statement::Exhale(statement) if statement.expression.is_pure() => { + desugar_expression( + &mut counter, + &mut procedure.locals, + &mut block.statements, + statement.expression, + statement.position, + &mut vir_low::Statement::exhale, + ); + } + _ => { + block.statements.push(statement); + } + } + } + } + procedure +} + +fn desugar_expression( + variable_counter: &mut usize, + locals: &mut Vec, + statements: &mut Vec, + expression: vir_low::Expression, + position: vir_low::Position, + statement_constructor: &mut impl FnMut(vir_low::Expression, vir_low::Position) -> vir_low::Statement, +) { + match expression { + vir_low::Expression::Quantifier(quantifier) + if quantifier.kind == vir_low::QuantifierKind::ForAll => + { + statements.push(vir_low::Statement::comment(format!( + "desugaring forall {quantifier}" + ))); + let mut variable_expressions = Vec::new(); + for bound_variable in &quantifier.variables { + let variable = vir_low::VariableDecl::new( + format!("{}$quantifier${}", bound_variable.name, variable_counter), + bound_variable.ty.clone(), + ); + *variable_counter += 1; + statements.push(vir_low::Statement::comment(format!( + " {bound_variable} → {variable}" + ))); + locals.push(variable.clone()); + variable_expressions.push(variable.clone().into()); + } + let replacements = quantifier + .variables + .iter() + .zip(variable_expressions.iter()) + .collect(); + let body = quantifier.body.substitute_variables(&replacements); + let assertion = match body { + vir_low::Expression::BinaryOp(binary_expression) + if binary_expression.op_kind == vir_low::BinaryOpKind::Implies => + { + statements.push(vir_low::Statement::assume( + *binary_expression.left, + position, + )); + *binary_expression.right + } + body => body, + }; + statements.push(vir_low::Statement::assert(assertion, position)); + } + _ => { + statements.push(statement_constructor(expression, position)); + } + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/inline_functions.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/inline_functions.rs index f7878f44769..329d5ec4ca0 100644 --- a/prusti-viper/src/encoder/middle/core_proof/transformations/inline_functions.rs +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/inline_functions.rs @@ -1,57 +1,86 @@ use rustc_hash::FxHashMap; use vir_crate::{ - common::expression::{ExpressionIterator, UnaryOperationHelpers}, + common::{ + expression::{ExpressionIterator, UnaryOperationHelpers}, + graphviz::ToGraphviz, + }, low::{self as vir_low}, }; use vir_low::expression::visitors::ExpressionFolder; -pub(crate) fn inline_caller_for(program: &mut vir_low::Program) { +pub(crate) fn inline_caller_for(source_filename: &str, program: &mut vir_low::Program) { let caller_for_functions = program .functions .drain_filter(|function| function.kind == vir_low::FunctionKind::CallerFor) .map(|function| (function.name.clone(), function)) .collect(); for procedure in &mut program.procedures { - for block in &mut procedure.basic_blocks { - inline_in_statements(&mut block.statements, &caller_for_functions); + let mut inliner = Inliner { + caller_for_functions: &caller_for_functions, + statements: Vec::new(), + path_condition: Vec::new(), + }; + for block in procedure.basic_blocks.values_mut() { + inline_in_statements(&mut inliner, std::mem::take(&mut block.statements)); + match &mut block.successor { + vir_low::Successor::Return => {} + vir_low::Successor::Goto(_) => {} + vir_low::Successor::GotoSwitch(targets) => { + let mut new_targets = Vec::new(); + for (guard, target) in std::mem::take(targets) { + let guard = inliner.fold_expression(guard); + new_targets.push((guard, target)); + } + block.successor = vir_low::Successor::GotoSwitch(new_targets); + } + } + block.statements = std::mem::take(&mut inliner.statements); + } + if prusti_common::config::dump_debug_info() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_low_after_inline_caller_for", + format!("{}.{}.dot", source_filename, procedure.name), + |writer| procedure.to_graphviz(writer).unwrap(), + ); } } } -fn inline_in_statements( - statements: &mut Vec, - caller_for_functions: &FxHashMap, -) { - let old_statements = std::mem::take(statements); - let mut inliner = Inliner { - caller_for_functions, - statements, - path_condition: Vec::new(), - }; - let mut sentinel = true.into(); +fn inline_in_statements(inliner: &mut Inliner, old_statements: Vec) { for statement in old_statements { assert!(inliner.path_condition.is_empty()); match statement { - vir_low::Statement::Assume(mut statement) => { - sentinel = - inliner.fold_expression(std::mem::replace(&mut statement.expression, sentinel)); - std::mem::swap(&mut statement.expression, &mut sentinel); - inliner - .statements - .push(vir_low::Statement::Assume(statement)); + vir_low::Statement::Assume(statement) => { + inliner.inline_statement( + statement.expression, + statement.position, + vir_low::Statement::assume, + ); + } + vir_low::Statement::Assert(statement) => { + inliner.inline_statement( + statement.expression, + statement.position, + vir_low::Statement::assert, + ); + } + vir_low::Statement::Inhale(statement) => { + inliner.inline_statement( + statement.expression, + statement.position, + vir_low::Statement::inhale, + ); } - vir_low::Statement::Assert(mut statement) => { - sentinel = - inliner.fold_expression(std::mem::replace(&mut statement.expression, sentinel)); - std::mem::swap(&mut statement.expression, &mut sentinel); - inliner - .statements - .push(vir_low::Statement::Assert(statement)); + vir_low::Statement::Exhale(statement) => { + inliner.inline_statement( + statement.expression, + statement.position, + vir_low::Statement::exhale, + ); } vir_low::Statement::Comment(_) + | vir_low::Statement::Label(_) | vir_low::Statement::LogEvent(_) - | vir_low::Statement::Inhale(_) - | vir_low::Statement::Exhale(_) | vir_low::Statement::Fold(_) | vir_low::Statement::Unfold(_) | vir_low::Statement::ApplyMagicWand(_) @@ -66,10 +95,23 @@ fn inline_in_statements( struct Inliner<'a> { caller_for_functions: &'a FxHashMap, - statements: &'a mut Vec, + statements: Vec, path_condition: Vec, } +impl<'a> Inliner<'a> { + fn inline_statement( + &mut self, + expression: vir_low::Expression, + position: vir_low::Position, + constructor: fn(vir_low::Expression, vir_low::Position) -> vir_low::Statement, + ) { + let expression = self.fold_expression(expression); + let statement = constructor(expression, position); + self.statements.push(statement); + } +} + impl<'a> ExpressionFolder for Inliner<'a> { fn fold_binary_op( &mut self, diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/mod.rs index b678daa9e5f..41516d0c15a 100644 --- a/prusti-viper/src/encoder/middle/core_proof/transformations/mod.rs +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/mod.rs @@ -1,3 +1,10 @@ pub(super) mod inline_functions; pub(super) mod remove_predicates; pub(super) mod remove_unvisited_blocks; +pub(super) mod custom_heap_encoding; +pub(super) mod desugar_fold_unfold; +pub(super) mod desugar_method_calls; +pub(super) mod desugar_conditionals; +pub(super) mod symbolic_execution; +pub(super) mod desugar_implications; +pub(super) mod expand_quantifiers; diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/remove_predicates.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/remove_predicates.rs index b53badeb770..bf9741aa204 100644 --- a/prusti-viper/src/encoder/middle/core_proof/transformations/remove_predicates.rs +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/remove_predicates.rs @@ -1,5 +1,5 @@ use rustc_hash::{FxHashMap, FxHashSet}; -use vir_crate::low as vir_low; +use vir_crate::low::{self as vir_low, expression::visitors::default_fold_func_app}; use vir_low::expression::visitors::ExpressionFolder; pub(in super::super) fn remove_predicates( @@ -22,7 +22,7 @@ fn from_procedure( removed_functions: &FxHashSet, predicates: &FxHashMap, ) { - for block in &mut procedure.basic_blocks { + for block in procedure.basic_blocks.values_mut() { from_statements( &mut block.statements, removed_methods, @@ -63,6 +63,7 @@ fn from_statements( for statement in std::mem::take(statements) { match statement { vir_low::Statement::Comment(_) + | vir_low::Statement::Label(_) | vir_low::Statement::LogEvent(_) | vir_low::Statement::Assume(_) | vir_low::Statement::Assert(_) @@ -149,7 +150,7 @@ impl<'a> ExpressionFolder for PredicateRemover<'a> { if self.removed_functions.contains(&func_app.function_name) { self.drop_parent_binary_op = true; } - func_app + default_fold_func_app(self, func_app) } fn fold_binary_op_enum( &mut self, @@ -163,6 +164,12 @@ impl<'a> ExpressionFolder for PredicateRemover<'a> { vir_low::Expression::BinaryOp(binary_op) } } + fn fold_unfolding_enum( + &mut self, + unfolding: vir_low::expression::Unfolding, + ) -> vir_low::Expression { + self.fold_expression(*unfolding.base) + } } struct PredicateInliner<'a> { diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/remove_unvisited_blocks.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/remove_unvisited_blocks.rs index 90964cf6507..1ce12e1dbcc 100644 --- a/prusti-viper/src/encoder/middle/core_proof/transformations/remove_unvisited_blocks.rs +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/remove_unvisited_blocks.rs @@ -10,8 +10,8 @@ pub(in super::super) fn remove_unvisited_blocks( label_markers: &FxHashMap, ) -> SpannedEncodingResult<()> { for procedure in procedures { - for block in &mut procedure.basic_blocks { - if !label_markers.get(&block.label.name).unwrap_or(&true) { + for (label, block) in &mut procedure.basic_blocks { + if !label_markers.get(&label.name).unwrap_or(&true) { // The block was not visited. Replace with assume false. let mut position = None; for statement in &block.statements { diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/consistency_tracker.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/consistency_tracker.rs new file mode 100644 index 00000000000..55b57eaef88 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/consistency_tracker.rs @@ -0,0 +1,148 @@ +//! Tracks the values of boolean variables to catch when we enter an +//! inconsistent state. + +use crate::encoder::errors::SpannedEncodingResult; +use std::collections::BTreeMap; +use vir_crate::{ + common::expression::SyntacticEvaluation, + low::{self as vir_low, operations::ty::Typed}, +}; + +#[derive(Clone, Default)] +pub(super) struct ConsistencyTracker { + /// The current values of the boolean variables. + variables: BTreeMap, + is_inconsistent: bool, +} +impl ConsistencyTracker { + pub(super) fn is_inconsistent(&self) -> SpannedEncodingResult { + Ok(self.is_inconsistent) + } + + pub(super) fn try_assume(&mut self, term: &vir_low::Expression) -> SpannedEncodingResult<()> { + assert!(term.get_type().is_bool(), "term: {term} {term:?}"); + if term.is_false() { + self.is_inconsistent = true; + } else if Some(false) == self.try_eval(term)? { + self.is_inconsistent = true; + } else { + self.try_assume_value(term, true)?; + } + Ok(()) + } + + fn try_assume_value( + &mut self, + term: &vir_low::Expression, + value: bool, + ) -> SpannedEncodingResult<()> { + match term { + vir_low::Expression::Local(local) => { + self.set_variable_bool(&local.variable.name, value)?; + } + vir_low::Expression::UnaryOp(vir_low::UnaryOp { + op_kind: vir_low::UnaryOpKind::Not, + argument, + .. + }) => { + self.try_assume_value(argument, !value)?; + } + vir_low::Expression::BinaryOp(vir_low::BinaryOp { + op_kind: vir_low::BinaryOpKind::And, + left, + right, + .. + }) => { + self.try_assume_equal(&left, &right)?; + } + _ => (), + } + Ok(()) + } + + pub(super) fn try_assume_equal( + &mut self, + left: &vir_low::Expression, + right: &vir_low::Expression, + ) -> SpannedEncodingResult<()> { + if !left.get_type().is_bool() { + return Ok(()); + } + match (left, right) { + (vir_low::Expression::Local(local), vir_low::Expression::Constant(constant)) + | (vir_low::Expression::Constant(constant), vir_low::Expression::Local(local)) => { + self.set_variable(local, constant)?; + } + (vir_low::Expression::Local(left_local), vir_low::Expression::Local(right_local)) => { + if let Some(left_value) = self.variables.get(&left_local.variable.name) { + self.set_variable_bool(&right_local.variable.name, *left_value)?; + } + if let Some(right_value) = self.variables.get(&right_local.variable.name) { + self.set_variable_bool(&left_local.variable.name, *right_value)?; + } + } + _ => {} + } + Ok(()) + } + + fn set_variable( + &mut self, + local: &vir_low::Local, + constant: &vir_low::Constant, + ) -> SpannedEncodingResult<()> { + let vir_low::ConstantValue::Bool(value) = &constant.value else { + unreachable!("local: {local:?} constant: {constant:?}"); + }; + self.set_variable_bool(&local.variable.name, *value)?; + Ok(()) + } + + fn set_variable_bool(&mut self, variable_name: &str, value: bool) -> SpannedEncodingResult<()> { + if let Some(current_value) = self.variables.get(variable_name) { + if value != *current_value { + self.is_inconsistent = true; + } + } else { + self.variables.insert(variable_name.to_string(), value); + } + Ok(()) + } + + fn try_eval(&self, term: &vir_low::Expression) -> SpannedEncodingResult> { + let result = match term { + vir_low::Expression::Local(local) => self.variables.get(&local.variable.name).cloned(), + vir_low::Expression::Constant(constant) => match constant.value { + vir_low::ConstantValue::Bool(value) => Some(value), + _ => None, + }, + vir_low::Expression::UnaryOp(vir_low::UnaryOp { + op_kind: vir_low::UnaryOpKind::Not, + argument, + .. + }) => self.try_eval(argument)?.map(|value| !value), + vir_low::Expression::BinaryOp(vir_low::BinaryOp { + op_kind: vir_low::BinaryOpKind::And, + left, + right, + .. + }) => match (self.try_eval(left)?, self.try_eval(right)?) { + (Some(left_value), Some(right_value)) => Some(left_value && right_value), + (Some(false), _) | (_, Some(false)) => Some(false), + _ => None, + }, + vir_low::Expression::BinaryOp(vir_low::BinaryOp { + op_kind: vir_low::BinaryOpKind::Or, + left, + right, + .. + }) => match (self.try_eval(left)?, self.try_eval(right)?) { + (Some(left_value), Some(right_value)) => Some(left_value || right_value), + (Some(true), _) | (_, Some(true)) => Some(true), + _ => None, + }, + _ => None, + }; + Ok(result) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/graphviz.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/graphviz.rs new file mode 100644 index 00000000000..4d87fc4f876 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/graphviz.rs @@ -0,0 +1,94 @@ +use super::{language::ExpressionLanguage, EGraphState}; +use crate::encoder::errors::SpannedEncodingResult; +use egg::{Id, Language}; +use rustc_hash::FxHashSet; +use std::{fmt::Write, path::Path}; + +impl EGraphState { + pub(in super::super) fn eclass_to_dot_file( + &self, + id: Id, + filename: impl AsRef, + ) -> SpannedEncodingResult<()> { + use std::io::Write; + let mut file = std::fs::File::create(filename).unwrap(); + let mut buffer = String::new(); + self.eclass_to_dot(id, &mut buffer).unwrap(); + writeln!(file, "{buffer}").unwrap(); + Ok(()) + } + + pub(in super::super) fn eclass_to_dot( + &self, + id: Id, + writer: &mut dyn Write, + ) -> std::fmt::Result { + writeln!(writer, "digraph {{")?; + + writeln!(writer, " compound=true")?; + writeln!(writer, " clusterrank=local")?; + + let mut printed_classes = FxHashSet::default(); + let mut classes_to_print = vec![id]; + while let Some(id) = classes_to_print.pop() { + self.print_eclass(id, writer, &mut printed_classes, &mut classes_to_print)?; + } + + writeln!(writer, "}}")?; + Ok(()) + } + + fn print_eclass( + &self, + id: Id, + writer: &mut dyn Write, + printed_classes: &mut FxHashSet, + classes_to_print: &mut Vec, + ) -> std::fmt::Result { + if !printed_classes.contains(&id) { + printed_classes.insert(id); + let class = &self.egraph[id]; + writeln!(writer, " subgraph cluster_{id} {{")?; + writeln!(writer, " style=dotted")?; + for (i, node) in class.iter().enumerate() { + match node { + ExpressionLanguage::Variable(symbol) + if symbol.as_str().starts_with("snapshot$") => + { + // ignore snapshot variables + writeln!(writer, " {id}.{i}[label = \"\"]")?; + } + _ => { + writeln!(writer, " {id}.{i}[label = \"{id} {node}\"]")?; + } + } + } + writeln!(writer, " }}")?; + + for (i_in_class, node) in class.iter().enumerate() { + let mut arg_i = 0; + node.try_for_each(|child| { + let child_class_id = self.egraph.find(child); + classes_to_print.push(child_class_id); + if child_class_id == class.id { + // We have a self-loop. + writeln!( + writer, + " {}.{} -> {}.{}:n [lhead = cluster_{}, label=\"{}:{}\"]", + class.id, i_in_class, class.id, i_in_class, class.id, arg_i, child + )?; + } else { + writeln!( + writer, + " {}.{} -> {}.0 [lhead = cluster_{}, label=\"{}:{}\"]", + class.id, i_in_class, child, child_class_id, arg_i, child + )?; + } + arg_i += 1; + Ok::<_, std::fmt::Error>(()) + })?; + } + } + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/language.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/language.rs new file mode 100644 index 00000000000..295b3402f91 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/language.rs @@ -0,0 +1,29 @@ +use egg::{define_language, Id, Symbol}; + +define_language! { + pub(super) enum ExpressionLanguage { + "true" = True, + "false" = False, + "==" = EqCmp([Id; 2]), + "!=" = NeCmp([Id; 2]), + ">" = GtCmp([Id; 2]), + ">=" = GeCmp([Id; 2]), + "<=" = LtCmp([Id; 2]), + "<" = LeCmp([Id; 2]), + "+" = Add([Id; 2]), + "-" = Sub([Id; 2]), + "*" = Mul([Id; 2]), + "/" = Div([Id; 2]), + "%" = Mod([Id; 2]), + "&&" = And([Id; 2]), + "||" = Or([Id; 2]), + "==>" = Implies([Id; 2]), + "!" = Not(Id), + "neg" = Minus(Id), + Int(i64), + BigInt(Symbol), + Variable(Symbol), + FuncApp(Symbol, Vec), + BuiltinFuncApp(Symbol, Vec), + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/mod.rs new file mode 100644 index 00000000000..1b373cad02e --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/mod.rs @@ -0,0 +1,492 @@ +use self::language::ExpressionLanguage; +use super::{consistency_tracker::ConsistencyTracker, ProcedureExecutor}; +use crate::encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + middle::core_proof::transformations::symbolic_execution::egg::{ + rule_applier::RuleApplier, term_interner::TermInterner, + }, +}; +use egg::{EGraph, Id, Language}; +use rustc_hash::{FxHashMap, FxHashSet}; +use std::{collections::BTreeMap, io::Write}; +use vir_crate::low::{ + self as vir_low, + expression::visitors::{default_fallible_walk_expression, ExpressionFallibleWalker}, +}; + +mod language; +mod term_interner; +mod rule_applier; +mod graphviz; + +// impl<'a> ProcedureExecutor<'a> { +// /// Returns true if all arguments are valid terms; that is they are heap +// /// independent. +// pub(super) fn check_and_register_terms( +// &mut self, +// arguments: &[vir_low::Expression], +// ) -> SpannedEncodingResult { +// let mut all_arguments_heap_independent = true; +// for argument in arguments { +// if argument.is_heap_independent() { +// self.execution_trace_builder +// .current_egraph_state() +// .intern_term(argument)?; +// } else { +// all_arguments_heap_independent = false; +// } +// } +// Ok(all_arguments_heap_independent) +// } +// } + +#[derive(Clone)] +pub(super) struct EGraphState { + egraph: EGraph, + simplification_rules: Vec>, + false_id: Id, + true_id: Id, + interned_terms: FxHashMap, + consistency_tracker: ConsistencyTracker, +} + +impl EGraphState { + pub(super) fn new(domains: &[vir_low::DomainDecl]) -> SpannedEncodingResult { + let mut egraph = EGraph::default(); + let true_id = egraph.add(ExpressionLanguage::True); + let false_id = egraph.add(ExpressionLanguage::False); + let mut simplification_rules = Vec::new(); + for domain in domains { + for rule in &domain.rewrite_rules { + let mut variables = BTreeMap::new(); + let mut source_pattern_ast: egg::RecExpr> = + egg::RecExpr::default(); + let true_id = + source_pattern_ast.add(egg::ENodeOrVar::ENode(ExpressionLanguage::True)); + let false_id = + source_pattern_ast.add(egg::ENodeOrVar::ENode(ExpressionLanguage::False)); + for variable in &rule.variables { + let egg_variable: egg::Var = format!("?{}", variable.name).parse().unwrap(); + let variable_id = source_pattern_ast.add(egg::ENodeOrVar::Var(egg_variable)); + variables.insert(variable.name.clone(), variable_id); + } + let mut target_pattern_ast = source_pattern_ast.clone(); + let mut trigger_pattern = source_pattern_ast.clone(); + source_pattern_ast.intern_pattern(&rule.source, true_id, false_id, &variables)?; + target_pattern_ast.intern_pattern(&rule.target, true_id, false_id, &variables)?; + let egg_rule = if let Some(triggers) = &rule.triggers { + assert_eq!( + triggers.len(), + 1, + "Currently only single term triggers are implemented." + ); + assert_eq!( + triggers[0].terms.len(), + 1, + "Currently only single term triggers are implemented." + ); + trigger_pattern.intern_pattern( + &triggers[0].terms[0], + true_id, + false_id, + &variables, + )?; + let trigger_pattern = egg::Pattern::new(trigger_pattern); + egg::rewrite!(&rule.name; trigger_pattern => { + RuleApplier::new(source_pattern_ast, target_pattern_ast) + }) + } else { + let source_pattern = egg::Pattern::new(source_pattern_ast); + let target_pattern = egg::Pattern::new(target_pattern_ast); + egg::rewrite!(&rule.name; source_pattern => target_pattern) + }; + simplification_rules.push(egg_rule); + } + } + // let rule = { + // let place_var: egg::Var = "?place".parse().unwrap(); + // let address_var: egg::Var = "?address".parse().unwrap(); + // let mut pattern: egg::RecExpr> = + // egg::RecExpr::default(); + // let place = pattern.add(egg::ENodeOrVar::Var(place_var)); + // let address = pattern.add(egg::ENodeOrVar::Var(address_var)); + // pattern.add(egg::ENodeOrVar::ENode(ExpressionLanguage::FuncApp( + // Symbol::from("compute_address"), + // vec![place, address], + // ))); + // let match_pattern = egg::Pattern::new(pattern); + // let mut pattern: egg::RecExpr> = + // egg::RecExpr::default(); + // pattern.add(egg::ENodeOrVar::Var(address_var)); + // let target_pattern = egg::Pattern::new(pattern); + // egg::rewrite!("evaluate_compute_address"; match_pattern => target_pattern) + // }; + // let simplification_rules = vec![rule]; + Ok(Self { + egraph, + simplification_rules, + true_id, + false_id, + interned_terms: Default::default(), + consistency_tracker: Default::default(), + }) + } + + /// Assume all internable conjuncts. Conjuncts are internable if they are + /// heap independent and do not contain quantifiers, conditionals, and let + /// expressions. + pub(super) fn try_assume_heap_independent_conjuncts( + &mut self, + expression: &vir_low::Expression, + ) -> SpannedEncodingResult<()> { + if let vir_low::Expression::BinaryOp(binary_expression) = expression { + match binary_expression.op_kind { + vir_low::BinaryOpKind::EqCmp => { + if expression.is_heap_independent() { + self.assume_equal(&binary_expression.left, &binary_expression.right)?; + return Ok(()); + } + } + vir_low::BinaryOpKind::And => { + self.try_assume_heap_independent_conjuncts(&binary_expression.left)?; + self.try_assume_heap_independent_conjuncts(&binary_expression.right)?; + return Ok(()); + } + _ => {} + } + } + if expression.is_heap_independent() { + self.try_assume(expression)?; + } + Ok(()) + } + + pub(super) fn try_intern_heap_independent_conjuncts( + &mut self, + expression: &vir_low::Expression, + ) -> SpannedEncodingResult<()> { + if let vir_low::Expression::BinaryOp(binary_expression) = expression { + if let vir_low::BinaryOpKind::And = binary_expression.op_kind { + self.try_intern_heap_independent_conjuncts(&binary_expression.left)?; + self.try_intern_heap_independent_conjuncts(&binary_expression.right)?; + return Ok(()); + } + } + if expression.is_heap_independent() { + self.try_intern_term(expression)?; + } + Ok(()) + } + + /// Returns true if any new terms were interned. + pub(super) fn intern_heap_independent_terms( + &mut self, + terms: &[vir_low::Expression], + ) -> SpannedEncodingResult { + let mut newly_interned = false; + for term in terms { + if term.is_heap_independent() && self.try_lookup_term(term)?.is_none() { + self.intern_term(term)?; + newly_interned = true; + } + } + Ok(newly_interned) + } + + pub(super) fn intern_heap_independent_subexpressions( + &mut self, + expression: &vir_low::Expression, + ) -> SpannedEncodingResult<()> { + // eprintln!("intern_heap_independent_subexpressions: {expression}"); + struct Walker<'a> { + egraph: &'a mut EGraphState, + } + impl<'a> ExpressionFallibleWalker for Walker<'a> { + type Error = SpannedEncodingError; + fn fallible_walk_expression( + &mut self, + expression: &vir_low::Expression, + ) -> Result<(), Self::Error> { + if expression.is_heap_independent() { + // eprintln!("Try interning: {expression}"); + self.egraph.try_intern_term(expression)?; + // return Ok(()); – FIXME: We cannot return early here + // because `try_intern_term` stores only the id of the + // whole expression, not the ids of its subexpressions. + } + default_fallible_walk_expression(self, expression) + } + } + let mut walker = Walker { egraph: self }; + walker.fallible_walk_expression(expression)?; + + // struct Walker2<'a> { + // egraph: &'a mut EGraphState, + // } + // impl<'a> ExpressionFallibleWalker for Walker2<'a> { + // type Error = SpannedEncodingError; + // fn fallible_walk_domain_func_app( + // &mut self, + // domain_func_app: &vir_low::DomainFuncApp, + // ) -> Result<(), Self::Error> { + // // assert_ne!(domain_func_app.function_name, "constructor$Snap$Usize$Mul_Usize"); + // // assert_ne!(domain_func_app.function_name, "Size$I32$"); + // if domain_func_app.function_name == "destructor$Snap$Usize$$value" { + // if let vir_low::Expression::DomainFuncApp(domain_func_app2) = &domain_func_app.arguments[0] { + // assert_ne!(domain_func_app2.function_name, "Size$I32$", "{domain_func_app}"); + // } + // } + // // assert_ne!(domain_func_app.function_name, "destructor$Snap$Usize$$value", "{domain_func_app}"); + // vir_low::expression::visitors::default_fallible_walk_domain_func_app(self, domain_func_app) + // } + // } + // let mut walker = Walker2 { + // egraph: self.execution_trace_builder.current_egraph_state(), + // }; + // walker.fallible_walk_expression(expression)?; + + Ok(()) + } + + pub(super) fn assume(&mut self, term: &vir_low::Expression) -> SpannedEncodingResult<()> { + self.consistency_tracker.try_assume(term)?; + let term_id = self.intern_term(term)?; + self.egraph.union(term_id, self.true_id); + Ok(()) + } + + fn try_assume(&mut self, term: &vir_low::Expression) -> SpannedEncodingResult<()> { + self.consistency_tracker.try_assume(term)?; + if let Some(term_id) = self.try_intern_term(term)? { + self.egraph.union(term_id, self.true_id); + } + Ok(()) + } + + pub(super) fn assume_equal( + &mut self, + left: &vir_low::Expression, + right: &vir_low::Expression, + ) -> SpannedEncodingResult<()> { + self.consistency_tracker.try_assume_equal(left, right)?; + let left_id = self.intern_term(left)?; + let right_id = self.intern_term(right)?; + self.egraph.union(left_id, right_id); + Ok(()) + } + + /// If the graph was modified, `saturate` must be called before `is_equal` can + /// be used. + pub(super) fn is_equal( + &self, + left: &vir_low::Expression, + right: &vir_low::Expression, + ) -> SpannedEncodingResult { + let left_id = self.lookup_term(left)?; + let right_id = self.lookup_term(right)?; + Ok(self.egraph.find(left_id) == self.egraph.find(right_id)) + } + + pub(super) fn is_true(&self, term: &vir_low::Expression) -> SpannedEncodingResult { + let term_id = self.lookup_term(term)?; + Ok(self.egraph.find(term_id) == self.egraph.find(self.true_id)) + } + + pub(super) fn try_is_true( + &self, + term: &vir_low::Expression, + ) -> SpannedEncodingResult> { + if let Some(term_id) = self.try_lookup_term(term)? { + Ok(Some( + self.egraph.find(term_id) == self.egraph.find(self.true_id), + )) + } else { + Ok(None) + } + } + + /// Check whether the term is known to be a constant. + /// + /// Returns: + /// + /// * `Some((Some(constructor_name), constant))` if the term is equivalent + /// to a given constantat wrapped in the specified constructor. + /// * `Some(None, constant)` if the term is directly equivalent to a constant. + /// * `None` if the term is not equivalent to a constant. + pub(super) fn resolve_constant( + &self, + term: &vir_low::Expression, + constant_constructors: &FxHashSet, + ) -> SpannedEncodingResult, vir_low::Expression)>> { + let Some(id) = self.try_lookup_term(term)? else { + // eprintln!("not interned: {term}"); + return Ok(None); + }; + struct PreferConstantsCostFunction<'a> { + constant_constructors: &'a FxHashSet, + } + impl<'a> egg::CostFunction for PreferConstantsCostFunction<'a> { + type Cost = f64; + fn cost(&mut self, enode: &ExpressionLanguage, mut costs: C) -> Self::Cost + where + C: FnMut(Id) -> Self::Cost, + { + let op_cost = match enode { + ExpressionLanguage::True + | ExpressionLanguage::False + | ExpressionLanguage::Int(_) + | ExpressionLanguage::BigInt(_) => 1.0, + ExpressionLanguage::FuncApp(symbol, _) + | ExpressionLanguage::BuiltinFuncApp(symbol, _) + if self.constant_constructors.contains(symbol.as_str()) => + { + 2.0 + } + _ => 10.0, + }; + enode + .children() + .iter() + .fold(op_cost, |sum, id| sum + costs(*id)) + } + } + let cost_func = PreferConstantsCostFunction { + constant_constructors, + }; + let extractor = egg::Extractor::new(&self.egraph, cost_func); + let (_best_cost, node) = extractor.find_best(id); + let last: Id = (node.as_ref().len() - 1).into(); + match &node[last] { + ExpressionLanguage::FuncApp(name, arguments) if arguments.len() == 1 => { + match node[arguments[0]] { + ExpressionLanguage::Int(constant) => { + return Ok(Some((Some(name.to_string()), constant.into()))); + } + ExpressionLanguage::BigInt(_) => todo!(), + _ => {} + } + } + ExpressionLanguage::Int(constant) => { + return Ok(Some((None, (*constant).into()))); + } + ExpressionLanguage::BigInt(constant) => { + let constant_value = vir_low::ConstantValue::BigInt(constant.to_string()); + let expression = + vir_low::Expression::constant_no_pos(constant_value, vir_low::Type::Int); + return Ok(Some((None, expression))); + } + _ => {} + } + Ok(None) + } + + pub(super) fn saturate(&mut self) -> SpannedEncodingResult<()> { + self.egraph.rebuild(); + let runner: egg::Runner<_, _, ()> = egg::Runner::new(()) + .with_egraph(std::mem::take(&mut self.egraph)) + // .with_node_limit(200) + .run(&self.simplification_rules); + if !(matches!(runner.stop_reason.unwrap(), egg::StopReason::Saturated)) { + runner + .egraph + .dot() + .to_dot("/tmp/egraph-unsaturated.dot") + .unwrap(); + panic!("simplification rules did not saturate; see /tmp/egraph-unsaturated.dot"); + } + self.egraph = runner.egraph; + Ok(()) + } + + pub(super) fn is_inconsistent(&mut self) -> SpannedEncodingResult { + self.consistency_tracker.is_inconsistent() + // if self.consistency_tracker.is_inconsistent()? { + // Ok(true) + // } else { + // // self.egraph.rebuild(); + // Ok(self.egraph.find(self.true_id) == self.egraph.find(self.false_id)) + // } + } + + /// Lookup the id of a previously interned term. + fn lookup_term(&self, term: &vir_low::Expression) -> SpannedEncodingResult { + Ok(self.try_lookup_term(term)?.unwrap_or_else(|| { + panic!("term {term} is not interned"); + })) + } + + /// Lookup the id of a previously interned term. + fn try_lookup_term(&self, term: &vir_low::Expression) -> SpannedEncodingResult> { + Ok(self.interned_terms.get(term).cloned()) + } + + pub(super) fn intern_term(&mut self, term: &vir_low::Expression) -> SpannedEncodingResult { + let id = self.try_intern_term(term)?.unwrap_or_else(|| { + panic!("term {term} cannot be interned"); + }); + Ok(id) + // if let Some(id) = self.interned_terms.get(term) { + // Ok(*id) + // } else { + // assert!(term.is_heap_independent(), "{term} is heap independent"); + // let id = self.egraph.intern_term(term, self.true_id, self.false_id)?; + // self.interned_terms.insert(term.clone(), id); + // Ok(id) + // } + } + + pub(super) fn try_intern_term( + &mut self, + term: &vir_low::Expression, + ) -> SpannedEncodingResult> { + if let Some(id) = self.interned_terms.get(term) { + Ok(Some(*id)) + } else { + assert!(term.is_heap_independent(), "{term} is heap dependent"); + if let Some(id) = self + .egraph + .try_intern_term(term, self.true_id, self.false_id)? + { + self.interned_terms.insert(term.clone(), id); + Ok(Some(id)) + } else { + Ok(None) + } + } + } + + pub(super) fn to_graphviz(&self, writer: &mut dyn Write) -> std::io::Result<()> { + write!(writer, "{}", self.egraph.dot()) + } + + #[allow(unused)] + pub(super) fn dump_dot(&self, path: &str) -> SpannedEncodingResult<()> { + self.egraph.dot().to_dot(path).unwrap(); + Ok(()) + } + + // pub(super) fn get_dump_eclass_of( + // &self, + // term: &vir_low::Expression, + // ) -> SpannedEncodingResult { + // use std::fmt::Write; + // let id = self.lookup_term(term)?; + // // if id == 337.into() { + // // println!("eclass of {term}: {id}"); + // // for node in &self.egraph[id].nodes { + // // println!(" {node}"); + // // } + // // // self.dump_dot("/tmp/egraph-337.dot").unwrap(); + // // self.eclass_to_dot_file(id, "/tmp/egraph-337.dot")?; + // // self.eclass_to_dot_file(322.into(), "/tmp/egraph-322.dot")?; + // // self.eclass_to_dot_file(134.into(), "/tmp/egraph-134.dot")?; + // // panic!(); + // // } + // let mut buf = String::new(); + // writeln!(buf, "// eclass of {term}: {id}").unwrap(); + // for node in &self.egraph[id].nodes { + // writeln!(buf, "// {node}").unwrap(); + // } + // Ok(buf) + // } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/rule_applier.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/rule_applier.rs new file mode 100644 index 00000000000..445aade957b --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/rule_applier.rs @@ -0,0 +1,49 @@ +use super::language::ExpressionLanguage; + +pub(super) struct RuleApplier { + // source: egg::Pattern, target: egg::Pattern, + source: egg::PatternAst, + target: egg::PatternAst, +} + +impl RuleApplier { + // pub(super) fn new(source: egg::Pattern, target: egg::Pattern) -> Self { + pub(super) fn new( + source: egg::PatternAst, + target: egg::PatternAst, + ) -> Self { + Self { source, target } + } +} + +impl egg::Applier for RuleApplier { + fn apply_one( + &self, + egraph: &mut egg::EGraph, + _eclass: egg::Id, + subst: &egg::Subst, + _searcher_ast: Option<&egg::PatternAst>, + rule_name: egg::Symbol, + ) -> Vec { + let (new_id, unified) = + egraph.union_instantiations(&self.source, &self.target, subst, rule_name); + if unified { + vec![new_id] + } else { + Vec::new() + } + // let source = self.source.apply_one(egraph, eclass, subst, searcher_ast, rule_name); + // let target = self.target.apply_one(egraph, eclass, subst, searcher_ast, rule_name); + // assert_eq!(source.len(), 1); + // assert_eq!(target.len(), 1); + // let source = source[0]; + // let target = target[0]; + // let source = egraph.find(source); + // let target = egraph.find(target); + // if source == target { + // Vec::new() + // } else { + // vec![source, target] + // } + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/term_interner.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/term_interner.rs new file mode 100644 index 00000000000..db7acfd625a --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/egg/term_interner.rs @@ -0,0 +1,370 @@ +use super::language::ExpressionLanguage; +use crate::encoder::errors::SpannedEncodingResult; +use egg::{EGraph, Id, RecExpr, Symbol}; +use rustc_hash::FxHashSet; +use std::collections::BTreeMap; +use vir_crate::low::{self as vir_low}; + +pub(super) trait TermInterner { + fn try_intern_term( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + ) -> SpannedEncodingResult>; + + fn intern_term( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + ) -> SpannedEncodingResult; + + fn intern_pattern( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + variables: &BTreeMap, + ) -> SpannedEncodingResult; + + fn add(&mut self, term: ExpressionLanguage) -> Id; +} + +impl TermInterner for EGraph { + fn try_intern_term( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + ) -> SpannedEncodingResult> { + Ok(try_intern_term_rec( + self, + true_id, + false_id, + &BTreeMap::new(), + &mut Vec::new(), + term, + )) + } + + fn intern_term( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + ) -> SpannedEncodingResult { + Ok(try_intern_term_rec( + self, + true_id, + false_id, + &BTreeMap::new(), + &mut Vec::new(), + term, + ) + .unwrap_or_else(|| panic!("Failed to intern term: {term}"))) + } + + fn intern_pattern( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + variables: &BTreeMap, + ) -> SpannedEncodingResult { + Ok( + try_intern_term_rec(self, true_id, false_id, variables, &mut Vec::new(), term) + .unwrap_or_else(|| panic!("Failed to intern term: {term}")), + ) + } + + fn add(&mut self, term: ExpressionLanguage) -> Id { + self.add(term) + } +} + +impl TermInterner for RecExpr> { + fn try_intern_term( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + ) -> SpannedEncodingResult> { + Ok(try_intern_term_rec( + self, + true_id, + false_id, + &BTreeMap::new(), + &mut Vec::new(), + term, + )) + } + + fn intern_term( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + ) -> SpannedEncodingResult { + Ok(try_intern_term_rec( + self, + true_id, + false_id, + &BTreeMap::new(), + &mut Vec::new(), + term, + ) + .unwrap_or_else(|| panic!("Failed to intern term: {term}"))) + } + + fn intern_pattern( + &mut self, + term: &vir_low::Expression, + true_id: Id, + false_id: Id, + variables: &BTreeMap, + ) -> SpannedEncodingResult { + Ok( + try_intern_term_rec(self, true_id, false_id, variables, &mut Vec::new(), term) + .unwrap_or_else(|| panic!("Failed to intern term: {term}")), + ) + } + + fn add(&mut self, term: ExpressionLanguage) -> Id { + self.add(egg::ENodeOrVar::ENode(term)) + } +} + +/// This method must be called only through `intern_term` that checks its +/// precondition. That is the reason why this method is private and not part of +/// `TermInterner`. +fn try_intern_term_rec( + egraph: &mut impl TermInterner, + true_id: Id, + false_id: Id, + variables: &BTreeMap, + bound_variables: &mut Vec>, + term: &vir_low::Expression, +) -> Option { + let id = match term { + vir_low::Expression::Local(expression) => { + for frame in bound_variables { + if frame.contains(&expression.variable.name) { + return None; + } + } + if let Some(variable_id) = variables.get(&expression.variable.name) { + *variable_id + } else { + let symbol = Symbol::from(&expression.variable.name); + egraph.add(ExpressionLanguage::Variable(symbol)) + } + } + vir_low::Expression::Constant(expression) => match &expression.value { + vir_low::ConstantValue::Bool(true) => true_id, + vir_low::ConstantValue::Bool(false) => false_id, + vir_low::ConstantValue::Int(value) => egraph.add(ExpressionLanguage::Int(*value)), + vir_low::ConstantValue::BigInt(value) => { + if let Ok(value_int) = std::str::FromStr::from_str(value) { + egraph.add(ExpressionLanguage::Int(value_int)) + } else { + egraph.add(ExpressionLanguage::BigInt(Symbol::from(value))) + } + } + }, + vir_low::Expression::UnaryOp(expression) => { + let operand_id = try_intern_term_rec( + egraph, + true_id, + false_id, + variables, + bound_variables, + &expression.argument, + )?; + match expression.op_kind { + vir_low::UnaryOpKind::Not => egraph.add(ExpressionLanguage::Not(operand_id)), + vir_low::UnaryOpKind::Minus => egraph.add(ExpressionLanguage::Minus(operand_id)), + } + } + vir_low::Expression::BinaryOp(expression) => { + let left_id = try_intern_term_rec( + egraph, + true_id, + false_id, + variables, + bound_variables, + &expression.left, + )?; + let right_id = try_intern_term_rec( + egraph, + true_id, + false_id, + variables, + bound_variables, + &expression.right, + )?; + match expression.op_kind { + vir_low::BinaryOpKind::EqCmp => { + egraph.add(ExpressionLanguage::EqCmp([left_id, right_id])) + } + vir_low::BinaryOpKind::NeCmp => { + egraph.add(ExpressionLanguage::NeCmp([left_id, right_id])) + } + vir_low::BinaryOpKind::GtCmp => { + egraph.add(ExpressionLanguage::GtCmp([left_id, right_id])) + } + vir_low::BinaryOpKind::GeCmp => { + egraph.add(ExpressionLanguage::GeCmp([left_id, right_id])) + } + vir_low::BinaryOpKind::LtCmp => { + egraph.add(ExpressionLanguage::LtCmp([left_id, right_id])) + } + vir_low::BinaryOpKind::LeCmp => { + egraph.add(ExpressionLanguage::LeCmp([left_id, right_id])) + } + vir_low::BinaryOpKind::Add => { + egraph.add(ExpressionLanguage::Add([left_id, right_id])) + } + vir_low::BinaryOpKind::Sub => { + egraph.add(ExpressionLanguage::Sub([left_id, right_id])) + } + vir_low::BinaryOpKind::Mul => { + egraph.add(ExpressionLanguage::Mul([left_id, right_id])) + } + vir_low::BinaryOpKind::Div => { + egraph.add(ExpressionLanguage::Div([left_id, right_id])) + } + vir_low::BinaryOpKind::Mod => { + egraph.add(ExpressionLanguage::Mod([left_id, right_id])) + } + vir_low::BinaryOpKind::And => { + egraph.add(ExpressionLanguage::And([left_id, right_id])) + } + vir_low::BinaryOpKind::Or => { + egraph.add(ExpressionLanguage::Or([left_id, right_id])) + } + vir_low::BinaryOpKind::Implies => { + egraph.add(ExpressionLanguage::Implies([left_id, right_id])) + } + } + } + vir_low::Expression::PermBinaryOp(expression) => { + let left_id = try_intern_term_rec( + egraph, + true_id, + false_id, + variables, + bound_variables, + &expression.left, + )?; + let right_id = try_intern_term_rec( + egraph, + true_id, + false_id, + variables, + bound_variables, + &expression.right, + )?; + match expression.op_kind { + vir_low::expression::PermBinaryOpKind::Add => { + egraph.add(ExpressionLanguage::Add([left_id, right_id])) + } + vir_low::expression::PermBinaryOpKind::Sub => { + egraph.add(ExpressionLanguage::Sub([left_id, right_id])) + } + vir_low::expression::PermBinaryOpKind::Mul => { + egraph.add(ExpressionLanguage::Mul([left_id, right_id])) + } + vir_low::expression::PermBinaryOpKind::Div => { + egraph.add(ExpressionLanguage::Div([left_id, right_id])) + } + } + } + vir_low::Expression::ContainerOp(expression) => { + let mut operands = Vec::new(); + for operand in &expression.operands { + let operand_id = try_intern_term_rec( + egraph, + true_id, + false_id, + variables, + bound_variables, + operand, + )?; + operands.push(operand_id); + } + egraph.add(ExpressionLanguage::BuiltinFuncApp( + Symbol::from(format!("{:?}", expression.kind)), + operands, + )) + } + vir_low::Expression::DomainFuncApp(expression) => { + let symbol = Symbol::from(&expression.function_name); + let arguments = expression + .arguments + .iter() + .map(|argument| { + try_intern_term_rec( + egraph, + true_id, + false_id, + variables, + bound_variables, + argument, + ) + }) + .collect::>>()?; + egraph.add(ExpressionLanguage::FuncApp(symbol, arguments)) + } + vir_low::Expression::LabelledOld(expression) => try_intern_term_rec( + egraph, + true_id, + false_id, + variables, + bound_variables, + &expression.base, + )?, + // FIXME: It does not make sense to intern the contents of these + // expressions because in the interning table we store only the id of + // the root. + vir_low::Expression::Conditional(_) + | vir_low::Expression::Quantifier(_) + | vir_low::Expression::LetExpr(_) => { + return None; + } + // vir_low::Expression::Conditional(expression) => { + // try_intern_term_rec(egraph, true_id, false_id, variables, bound_variables,&expression.guard)?; + // try_intern_term_rec(egraph, true_id, false_id, variables, bound_variables,&expression.then_expr)?; + // try_intern_term_rec(egraph, true_id, false_id, variables, bound_variables,&expression.else_expr)?; + // return None; + // } + // vir_low::Expression::Quantifier(expression) => { + // bound_variables.push(expression.variables.iter().map(|variable| variable.name.clone()).collect()); + // try_intern_term_rec(egraph, true_id, false_id, variables, bound_variables, &expression.body)?; + // for trigger in &expression.triggers { + // for term in &trigger.terms { + // try_intern_term_rec(egraph, true_id, false_id, variables, bound_variables, term)?; + // } + // } + // bound_variables.pop(); + // return None; + // } + // vir_low::Expression::LetExpr(expression) => { + // try_intern_term_rec(egraph, true_id, false_id, variables, bound_variables, &expression.def)?; + // bound_variables.push(vec![expression.variable.name.clone()].into_iter().collect()); + // try_intern_term_rec(egraph, true_id, false_id, variables, bound_variables, &expression.body)?; + // bound_variables.pop(); + // return None; + // } + vir_low::Expression::MagicWand(_) + | vir_low::Expression::PredicateAccessPredicate(_) + | vir_low::Expression::FieldAccessPredicate(_) + | vir_low::Expression::Unfolding(_) + | vir_low::Expression::FuncApp(_) + | vir_low::Expression::InhaleExhale(_) + | vir_low::Expression::Field(_) => { + unreachable!("term: {}", term); + } + }; + Some(id) +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/entry.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/entry.rs new file mode 100644 index 00000000000..2ab62014c85 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/entry.rs @@ -0,0 +1,41 @@ +use vir_crate::low::{self as vir_low}; + +pub(in super::super) enum HeapEntry { + Comment(vir_low::ast::statement::Comment), + Label(vir_low::ast::statement::Label), + /// An inhale entry that can be purified. + InhalePredicate( + vir_low::ast::expression::PredicateAccessPredicate, + vir_low::Position, + ), + /// An exhale entry that can be purified. + ExhalePredicate( + vir_low::ast::expression::PredicateAccessPredicate, + vir_low::Position, + ), + /// A generic inhale entry that cannot be purified. + InhaleGeneric(vir_low::ast::statement::Inhale), + /// A generic exhale entry that cannot be purified. + ExhaleGeneric(vir_low::ast::statement::Exhale), + Assume(vir_low::ast::statement::Assume), + Assert(vir_low::ast::statement::Assert), +} + +impl std::fmt::Display for HeapEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + HeapEntry::Comment(statement) => write!(f, "{statement}"), + HeapEntry::Label(statement) => write!(f, "{statement}"), + HeapEntry::InhalePredicate(predicate, _position) => { + write!(f, "inhale-predicate {predicate}") + } + HeapEntry::ExhalePredicate(predicate, _position) => { + write!(f, "exhale-predicate {predicate}") + } + HeapEntry::InhaleGeneric(statement) => write!(f, "{statement}"), + HeapEntry::ExhaleGeneric(statement) => write!(f, "{statement}"), + HeapEntry::Assume(statement) => write!(f, "{statement}"), + HeapEntry::Assert(statement) => write!(f, "{statement}"), + } + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/finalizer.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/finalizer.rs new file mode 100644 index 00000000000..56ef2dec8f3 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/finalizer.rs @@ -0,0 +1,347 @@ +use super::{ + lifetime_tokens::LifetimeTokens, + predicate_snapshots::{ + check_non_aliased_snap_calls_purified, purify_snap_calls, purify_snap_calls_vec, + purify_snap_calls_vec_with_retry, PredicateSnapshots, + }, + state::{PredicateInstance, PredicateInstanceState}, + HeapEntry, HeapState, Location, +}; +use crate::encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + middle::core_proof::transformations::symbolic_execution::{ + egg::EGraphState, program_context::ProgramContext, simplifier::simplify_expression, + trace::Trace, utils::all_heap_independent, + }, +}; +use prusti_common::config; +use std::collections::BTreeMap; +use vir_crate::{ + common::{display, expression::SyntacticEvaluation}, + low::{self as vir_low, expression::visitors::ExpressionFallibleFolder}, +}; + +pub(super) struct TraceFinalizer<'a> { + source_filename: &'a str, + procedure_name: &'a str, + purification_failure_count: usize, + final_state: &'a HeapState, + trace: Vec, + new_variables: Vec, + new_labels: Vec, + predicate_snapshots: PredicateSnapshots, + predicate_snapshots_at_label: BTreeMap, + lifetime_tokens: LifetimeTokens, + solver: &'a mut EGraphState, + program: &'a ProgramContext<'a>, +} + +impl<'a> TraceFinalizer<'a> { + pub(super) fn new( + source_filename: &'a str, + procedure_name: &'a str, + final_state: &'a HeapState, + solver: &'a mut EGraphState, + program: &'a ProgramContext<'a>, + ) -> Self { + Self { + source_filename, + procedure_name, + purification_failure_count: 0, + final_state, + trace: Vec::new(), + new_variables: Vec::new(), + new_labels: Vec::new(), + predicate_snapshots: Default::default(), + predicate_snapshots_at_label: Default::default(), + lifetime_tokens: Default::default(), + solver, + program, + } + } + + pub(super) fn into_trace(self) -> Trace { + let mut variables = self.new_variables; + variables.extend(self.predicate_snapshots.into_variables()); + variables.extend(self.lifetime_tokens.into_variables()); + Trace { + statements: self.trace, + variables, + labels: self.new_labels, + } + } + + pub(super) fn add_variables( + &mut self, + new_variables: &[vir_low::VariableDecl], + ) -> SpannedEncodingResult<()> { + self.new_variables.extend_from_slice(new_variables); + Ok(()) + } + + pub(super) fn add_labels( + &mut self, + new_labels: &[vir_low::Label], + ) -> SpannedEncodingResult<()> { + self.new_labels.extend_from_slice(new_labels); + Ok(()) + } + + pub(super) fn add_entry( + &mut self, + location: Location, + entry: &HeapEntry, + ) -> SpannedEncodingResult<()> { + match entry { + HeapEntry::Comment(statement) => { + self.trace + .push(vir_low::Statement::Comment(statement.clone())); + } + HeapEntry::Label(statement) => { + self.save_state(statement.label.clone()); + self.trace + .push(vir_low::Statement::Label(statement.clone())); + } + HeapEntry::InhalePredicate(predicate, position) => { + let predicate_kind = self.program.get_predicate_kind(&predicate.name); + let arguments = purify_snap_calls_vec_with_retry( + &self.predicate_snapshots, + &self.predicate_snapshots_at_label, + self.solver, + self.program, + predicate.arguments.clone(), + )?; + if predicate_kind == vir_low::PredicateKind::LifetimeToken { + self.purify_lifetime_token_inhale(predicate, *position)?; + } else if let Some(predicate_instance) = + self.is_purified_inhale(location, predicate) + { + let snapshot = predicate_instance.snapshot.clone(); + if config::report_symbolic_execution_purification() { + self.trace.push(vir_low::Statement::comment(format!( + "purified out: {entry}" + ))); + } + if let Some(snapshot_variable_name) = snapshot { + self.predicate_snapshots.register_predicate_snapshot( + self.program, + &predicate.name, + arguments, + snapshot_variable_name, + ); + } else { + self.predicate_snapshots.create_predicate_snapshot( + self.program, + &predicate.name, + arguments, + ); + } + } else { + self.report_purification_failure(*position)?; + self.trace.push(vir_low::Statement::inhale( + vir_low::Expression::predicate_access_predicate( + predicate.name.clone(), + arguments, + *(predicate.permission).clone(), + predicate.position, + ), + *position, + )); + } + } + HeapEntry::ExhalePredicate(predicate, position) => { + let predicate_kind = self.program.get_predicate_kind(&predicate.name); + let arguments = purify_snap_calls_vec_with_retry( + &self.predicate_snapshots, + &self.predicate_snapshots_at_label, + self.solver, + self.program, + predicate.arguments.clone(), + )?; + if predicate_kind == vir_low::PredicateKind::LifetimeToken { + self.purify_lifetime_token_exhale(predicate, *position)?; + } else if self.is_purified_exhale(location, predicate) { + if config::report_symbolic_execution_purification() { + self.trace.push(vir_low::Statement::comment(format!( + "purified out: {entry}" + ))); + } + self.predicate_snapshots.destroy_predicate_snapshot( + &predicate.name, + &arguments, + self.solver, + )?; + } else { + self.report_purification_failure(*position)?; + self.trace.push(vir_low::Statement::exhale( + vir_low::Expression::predicate_access_predicate( + predicate.name.clone(), + arguments, + *(predicate.permission).clone(), + predicate.position, + ), + *position, + )); + } + } + HeapEntry::InhaleGeneric(statement) => { + // eprintln!("InhaleGeneric: {}", statement.expression); + self.add_expression_entry( + &statement.expression, + statement.position, + vir_low::Statement::inhale, + )?; + } + HeapEntry::ExhaleGeneric(statement) => { + self.add_expression_entry( + &statement.expression, + statement.position, + vir_low::Statement::exhale, + )?; + } + HeapEntry::Assume(statement) => { + self.add_expression_entry( + &statement.expression, + statement.position, + vir_low::Statement::assume, + )?; + } + HeapEntry::Assert(statement) => { + self.add_expression_entry( + &statement.expression, + statement.position, + vir_low::Statement::assert, + )?; + } + } + Ok(()) + } + + fn add_expression_entry( + &mut self, + expression: &vir_low::Expression, + position: vir_low::Position, + constructor: fn(vir_low::Expression, vir_low::Position) -> vir_low::Statement, + ) -> SpannedEncodingResult<()> { + // let simplified_expression = simplify_expression(expression.clone(), self.program, self.solver)?; + // if &simplified_expression != expression { + // self.solver.intern_heap_independent_subexpressions(&simplified_expression)?; + // } + let simplified_expression = expression.clone(); + let expression = self.purify_snap_calls(simplified_expression)?; + check_non_aliased_snap_calls_purified(&expression, self.program); + self.trace.push(constructor(expression, position)); + Ok(()) + } + + fn report_purification_failure( + &mut self, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + if config::report_symbolic_execution_failures() { + prusti_common::report::log::report_with_writer( + "symbex_purification_failures", + format!( + "{}.{}.{}.dot", + self.source_filename, self.procedure_name, self.purification_failure_count + ), + |writer| self.solver.to_graphviz(writer).unwrap(), + ); + self.trace.push(vir_low::Statement::comment(format!( + "Failed to purify. Failure id: {}", + self.purification_failure_count + ))); + self.trace + .push(vir_low::Statement::assert(false.into(), position)); + self.purification_failure_count += 1; + } + Ok(()) + } + + fn is_purified_inhale( + &self, + location: Location, + predicate: &vir_low::expression::PredicateAccessPredicate, + ) -> Option<&PredicateInstance> { + if let Some(predicate_state) = self.final_state.get_predicate(&predicate.name) { + for predicate_instance in predicate_state.get_instances() { + if predicate_instance.inhale_location == location { + // We can purify out exhaled predicates. + // + // FIXME: We also can purify + // `PredicateInstanceState::FreshNonAliased`, but purifying + // it too early may miss an exhale of the predicate and lead + // to an usoudness. (The unsoudness can be replaced with a + // verification error by uncommenting the asserts in + // `try_removing_predicate_instance`.) + if matches!( + predicate_instance.state, + PredicateInstanceState::Exhaled(_) + | PredicateInstanceState::FreshNonAliased + ) { + return Some(predicate_instance); + } + } + } + } + None + } + + fn is_purified_exhale( + &self, + location: Location, + predicate: &vir_low::expression::PredicateAccessPredicate, + ) -> bool { + if let Some(predicate_state) = self.final_state.get_predicate(&predicate.name) { + for predicate_instance in predicate_state.get_instances() { + if let PredicateInstanceState::Exhaled(exhale_location) = predicate_instance.state { + if exhale_location == location { + assert_eq!(*predicate.permission, predicate_instance.permission_amount); + return true; + } + } + } + } + false + } + + fn save_state(&mut self, label: String) { + assert!(self + .predicate_snapshots_at_label + .insert(label, self.predicate_snapshots.clone()) + .is_none()); + } + + fn purify_snap_calls( + &mut self, + expression: vir_low::Expression, + ) -> SpannedEncodingResult { + let result = purify_snap_calls( + &self.predicate_snapshots, + &self.predicate_snapshots_at_label, + self.solver, + self.program, + expression, + )?; + check_non_aliased_snap_calls_purified(&result, self.program); + Ok(result) + } + + pub(super) fn purify_lifetime_token_inhale( + &mut self, + predicate: &vir_low::PredicateAccessPredicate, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + self.lifetime_tokens + .inhale_predicate(&mut self.trace, self.solver, predicate, position) + } + + pub(super) fn purify_lifetime_token_exhale( + &mut self, + predicate: &vir_low::PredicateAccessPredicate, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + self.lifetime_tokens + .exhale_predicate(&mut self.trace, self.solver, predicate, position) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/graphviz.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/graphviz.rs new file mode 100644 index 00000000000..252c4aa4728 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/graphviz.rs @@ -0,0 +1,29 @@ +use super::HeapEntry; +use crate::encoder::middle::core_proof::transformations::symbolic_execution::trace_builder::ExecutionTraceHeapView; +use vir_crate::common::graphviz::{escape_html_wrap, Graph, ToGraphviz}; + +impl<'a> ToGraphviz for ExecutionTraceHeapView<'a> { + fn to_graph(&self) -> Graph { + let mut graph = Graph::with_columns(&["statement"]); + for (block_id, block) in self.iter_blocks().enumerate() { + let mut node_builder = graph.create_node(format!("block{block_id}")); + for statement in block.iter_entries() { + let statement_string = match statement { + HeapEntry::Comment(statement) => { + format!( + "{}", + escape_html_wrap(statement) + ) + } + _ => escape_html_wrap(statement.to_string()), + }; + node_builder.add_row_sequence(vec![statement_string]); + } + node_builder.build(); + if let Some(parent) = block.parent() { + graph.add_regular_edge(format!("block{parent}"), format!("block{block_id}")); + } + } + graph + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/lifetime_tokens.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/lifetime_tokens.rs new file mode 100644 index 00000000000..ff2f058291b --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/lifetime_tokens.rs @@ -0,0 +1,157 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::transformations::symbolic_execution::egg::EGraphState, +}; +use vir_crate::{ + common::expression::BinaryOperationHelpers, + low::{self as vir_low}, +}; + +#[derive(Default)] +pub(super) struct LifetimeTokens { + permission_variables: Vec, +} + +impl LifetimeTokens { + pub(super) fn into_variables(self) -> Vec { + let mut variables = Vec::new(); + for permission_variable in self.permission_variables { + for version in 0..permission_variable.permission_variable_version + 1 { + variables.push(permission_variable.create_variable(version)); + } + } + variables + } + + fn find_permission_variable( + &mut self, + solver: &EGraphState, + lifetime: &vir_low::Expression, + ) -> SpannedEncodingResult> { + for permission_variable in &mut self.permission_variables { + if solver.is_equal(&permission_variable.lifetime, lifetime)? { + return Ok(Some(permission_variable)); + } + } + Ok(None) + } + + pub(super) fn inhale_predicate( + &mut self, + statements: &mut Vec, + solver: &EGraphState, + predicate: &vir_low::PredicateAccessPredicate, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + assert_eq!(predicate.arguments.len(), 1); + if let Some(permission_variable) = + self.find_permission_variable(solver, &predicate.arguments[0])? + { + let current_permission_amount_variable = + permission_variable.current_permission_amount_variable(); + let new_permission_amount_variable = + permission_variable.new_permission_amount_variable(); + statements.push(vir_low::Statement::assume( + vir_low::Expression::equals( + new_permission_amount_variable.into(), + vir_low::Expression::perm_binary_op_no_pos( + vir_low::PermBinaryOpKind::Add, + current_permission_amount_variable.into(), + (*predicate.permission).clone(), + ), + ) + .set_default_position(position), + position, + )); + } else { + let permission_variable = PermissionVariable { + lifetime: predicate.arguments[0].clone(), + permission_variable_name: format!( + "lifetime_token${}", + self.permission_variables.len() + ), + permission_variable_version: 0, + }; + let new_permission_amount_variable = + permission_variable.current_permission_amount_variable(); + self.permission_variables.push(permission_variable); + statements.push(vir_low::Statement::assume( + vir_low::Expression::equals( + new_permission_amount_variable.into(), + (*predicate.permission).clone(), + ) + .set_default_position(position), + position, + )); + } + Ok(()) + } + + pub(super) fn exhale_predicate( + &mut self, + statements: &mut Vec, + solver: &EGraphState, + predicate: &vir_low::PredicateAccessPredicate, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + assert_eq!(predicate.arguments.len(), 1); + if let Some(permission_variable) = + self.find_permission_variable(solver, &predicate.arguments[0])? + { + let current_permission_amount_variable = + permission_variable.current_permission_amount_variable(); + let new_permission_amount_variable = + permission_variable.new_permission_amount_variable(); + statements.push(vir_low::Statement::assert( + vir_low::Expression::greater_equals( + current_permission_amount_variable.clone().into(), + (*predicate.permission).clone(), + ) + .set_default_position(position), + position, + )); + statements.push(vir_low::Statement::assume( + vir_low::Expression::equals( + new_permission_amount_variable.into(), + vir_low::Expression::perm_binary_op_no_pos( + vir_low::PermBinaryOpKind::Sub, + current_permission_amount_variable.into(), + (*predicate.permission).clone(), + ), + ) + .set_default_position(position), + position, + )); + } else { + unreachable!("Exhaling a predicate that was not inhaled before: {predicate}"); + } + Ok(()) + } +} + +struct PermissionVariable { + /// An expression that indicates the lifetime that is mapped to this + /// variable. + lifetime: vir_low::Expression, + /// The name of the variable used to track the permission amount of this + /// lifetime. + permission_variable_name: String, + /// The SSA version of the permission variable. + permission_variable_version: u32, +} + +impl PermissionVariable { + fn create_variable(&self, version: u32) -> vir_low::VariableDecl { + let variable_name = format!("{}${}", self.permission_variable_name, version); + vir_low::VariableDecl::new(variable_name, vir_low::Type::Perm) + } + + fn current_permission_amount_variable(&self) -> vir_low::VariableDecl { + self.create_variable(self.permission_variable_version) + } + + fn new_permission_amount_variable(&mut self) -> vir_low::VariableDecl { + self.permission_variable_version += 1; + self.create_variable(self.permission_variable_version) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/mod.rs new file mode 100644 index 00000000000..5665ef13908 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/mod.rs @@ -0,0 +1,197 @@ +mod graphviz; +mod entry; +mod state; +mod finalizer; +mod predicate_snapshots; +mod lifetime_tokens; + +use super::{ + program_context::ProgramContext, + trace::Trace, + trace_builder::{ExecutionTraceBuilder, ExecutionTraceHeapView}, +}; +use crate::encoder::errors::SpannedEncodingResult; +use log::debug; +use vir_crate::low::{self as vir_low}; + +use self::finalizer::TraceFinalizer; +pub(super) use self::{entry::HeapEntry, state::HeapState}; + +impl<'a> ExecutionTraceBuilder<'a> { + pub(super) fn heap_comment( + &mut self, + statement: vir_low::ast::statement::Comment, + ) -> SpannedEncodingResult<()> { + self.add_heap_entry(HeapEntry::Comment(statement))?; + Ok(()) + } + + pub(super) fn heap_label( + &mut self, + statement: vir_low::ast::statement::Label, + ) -> SpannedEncodingResult<()> { + let state = self.current_heap_state_mut(); + state.save_state(statement.label.clone()); + self.add_heap_entry(HeapEntry::Label(statement))?; + Ok(()) + } + + pub(super) fn heap_assume( + &mut self, + expression: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + assert!( + !position.is_default(), + "assume {expression} with default position" + ); + self.add_heap_entry(HeapEntry::Assume(vir_low::ast::statement::Assume { + expression, + position, + }))?; + Ok(()) + } + + pub(super) fn heap_assert( + &mut self, + expression: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + self.add_heap_entry(HeapEntry::Assert(vir_low::ast::statement::Assert { + expression, + position, + }))?; + Ok(()) + } + + fn next_location(&self) -> Location { + let view = self.heap_view(); + Location { + block_id: view.block_count() - 1, + entry_id: view.last_block_entry_count(), + } + } + + pub(super) fn heap_inhale_predicate( + &mut self, + predicate: vir_low::ast::expression::PredicateAccessPredicate, + program: &ProgramContext, + // is_instance_non_aliased: bool, + // non_aliased_predicate_instances: &'a FxHashSet, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + let next_location = self.next_location(); + let (state, solver) = self.current_heap_and_egraph_state_mut(); + // let (state, solver) = self.current_heap_and_egraph_state_mut(); + // solver.saturate()?; + // let mut is_instance_non_aliased = false; + // for non_aliased_predicate in non_aliased_predicate_instances { + // if non_aliased_predicate.name == predicate.name { + // if arguments_match( + // &non_aliased_predicate.arguments, + // &predicate.arguments, + // solver, + // )? { + // is_instance_non_aliased = true; + // break; + // } + // } + // } + state.add_predicate_instance( + solver, + program, + &predicate, + // is_instance_non_aliased, + next_location, + )?; + self.add_heap_entry(HeapEntry::InhalePredicate(predicate, position)) + } + + pub(super) fn heap_inhale_generic( + &mut self, + expression: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + let state = self.current_heap_state_mut(); + for predicate_name in expression.collect_access_predicate_names() { + state.mark_predicate_instances_seen_qp_inhale(&predicate_name); + } + self.add_heap_entry(HeapEntry::InhaleGeneric(vir_low::ast::statement::Inhale { + expression, + position, + })) + } + + pub(super) fn heap_exhale_predicate( + &mut self, + predicate: vir_low::ast::expression::PredicateAccessPredicate, + program: &ProgramContext, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + let next_location = self.next_location(); + let (state, solver) = self.current_heap_and_egraph_state_mut(); + state.try_removing_predicate_instance(solver, program, &predicate, next_location)?; + self.add_heap_entry(HeapEntry::ExhalePredicate(predicate, position))?; + Ok(()) + } + + pub(super) fn heap_exhale_generic( + &mut self, + expression: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + let state = self.current_heap_state_mut(); + for predicate_name in expression.collect_access_predicate_names() { + state.mark_predicate_instances_seen_qp_exhale(&predicate_name); + } + self.add_heap_entry(HeapEntry::ExhaleGeneric(vir_low::ast::statement::Exhale { + expression, + position, + })) + } + + pub(super) fn heap_finalize_trace( + &mut self, + program: &ProgramContext, + ) -> SpannedEncodingResult { + debug!("Finalizing trace"); + // let (state, solver) = self.current_heap_and_egraph_state(); + let mut solver = self.steal_current_egraph_solver(); + let state = self.current_heap_state(); + let view = self.heap_view(); + let last_block_id = view.last_block_id(); + let mut trace_finalizer = TraceFinalizer::new( + self.source_filename, + self.procedure_name, + state, + &mut solver, + program, + ); + self.finalize_trace_for_block(&mut trace_finalizer, view, last_block_id)?; + Ok(trace_finalizer.into_trace()) + } + + fn finalize_trace_for_block( + &self, + trace_finalizer: &mut TraceFinalizer, + view: ExecutionTraceHeapView, + block_id: usize, + ) -> SpannedEncodingResult<()> { + let block = view.get_block(block_id); + if let Some(parent_id) = block.parent() { + self.finalize_trace_for_block(trace_finalizer, view, parent_id)?; + } + trace_finalizer.add_variables(block.get_new_variables())?; + trace_finalizer.add_labels(block.get_new_labels())?; + for (entry_id, entry) in block.iter_entries().enumerate() { + trace_finalizer.add_entry(Location { block_id, entry_id }, entry)?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Location { + block_id: usize, + entry_id: usize, +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/predicate_snapshots.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/predicate_snapshots.rs new file mode 100644 index 00000000000..8ff0b4218b3 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/predicate_snapshots.rs @@ -0,0 +1,416 @@ +use crate::encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + middle::core_proof::transformations::symbolic_execution::{ + egg::EGraphState, + program_context::ProgramContext, + utils::{all_heap_independent, arguments_match, is_place_non_aliased}, + }, +}; +use log::debug; +use std::collections::BTreeMap; +use vir_crate::{ + common::display, + low::{ + self as vir_low, + expression::visitors::{ExpressionFallibleFolder, ExpressionWalker}, + }, +}; + +pub(super) fn check_non_aliased_snap_calls_purified<'a>( + expression: &vir_low::Expression, + program: &'a ProgramContext<'a>, +) { + struct Walker<'a> { + program: &'a ProgramContext<'a>, + } + impl<'a> ExpressionWalker for Walker<'a> { + fn walk_func_app_enum(&mut self, func_app: &vir_low::expression::FuncApp) { + self.walk_func_app(func_app); + let function = self.program.get_function(&func_app.function_name); + assert_eq!(function.parameters.len(), func_app.arguments.len()); + match function.kind { + vir_low::FunctionKind::CallerFor => {} + vir_low::FunctionKind::SnapRange => {} + vir_low::FunctionKind::MemoryBlockBytes => {} + vir_low::FunctionKind::Snap => { + assert!( + !is_place_non_aliased(&func_app.arguments[0]), + "func_app: {func_app}" + ); + } + } + } + } + let mut purifier = Walker { program }; + purifier.walk_expression(expression) +} + +pub(super) fn purify_snap_calls_vec_with_retry<'a>( + predicate_snapshots: &'a PredicateSnapshots, + predicate_snapshots_at_label: &'a BTreeMap, + solver: &'a mut EGraphState, + program: &'a ProgramContext<'a>, + expressions: Vec, +) -> SpannedEncodingResult> { + let mut expressions = purify_snap_calls_vec( + predicate_snapshots, + predicate_snapshots_at_label, + solver, + program, + expressions, + )?; + if !all_heap_independent(&expressions) { + solver.saturate()?; + expressions = purify_snap_calls_vec( + predicate_snapshots, + predicate_snapshots_at_label, + solver, + program, + expressions, + )?; + } + solver.intern_heap_independent_terms(&expressions)?; + for expression in &expressions { + check_non_aliased_snap_calls_purified(expression, program); + } + Ok(expressions) +} + +pub(super) fn purify_snap_calls_vec<'a>( + predicate_snapshots: &'a PredicateSnapshots, + predicate_snapshots_at_label: &'a BTreeMap, + solver: &'a mut EGraphState, + program: &'a ProgramContext<'a>, + original_expressions: Vec, +) -> SpannedEncodingResult> { + let mut expressions = Vec::new(); + for expression in original_expressions { + expressions.push(purify_snap_calls( + predicate_snapshots, + predicate_snapshots_at_label, + solver, + program, + expression, + )?); + } + Ok(expressions) +} + +pub(super) fn purify_snap_calls<'a>( + predicate_snapshots: &'a PredicateSnapshots, + predicate_snapshots_at_label: &'a BTreeMap, + solver: &'a mut EGraphState, + program: &'a ProgramContext<'a>, + expression: vir_low::Expression, +) -> SpannedEncodingResult { + struct Purifier<'a> { + predicate_snapshots: &'a PredicateSnapshots, + predicate_snapshots_at_label: &'a BTreeMap, + solver: &'a mut EGraphState, + program: &'a ProgramContext<'a>, + label: Option, + argument_purified: bool, + } + impl<'a> ExpressionFallibleFolder for Purifier<'a> { + type Error = SpannedEncodingError; + + fn fallible_fold_func_app_enum( + &mut self, + func_app: vir_low::expression::FuncApp, + ) -> Result { + let old_argument_purified = self.argument_purified; + let func_app = self.fallible_fold_func_app(func_app)?; + if self.argument_purified && all_heap_independent(&func_app.arguments) { + if self + .solver + .intern_heap_independent_terms(&func_app.arguments)? + { + self.solver.saturate()?; + } + } + self.argument_purified = old_argument_purified; + let function = self.program.get_function(&func_app.function_name); + assert_eq!(function.parameters.len(), func_app.arguments.len()); + match function.kind { + vir_low::FunctionKind::CallerFor => todo!("func_app: {func_app}"), + vir_low::FunctionKind::SnapRange => Ok(vir_low::Expression::FuncApp(func_app)), + vir_low::FunctionKind::MemoryBlockBytes | vir_low::FunctionKind::Snap => { + if let Some(snapshot_variable) = + self.resolve_snapshot(&func_app.function_name, &func_app.arguments)? + { + self.argument_purified = true; + Ok(vir_low::Expression::local( + snapshot_variable, + func_app.position, + )) + } else { + Ok(vir_low::Expression::FuncApp(func_app)) + } + } + } + } + + fn fallible_fold_labelled_old( + &mut self, + mut labelled_old: vir_low::expression::LabelledOld, + ) -> Result { + std::mem::swap(&mut labelled_old.label, &mut self.label); + labelled_old.base = self.fallible_fold_expression_boxed(labelled_old.base)?; + std::mem::swap(&mut labelled_old.label, &mut self.label); + Ok(labelled_old) + } + } + impl<'a> Purifier<'a> { + fn resolve_snapshot( + &mut self, + function_name: &str, + arguments: &[vir_low::Expression], + ) -> SpannedEncodingResult> { + let predicate_snapshots = if let Some(label) = &self.label { + self.predicate_snapshots_at_label.get(label).unwrap() + } else { + self.predicate_snapshots + }; + let Some(predicate_name) = self.program.get_snapshot_predicate(function_name) else { + // The final snapshot function is already pure and, + // therefore, is not mapped to a predicate. + return Ok(None); + }; + predicate_snapshots.find_snapshot(predicate_name, arguments, self.solver) + } + } + let mut purifier = Purifier { + predicate_snapshots, + predicate_snapshots_at_label, + solver, + program, + label: None, + argument_purified: false, + }; + purifier.fallible_fold_expression(expression) +} + +#[derive(Default, Clone)] +pub(super) struct PredicateSnapshots { + snapshots: BTreeMap>, + variables: Vec, +} + +impl std::fmt::Display for PredicateSnapshots { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (predicate_name, snapshots) in &self.snapshots { + writeln!(f, "Predicate {}", predicate_name)?; + for snapshot in snapshots { + writeln!(f, " {}", snapshot)?; + } + } + Ok(()) + } +} + +impl PredicateSnapshots { + /// Method to be used during the initial symbolic execution. + pub(super) fn create_non_aliased_predicate_snapshot( + &mut self, + program_context: &ProgramContext, + predicate_name: &str, + arguments: Vec, + ) -> SpannedEncodingResult { + let predicate_snapshots = self + .snapshots + .entry(predicate_name.to_string()) + .or_default(); + let snapshot_variable_name = format!( + "snapshot_non_aliased${}$round{}${}", + predicate_name, + program_context.get_purification_round(), + predicate_snapshots.len() + ); + let snapshot = if let Some(ty) = program_context.get_snapshot_type(predicate_name) { + let snapshot = vir_low::VariableDecl::new(snapshot_variable_name.clone(), ty); + self.variables.push(snapshot.clone()); + PredicateSnapshotState::Inhaled(snapshot) + } else { + PredicateSnapshotState::NoSnapshot + }; + assert!( + all_heap_independent(&arguments), + "arguments: {}", + display::cjoin(&arguments) + ); + predicate_snapshots.push(PredicateSnapshot { + arguments, + snapshot, + }); + Ok(snapshot_variable_name) + } + + /// Method to be used by the finalizer. + pub(super) fn register_predicate_snapshot( + &mut self, + program_context: &ProgramContext, + predicate_name: &str, + arguments: Vec, + snapshot_variable_name: String, + ) { + let predicate_snapshots = self + .snapshots + .entry(predicate_name.to_string()) + .or_default(); + let snapshot = if let Some(ty) = program_context.get_snapshot_type(predicate_name) { + let snapshot = vir_low::VariableDecl::new(snapshot_variable_name, ty); + self.variables.push(snapshot.clone()); + PredicateSnapshotState::Inhaled(snapshot) + } else { + PredicateSnapshotState::NoSnapshot + }; + // assert!(all_heap_independent(&predicate.arguments), "arguments: {}", display::cjoin(&predicate.arguments)); + predicate_snapshots.push(PredicateSnapshot { + arguments, + snapshot, + }); + } + + /// Method to be used by the finalizer. + pub(super) fn create_predicate_snapshot( + &mut self, + program_context: &ProgramContext, + predicate_name: &str, + arguments: Vec, + ) { + let predicate_snapshots = self + .snapshots + .entry(predicate_name.to_string()) + .or_default(); + let snapshot_variable_name = format!( + "snapshot${}$round{}${}", + predicate_name, + program_context.get_purification_round(), + predicate_snapshots.len() + ); + let snapshot = if let Some(ty) = program_context.get_snapshot_type(predicate_name) { + let snapshot = vir_low::VariableDecl::new(snapshot_variable_name, ty); + self.variables.push(snapshot.clone()); + PredicateSnapshotState::Inhaled(snapshot) + } else { + PredicateSnapshotState::NoSnapshot + }; + // assert!(all_heap_independent(&predicate.arguments), "arguments: {}", display::cjoin(&predicate.arguments)); + predicate_snapshots.push(PredicateSnapshot { + arguments, + snapshot, + }); + } + + pub(super) fn destroy_predicate_snapshot( + &mut self, + // predicate: &vir_low::expression::PredicateAccessPredicate, + predicate_name: &str, + arguments: &[vir_low::Expression], + solver: &mut EGraphState, + ) -> SpannedEncodingResult<()> { + let predicate_snapshots = self + .snapshots + .get_mut(predicate_name) + .unwrap_or_else(|| panic!("no key: {predicate_name} {}", display::cjoin(arguments))); + for predicate_snapshot in predicate_snapshots.iter_mut() { + if predicate_snapshot.snapshot.is_not_exhaled() + && predicate_snapshot.matches_arguments(arguments, solver)? + { + predicate_snapshot.snapshot = PredicateSnapshotState::Exhaled; + return Ok(()); + } + } + solver.saturate()?; + for predicate_snapshot in predicate_snapshots { + if predicate_snapshot.snapshot.is_not_exhaled() + && predicate_snapshot.matches_arguments(arguments, solver)? + { + predicate_snapshot.snapshot = PredicateSnapshotState::Exhaled; + return Ok(()); + } + } + unreachable!( + "snapshot not found: {predicate_name} {}", + display::cjoin(arguments) + ); + } + + pub(super) fn find_snapshot( + &self, + predicate_name: &str, + arguments: &[vir_low::Expression], + solver: &EGraphState, + ) -> SpannedEncodingResult> { + if let Some(predicate_snapshots) = self.snapshots.get(predicate_name) { + for predicate_snapshot in predicate_snapshots { + if let PredicateSnapshotState::Inhaled(snapshot) = &predicate_snapshot.snapshot { + if predicate_snapshot.matches_arguments(arguments, solver)? { + return Ok(Some(snapshot.clone())); + } + } + } + } + Ok(None) + } + + pub(super) fn into_variables(self) -> Vec { + self.variables + } +} + +#[derive(Clone, derive_more::Display)] +enum PredicateSnapshotState { + /// The snapshot is valid. + Inhaled(vir_low::VariableDecl), + /// The snapshot was exhaled and no longer valid. + Exhaled, + /// The predicate does not have a snapshot. + NoSnapshot, +} + +impl PredicateSnapshotState { + pub(super) fn is_not_exhaled(&self) -> bool { + matches!( + self, + PredicateSnapshotState::Inhaled(_) | PredicateSnapshotState::NoSnapshot + ) + } +} + +#[derive(Clone)] +pub(super) struct PredicateSnapshot { + /// Predicate arguments. + arguments: Vec, + /// None means that the corresponding predicate was exhaled. + snapshot: PredicateSnapshotState, +} + +impl std::fmt::Display for PredicateSnapshot { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", display::cjoin(&self.arguments), self.snapshot) + } +} + +impl PredicateSnapshot { + pub(super) fn matches( + &self, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + solver: &EGraphState, + ) -> SpannedEncodingResult { + arguments_match(&self.arguments, &predicate.arguments, solver) + } + + pub(super) fn matches_arguments( + &self, + arguments: &[vir_low::Expression], + solver: &EGraphState, + ) -> SpannedEncodingResult { + debug!( + "matches_arguments:\n self: {}\n other: {}", + display::cjoin(&self.arguments), + display::cjoin(arguments) + ); + arguments_match(&self.arguments, arguments, solver) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/state.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/state.rs new file mode 100644 index 00000000000..3cc796fcc39 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/heap/state.rs @@ -0,0 +1,370 @@ +use super::{ + predicate_snapshots::{ + purify_snap_calls, purify_snap_calls_vec, purify_snap_calls_vec_with_retry, + PredicateSnapshots, + }, + Location, +}; +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::transformations::symbolic_execution::{ + egg::EGraphState, + program_context::ProgramContext, + utils::{all_heap_independent, arguments_match, is_place_non_aliased}, + }, +}; +use std::collections::BTreeMap; +use vir_crate::{ + common::display, + low::{self as vir_low, operations::ty::Typed}, +}; + +#[derive(Default, Clone)] +pub(in super::super) struct HeapState { + /// A map from predicate names to their state. + predicates: BTreeMap, + predicate_snapshots: PredicateSnapshots, + predicate_snapshots_at_label: BTreeMap, +} + +impl std::fmt::Display for HeapState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (predicate_name, predicate_state) in &self.predicates { + writeln!(f, "{predicate_name}:")?; + for predicate_instance in &predicate_state.instances { + writeln!( + f, + " {} @ {:?}: {}, {:?}", + display::cjoin(&predicate_instance.arguments), + predicate_instance.inhale_location, + predicate_instance.permission_amount, + predicate_instance.state + )?; + } + } + writeln!( + f, + "current predicate snapshots:\n{}", + self.predicate_snapshots + )?; + Ok(()) + } +} + +impl HeapState { + fn is_predicate_instance_non_aliased( + &mut self, + solver: &mut EGraphState, + program: &ProgramContext, + predicate_name: &str, + arguments: &[vir_low::Expression], + ) -> SpannedEncodingResult { + if program.is_predicate_kind_non_aliased(predicate_name) { + return Ok(true); + } + fn construct_predicate_address_non_aliased_call( + predicate_address: &vir_low::Expression, + ) -> vir_low::Expression { + use vir_low::macros::*; + let address_is_non_aliased = ty!(Bool); + expr! { + (ComputeAddress::address_is_non_aliased([predicate_address.clone()])) + } + } + match program.get_predicate_kind(predicate_name) { + vir_low::PredicateKind::MemoryBlock => { + let predicate_address = &arguments[0]; + let predicate_address_non_aliased_call = + construct_predicate_address_non_aliased_call(predicate_address); + solver.intern_term(&predicate_address_non_aliased_call)?; + if solver.is_true(&predicate_address_non_aliased_call)? { + return Ok(true); + } else { + solver.saturate()?; + if solver.is_true(&predicate_address_non_aliased_call)? { + return Ok(true); + } + } + } + vir_low::PredicateKind::Owned => { + let predicate_place = &arguments[0]; + if is_place_non_aliased(predicate_place) { + return Ok(true); + } + } + _ => {} + } + Ok(false) + } + + pub(in super::super) fn purify_expression_with_retry( + &mut self, + solver: &mut EGraphState, + program: &ProgramContext, + expression: vir_low::Expression, + ) -> SpannedEncodingResult { + let mut expression = purify_snap_calls( + &self.predicate_snapshots, + &self.predicate_snapshots_at_label, + solver, + program, + expression, + )?; + if !expression.is_heap_independent() { + solver.saturate()?; + expression = purify_snap_calls( + &self.predicate_snapshots, + &self.predicate_snapshots_at_label, + solver, + program, + expression, + )?; + } + Ok(expression) + } + + fn purify_predicate_arguments_with_retry( + &mut self, + solver: &mut EGraphState, + program: &ProgramContext, + arguments: Vec, + ) -> SpannedEncodingResult> { + purify_snap_calls_vec_with_retry( + &self.predicate_snapshots, + &self.predicate_snapshots_at_label, + solver, + program, + arguments, + ) + // eprintln!("purify_predicate_arguments_with_retry"); + // eprintln!(" 1: {}", display::cjoin(&arguments)); + // let mut arguments = purify_snap_calls_vec( + // &self.predicate_snapshots, + // &self.predicate_snapshots_at_label, + // solver, + // program, + // arguments, + // )?; + // eprintln!(" 2: {}", display::cjoin(&arguments)); + // if !all_heap_independent(&arguments) { + // solver.saturate()?; + // arguments = purify_snap_calls_vec( + // &self.predicate_snapshots, + // &self.predicate_snapshots_at_label, + // solver, + // program, + // arguments, + // )?; + // eprintln!(" 3: {}", display::cjoin(&arguments)); + // } + // eprintln!(" 4: {}", display::cjoin(&arguments)); + // solver.intern_heap_independent_terms(&arguments)?; + // Ok(arguments) + } + + pub(super) fn save_state(&mut self, label: String) { + assert!(self + .predicate_snapshots_at_label + .insert(label, self.predicate_snapshots.clone()) + .is_none()); + } + + pub(super) fn add_predicate_instance( + &mut self, + solver: &mut EGraphState, + program: &ProgramContext, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + inhale_location: Location, + ) -> SpannedEncodingResult<()> { + let arguments = self.purify_predicate_arguments_with_retry( + solver, + program, + predicate.arguments.clone(), + )?; + let (state, snapshot) = if all_heap_independent(&arguments) { + if self.is_predicate_instance_non_aliased( + solver, + program, + &predicate.name, + &arguments, + )? { + let predicate_snapshot = self + .predicate_snapshots + .create_non_aliased_predicate_snapshot( + program, + &predicate.name, + arguments.clone(), + )?; + ( + PredicateInstanceState::FreshNonAliased, + Some(predicate_snapshot), + ) + } else { + (PredicateInstanceState::FreshAliased, None) + } + } else { + self.mark_predicate_instances_seen_qp_inhale(&predicate.name); + (PredicateInstanceState::FreshHeapDependent, None) + }; + let predicate_name = predicate.name.clone(); + let predicate_state = + self.predicates + .entry(predicate_name) + .or_insert_with(|| PredicateState { + instances: Vec::new(), + }); + let predicate_instance = PredicateInstance { + arguments, + permission_amount: (*predicate.permission).clone(), + inhale_location, + state, + snapshot, + }; + predicate_state.instances.push(predicate_instance); + Ok(()) + } + + pub(super) fn try_removing_predicate_instance( + &mut self, + solver: &mut EGraphState, + program: &ProgramContext, + predicate: &vir_low::ast::expression::PredicateAccessPredicate, + exhale_location: Location, + ) -> SpannedEncodingResult<()> { + let arguments = self.purify_predicate_arguments_with_retry( + solver, + program, + predicate.arguments.clone(), + )?; + if all_heap_independent(&arguments) { + let is_instance_non_aliased = self.is_predicate_instance_non_aliased( + solver, + program, + &predicate.name, + &arguments, + )?; + if let Some(predicate_state) = self.predicates.get_mut(&predicate.name) { + for predicate_instance in &mut predicate_state.instances { + // eprintln!("predicate_instance {} state: {:?}", display::cjoin(&predicate_instance.arguments), predicate_instance.state); + if matches!( + predicate_instance.state, + PredicateInstanceState::FreshAliased + | PredicateInstanceState::FreshNonAliased + | PredicateInstanceState::SeenQPExhale + ) && predicate_instance.matches(&arguments, &predicate.permission, solver)? + { + predicate_instance.state = PredicateInstanceState::Exhaled(exhale_location); + assert_eq!(predicate_instance.permission_amount, *predicate.permission); + if predicate_instance.snapshot.is_some() { + self.predicate_snapshots.destroy_predicate_snapshot( + &predicate.name, + &arguments, + solver, + )?; + } + return Ok(()); + } + } + assert!( + !is_instance_non_aliased, + "Failed to exhale non-aliased predicate: {predicate}" + ); + // Failed to exhale, mark all instances as failed exhale. + for predicate_instance in &mut predicate_state.instances { + if matches!( + predicate_instance.state, + PredicateInstanceState::FreshAliased | PredicateInstanceState::SeenQPExhale + ) { + predicate_instance.state = PredicateInstanceState::SeenFailedExhale; + } + } + } else { + assert!( + !is_instance_non_aliased, + "Failed to exhale non-aliased predicate: {predicate}" + ); + } + } else { + self.mark_predicate_instances_seen_qp_exhale(&predicate.name); + } + Ok(()) + } + + pub(super) fn mark_predicate_instances_seen_qp_inhale(&mut self, predicate_name: &str) { + if let Some(predicate_state) = self.predicates.get_mut(predicate_name) { + for predicate_instance in &mut predicate_state.instances { + if predicate_instance.state == PredicateInstanceState::SeenQPExhale { + predicate_instance.state = PredicateInstanceState::SeenQPInhale; + } + } + } + } + + pub(super) fn mark_predicate_instances_seen_qp_exhale(&mut self, predicate_name: &str) { + if let Some(predicate_state) = self.predicates.get_mut(predicate_name) { + for predicate_instance in &mut predicate_state.instances { + if predicate_instance.state == PredicateInstanceState::FreshAliased { + predicate_instance.state = PredicateInstanceState::SeenQPExhale; + } + } + } + } + + pub(super) fn get_predicate(&self, predicate_name: &str) -> Option<&PredicateState> { + self.predicates.get(predicate_name) + } +} + +#[derive(Clone)] +pub(super) struct PredicateState { + instances: Vec, +} + +impl PredicateState { + pub(super) fn get_instances(&self) -> &[PredicateInstance] { + &self.instances + } +} + +#[derive(Clone)] +pub(super) struct PredicateInstance { + /// The arguments of the predicate instance. + pub(super) arguments: Vec, + pub(super) permission_amount: vir_low::Expression, + /// The location of the inhale statement that inhaled this predicate instance. + pub(super) inhale_location: Location, + /// The state of the predicate. + pub(super) state: PredicateInstanceState, + /// The snapshot variable name given when purifying a non-aliased predicate. + pub(super) snapshot: Option, +} + +impl PredicateInstance { + fn matches( + &self, + predicate_arguments: &[vir_low::Expression], + permission: &vir_low::Expression, + solver: &EGraphState, + ) -> SpannedEncodingResult { + assert_eq!(self.arguments.len(), predicate_arguments.len()); + if !arguments_match(&self.arguments, predicate_arguments, solver)? { + return Ok(false); + } + Ok(self.permission_amount == *permission) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub(super) enum PredicateInstanceState { + /// The predicate was inhaled and has not seen QP exhale yet. The predicate + /// instance can be aliased by QPs, etc. + FreshAliased, + /// The predicate was inhaled. The predicate instance cannot be aliased by + /// QPs. + FreshNonAliased, + SeenQPExhale, + SeenQPInhale, + SeenFailedExhale, + Exhaled(Location), + FreshHeapDependent, +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/mod.rs new file mode 100644 index 00000000000..1480533fe58 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/mod.rs @@ -0,0 +1,365 @@ +//! This module contains the symbolic execution engine that is used to purify +//! predicates in the Viper program. This module depends on `ErrorManager` and, +//! therefore, has to be in the `prusti-viper` crate. + +mod trace_builder; +mod egg; +mod statements; +mod heap; +mod trace; +mod program_context; +mod utils; +mod simplifier; +mod consistency_tracker; + +use self::{egg::EGraphState, program_context::ProgramContext, simplifier::simplify_expression}; +use crate::encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + middle::core_proof::{predicates::OwnedPredicateInfo, snapshots::SnapshotDomainsInfo}, +}; +use log::debug; +use prusti_common::config; +use rustc_hash::FxHashSet; +use std::collections::BTreeMap; + +use vir_crate::{ + common::{ + expression::{BinaryOperationHelpers, ExpressionIterator, UnaryOperationHelpers}, + graphviz::ToGraphviz, + }, + low::{self as vir_low, expression::visitors::ExpressionFallibleWalker}, +}; + +pub(in super::super) fn purify_with_symbolic_execution( + source_filename: &str, + program: vir_low::Program, + non_aliased_memory_block_addresses: FxHashSet, + snapshot_domains_info: &SnapshotDomainsInfo, + owned_predicates_info: BTreeMap, + purification_round: u32, +) -> SpannedEncodingResult { + debug!( + "purify_with_symbolic_execution {} {} {}", + source_filename, program.name, purification_round + ); + let mut executor = Executor::new(purification_round); + let program = executor.execute( + source_filename, + program, + non_aliased_memory_block_addresses, + snapshot_domains_info, + owned_predicates_info, + )?; + Ok(program) +} + +struct Executor { + /// Which iteration of purification with symbolic execution we are currently + /// executing? + /// + /// 1. The first iteration should purify all stack-allocated variables that + /// are non-aliased by default. + /// 2. The second iteration should purify heap resources that are dependent + /// only on stack-allocated variables. + purification_round: u32, +} + +struct ProcedureExecutor<'a> { + executor: &'a mut Executor, + source_filename: &'a str, + program_context: &'a ProgramContext<'a>, + continuations: Vec, + exhale_label_generator_counter: u64, + /// The execution trace showing in what order the statements were executed. + execution_trace_builder: trace_builder::ExecutionTraceBuilder<'a>, + /// The original execution traces. + original_traces: Vec, + /// Traces in which purifiable predicates were purified. + final_traces: Vec, +} + +#[derive(Debug)] +pub struct Continuation { + next_block_label: vir_low::Label, + parent_block_label: vir_low::Label, + condition: vir_low::Expression, +} + +impl Executor { + pub(crate) fn new(purification_round: u32) -> Self { + Self { purification_round } + } + + pub(crate) fn execute( + &mut self, + source_filename: &str, + mut program: vir_low::Program, + non_aliased_memory_block_addresses: FxHashSet, + snapshot_domains_info: &SnapshotDomainsInfo, + owned_predicates_info: BTreeMap, + ) -> SpannedEncodingResult { + let program_context = ProgramContext::new( + &program.domains, + &program.functions, + &program.predicates, + snapshot_domains_info, + owned_predicates_info, + &non_aliased_memory_block_addresses, + self.purification_round, + ); + let mut new_procedures = Vec::new(); + for procedure in program.procedures { + let procedure_name = procedure.name.clone(); + let procedure_executor = + ProcedureExecutor::new(self, source_filename, &procedure_name, &program_context)?; + procedure_executor.execute_procedure(procedure, &mut new_procedures)?; + } + program.procedures = new_procedures; + Ok(program) + } +} + +impl<'a> ProcedureExecutor<'a> { + fn new( + executor: &'a mut Executor, + source_filename: &'a str, + procedure_name: &'a str, + program_context: &'a ProgramContext<'a>, + ) -> SpannedEncodingResult { + Ok(Self { + executor, + source_filename, + continuations: Vec::new(), + exhale_label_generator_counter: 0, + execution_trace_builder: trace_builder::ExecutionTraceBuilder::new( + source_filename, + procedure_name, + program_context.get_domains(), + )?, + program_context, + original_traces: Vec::new(), + final_traces: Vec::new(), + }) + } + + fn execute_procedure( + mut self, + procedure: vir_low::ProcedureDecl, + new_procedures: &mut Vec, + ) -> SpannedEncodingResult<()> { + debug!( + "Executing procedure: {} round: {}", + procedure.name, self.executor.purification_round + ); + // Intern all non-aliased predicates. + for address in self + .program_context + .get_non_aliased_memory_block_addresses() + { + assert!(address.is_heap_independent()); + use vir_low::macros::*; + let address_is_non_aliased = ty!(Bool); + let address_non_aliased_call = expr! { + (ComputeAddress::address_is_non_aliased([address.clone()])) + }; + self.execution_trace_builder + .current_egraph_state() + .assume(&address_non_aliased_call)?; + } + let mut current_block = procedure.entry.clone(); + loop { + if self + .execution_trace_builder + .current_egraph_state() + .is_inconsistent()? + { + self.finalize_trace()?; + if let Some(new_current_block) = self.next_continuation(procedure.position)? { + current_block = new_current_block; + continue; + } else { + break; + } + } + let block = procedure.basic_blocks.get(¤t_block).unwrap(); + self.execute_block(¤t_block, block)?; + if self + .execution_trace_builder + .current_egraph_state() + .is_inconsistent()? + { + self.finalize_trace()?; + if let Some(new_current_block) = self.next_continuation(procedure.position)? { + current_block = new_current_block; + continue; + } else { + break; + } + } + match &block.successor { + vir_low::Successor::Return => { + self.finalize_trace()?; + if let Some(new_current_block) = self.next_continuation(procedure.position)? { + current_block = new_current_block; + } else { + break; + } + } + vir_low::Successor::Goto(label) => current_block = label.clone(), + vir_low::Successor::GotoSwitch(targets) => { + let parent_block_label = current_block.clone(); + self.execution_trace_builder + .add_split_point(parent_block_label.clone())?; + // Since the jumps are evaluated one after another, we need + // to negate all the previous conditions when considering + // the new one. + let mut negated_conditions = Vec::new(); + let mut targets = targets.iter(); + let (condition, label) = targets.next().unwrap(); + self.assume_condition(condition.clone(), procedure.position)?; + current_block = label.clone(); + negated_conditions.push(UnaryOperationHelpers::not(condition.clone())); + for (condition, label) in targets { + let continuation = Continuation { + next_block_label: label.clone(), + parent_block_label: parent_block_label.clone(), + condition: vir_low::Expression::and( + negated_conditions.clone().into_iter().conjoin(), + condition.clone(), + ), + }; + self.continuations.push(continuation); + negated_conditions.push(UnaryOperationHelpers::not(condition.clone())); + } + } + } + } + if prusti_common::config::dump_debug_info() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_symbex_original", + format!( + "{}.{}.round-{}.dot", + self.source_filename, procedure.name, self.executor.purification_round, + ), + |writer| { + self.execution_trace_builder + .original_view() + .to_graphviz(writer) + .unwrap() + }, + ); + for (i, trace) in self.original_traces.iter().enumerate() { + prusti_common::report::log::report_with_writer( + "vir_symbex_original_traces", + format!( + "{}.{}.round-{}.{}.vpr", + self.source_filename, procedure.name, self.executor.purification_round, i + ), + |writer| trace.write(writer).unwrap(), + ); + } + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_symbex_optimized", + format!( + "{}.{}.round-{}.dot", + self.source_filename, procedure.name, self.executor.purification_round, + ), + |writer| { + self.execution_trace_builder + .heap_view() + .to_graphviz(writer) + .unwrap() + }, + ); + for (i, trace) in self.final_traces.iter().enumerate() { + prusti_common::report::log::report_with_writer( + "vir_symbex_optimized_traces", + format!( + "{}.{}.round-{}.{}.vpr", + self.source_filename, procedure.name, self.executor.purification_round, i + ), + |writer| trace.write(writer).unwrap(), + ); + } + } + if config::purify_with_symbolic_execution() { + for (i, trace) in self.final_traces.into_iter().enumerate() { + let new_procedure = trace.into_procedure(i, &procedure); + new_procedures.push(new_procedure); + } + } else { + for (i, trace) in self.original_traces.into_iter().enumerate() { + let new_procedure = trace.into_procedure(i, &procedure); + new_procedures.push(new_procedure); + } + } + Ok(()) + } + + fn next_continuation( + &mut self, + default_position: vir_low::Position, + ) -> SpannedEncodingResult> { + while let Some(continuation) = self.continuations.pop() { + debug!("Rolling back to {}", continuation.parent_block_label); + self.execution_trace_builder + .rollback_to_split_point(continuation.parent_block_label)?; + self.assume_condition(continuation.condition, default_position)?; + if self + .execution_trace_builder + .current_egraph_state() + .is_inconsistent()? + { + debug!("Inconsistent state after rollback"); + self.execution_trace_builder.remove_last_block()?; + } else { + return Ok(Some(continuation.next_block_label)); + } + } + Ok(None) + } + + fn assume_condition( + &mut self, + condition: vir_low::Expression, + default_position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + self.execution_trace_builder + .current_egraph_state() + .try_assume_heap_independent_conjuncts(&condition)?; + self.execution_trace_builder + .heap_assume(condition.clone(), default_position)?; + self.execution_trace_builder + .add_original_statement(vir_low::Statement::assume(condition, default_position))?; + Ok(()) + } + + fn execute_block( + &mut self, + current_block: &vir_low::Label, + block: &vir_low::BasicBlock, + ) -> SpannedEncodingResult<()> { + debug!("Executing block {}", current_block); + let comment = format!("Executing block: {current_block}"); + self.execution_trace_builder + .add_original_statement(vir_low::Statement::comment(comment.clone()))?; + self.execution_trace_builder + .heap_comment(vir_low::Comment::new(comment))?; + for statement in &block.statements { + self.execute_statement(current_block, statement)?; + } + Ok(()) + } + + fn finalize_trace(&mut self) -> SpannedEncodingResult<()> { + self.execution_trace_builder + .current_egraph_state() + .saturate()?; + let (original_trace, final_trace) = self + .execution_trace_builder + .finalize_trace(self.program_context)?; + self.original_traces.push(original_trace); + self.final_traces.push(final_trace); + Ok(()) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/program_context.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/program_context.rs new file mode 100644 index 00000000000..3e4c0ac05c7 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/program_context.rs @@ -0,0 +1,196 @@ +use crate::encoder::middle::core_proof::{ + predicates::{OwnedPredicateInfo, SnapshotFunctionInfo}, + snapshots::SnapshotDomainsInfo, +}; +use rustc_hash::{FxHashMap, FxHashSet}; +use std::collections::BTreeMap; +use vir_crate::low::{self as vir_low}; + +pub(super) struct ProgramContext<'a> { + domains: &'a [vir_low::DomainDecl], + domain_functions: FxHashMap, + functions: FxHashMap, + predicate_decls: FxHashMap, + snapshot_functions_to_predicates: BTreeMap, + predicates_to_snapshot_types: BTreeMap, + non_aliased_memory_block_addresses: &'a FxHashSet, + snapshot_domains_info: &'a SnapshotDomainsInfo, + constant_constructor_names: FxHashSet, + purification_round: u32, +} + +impl<'a> ProgramContext<'a> { + pub(super) fn new( + domains: &'a [vir_low::DomainDecl], + functions: &'a [vir_low::FunctionDecl], + predicate_decls: &'a [vir_low::PredicateDecl], + snapshot_domains_info: &'a SnapshotDomainsInfo, + predicate_info: BTreeMap, + non_aliased_memory_block_addresses: &'a FxHashSet, + purification_round: u32, + ) -> Self { + let mut snapshot_functions_to_predicates = BTreeMap::new(); + let mut predicates_to_snapshot_types = BTreeMap::new(); + for ( + predicate_name, + OwnedPredicateInfo { + current_snapshot_function: SnapshotFunctionInfo { function_name, .. }, + // We are not purifying the final snapshot function because it + // is already pure. + final_snapshot_function: _, + snapshot_type, + }, + ) in predicate_info + { + snapshot_functions_to_predicates.insert(function_name, predicate_name.clone()); + predicates_to_snapshot_types.insert(predicate_name, snapshot_type); + } + Self { + constant_constructor_names: snapshot_domains_info + .snapshot_domains + .values() + .flat_map(|domain| domain.constant_constructor_name.clone()) + .collect(), + domain_functions: domains + .iter() + .flat_map(|domain| { + domain + .functions + .iter() + .map(move |function| (function.name.clone(), function)) + }) + .collect(), + domains, + snapshot_functions_to_predicates, + predicates_to_snapshot_types, + functions: functions + .iter() + .map(|function| (function.name.clone(), function)) + .collect(), + predicate_decls: predicate_decls + .iter() + .map(|predicate| (predicate.name.clone(), predicate)) + .collect(), + non_aliased_memory_block_addresses, + snapshot_domains_info, + purification_round, + } + } + + pub(super) fn get_domains(&self) -> &'a [vir_low::DomainDecl] { + self.domains + } + + pub(super) fn get_function(&self, name: &str) -> &'a vir_low::FunctionDecl { + self.functions.get(name).unwrap_or_else(|| { + panic!( + "Function not found: {} (purification round: {})", + name, self.purification_round + ) + }) + } + + pub(super) fn get_snapshot_type(&self, predicate_name: &str) -> Option { + // FIXME: Code duplication with + // prusti-viper/src/encoder/middle/core_proof/transformations/custom_heap_encoding/heap_encoder/predicates.rs + let predicate = self.predicate_decls[predicate_name]; + match predicate.kind { + vir_low::PredicateKind::MemoryBlock => { + use vir_low::macros::*; + Some(ty!(Bytes)) + } + vir_low::PredicateKind::Owned => Some( + self.predicates_to_snapshot_types + .get(predicate_name) + .unwrap_or_else(|| unreachable!("predicate not found: {}", predicate_name)) + .clone(), + ), + vir_low::PredicateKind::LifetimeToken + | vir_low::PredicateKind::WithoutSnapshotWhole + | vir_low::PredicateKind::WithoutSnapshotWholeNonAliased + | vir_low::PredicateKind::WithoutSnapshotFrac => None, + } + } + + pub(super) fn get_snapshot_predicate(&self, function_name: &str) -> Option<&str> { + let function = self.get_function(function_name); + match function.kind { + vir_low::FunctionKind::MemoryBlockBytes => Some("MemoryBlock"), + vir_low::FunctionKind::CallerFor => todo!(), + vir_low::FunctionKind::SnapRange => todo!(), + vir_low::FunctionKind::Snap => self + .snapshot_functions_to_predicates + .get(function_name) + .map(|s| s.as_str()), + } + } + + pub(super) fn get_non_aliased_memory_block_addresses( + &self, + ) -> &'a FxHashSet { + self.non_aliased_memory_block_addresses + } + + pub(super) fn get_predicate_kind(&self, predicate_name: &str) -> vir_low::PredicateKind { + self.predicate_decls[predicate_name].kind + } + + pub(super) fn is_predicate_kind_non_aliased(&self, predicate_name: &str) -> bool { + self.predicate_decls + .get(predicate_name) + .unwrap_or_else(|| panic!("{predicate_name}")) + .kind + .is_non_aliased() + } + + pub(super) fn get_purification_round(&self) -> u32 { + self.purification_round + } + + pub(super) fn get_binary_operator( + &self, + snapshot_domain_name: &str, + function_name: &str, + ) -> Option { + self.snapshot_domains_info + .snapshot_domains + .get(snapshot_domain_name) + .and_then(|snapshot_domain| { + snapshot_domain.binary_operators.get(function_name).cloned() + }) + } + + pub(super) fn get_constant_constructor( + &self, + snapshot_domain_name: &str, + ) -> &'a vir_low::DomainFunctionDecl { + let constructor_name = self + .snapshot_domains_info + .snapshot_domains + .get(snapshot_domain_name) + .unwrap() + .constant_constructor_name + .as_ref() + .unwrap_or_else(|| panic!("not found: {snapshot_domain_name}")); + self.domain_functions[constructor_name] + } + + pub(super) fn get_constant_destructor( + &self, + snapshot_domain_name: &str, + ) -> &vir_low::DomainFunctionDecl { + let destructor_name = self + .snapshot_domains_info + .snapshot_domains + .get(snapshot_domain_name) + .unwrap() + .constant_destructor_name + .as_ref() + .unwrap_or_else(|| panic!("not found: {snapshot_domain_name}")); + self.domain_functions[destructor_name] + } + + pub(super) fn get_constant_constructor_names(&self) -> &FxHashSet { + &self.constant_constructor_names + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/simplifier.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/simplifier.rs new file mode 100644 index 00000000000..fd54f7b5674 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/simplifier.rs @@ -0,0 +1,181 @@ +use super::{egg::EGraphState, program_context::ProgramContext, ProcedureExecutor}; +use crate::encoder::errors::{SpannedEncodingError, SpannedEncodingResult}; +use vir_crate::{ + common::expression::BinaryOperationHelpers, + low::{self as vir_low, expression::visitors::ExpressionFallibleFolder}, +}; + +// impl<'a> ProcedureExecutor<'a> { +// pub(super) fn simplify_expression( +// &mut self, +// expression: vir_low::Expression, +// ) -> SpannedEncodingResult { +// let mut simplifier = Simplifier { +// program_context: self.program_context, +// solver: self.execution_trace_builder.current_egraph_state(), +// }; +// simplifier.fallible_fold_expression(expression) +// } +// } + +pub(super) fn simplify_expression<'a>( + expression: vir_low::Expression, + program_context: &'a ProgramContext<'a>, + solver: &'a mut EGraphState, +) -> SpannedEncodingResult { + let mut simplifier = Simplifier { + program_context, + solver, + }; + simplifier.fallible_fold_expression(expression) +} + +struct Simplifier<'a> { + program_context: &'a ProgramContext<'a>, + solver: &'a mut EGraphState, +} + +impl<'a> Simplifier<'a> { + // fn intern_arguments_and_saturate( + // &mut self, + // arguments: &[vir_low::Expression], + // ) -> SpannedEncodingResult<()> { + // for argument in arguments { + // self.solver + // .try_intern_heap_independent_conjuncts(argument)?; + // } + // self.solver.saturate()?; + // Ok(()) + // } + + fn try_resolve_constants( + &mut self, + arguments: &[vir_low::Expression], + ) -> SpannedEncodingResult<(bool, Vec, vir_low::Expression)>>)> { + let mut maybe_constants = Vec::new(); + let mut found_constant = false; + for argument in arguments { + let maybe_constant = self.solver.resolve_constant( + argument, + self.program_context.get_constant_constructor_names(), + )?; + if maybe_constant.is_some() { + found_constant = true; + } + maybe_constants.push(maybe_constant); + } + Ok((found_constant, maybe_constants)) + } +} + +impl<'a> ExpressionFallibleFolder for Simplifier<'a> { + type Error = SpannedEncodingError; + + fn fallible_fold_domain_func_app_enum( + &mut self, + mut domain_func_app: vir_low::DomainFuncApp, + ) -> Result { + if let Some(op) = self + .program_context + .get_binary_operator(&domain_func_app.domain_name, &domain_func_app.function_name) + { + if matches!(op, vir_low::BinaryOpKind::Mul) { + let domain_func_app_original = + vir_low::Expression::DomainFuncApp(domain_func_app.clone()); + // eprintln!("simplify: {domain_func_app}"); + // self.intern_arguments_and_saturate(&domain_func_app.arguments)?; + let (found_constant, maybe_constants) = + self.try_resolve_constants(&domain_func_app.arguments)?; + if found_constant { + let constructor = self + .program_context + .get_constant_constructor(&domain_func_app.domain_name); + let destructor = self + .program_context + .get_constant_destructor(&domain_func_app.domain_name); + let mut constructor_arguments = Vec::new(); + for (maybe_constant, argument) in maybe_constants + .into_iter() + .zip(std::mem::take(&mut domain_func_app.arguments).into_iter()) + { + if let Some((constructor_name, constant)) = maybe_constant { + assert_eq!(constructor_name.unwrap(), constructor.name); + constructor_arguments.push(constant); + } else { + let destructor = vir_low::Expression::domain_function_call( + &domain_func_app.domain_name, + destructor.name.clone(), + vec![argument], + vir_low::Type::Int, + ); + constructor_arguments.push(destructor); + } + } + let right = constructor_arguments.pop().unwrap(); + let left = constructor_arguments.pop().unwrap(); + assert!(constructor_arguments.is_empty()); + let result = vir_low::Expression::domain_function_call( + domain_func_app.domain_name, + constructor.name.clone(), + vec![vir_low::Expression::multiply(left, right)], + domain_func_app.return_type, + ) + .set_default_position(domain_func_app.position); + + if result.is_heap_independent() + && domain_func_app_original.is_heap_independent() + { + self.solver + .assume_equal(&result, &domain_func_app_original)?; + } + return Ok(result); + } else { + unimplemented!("failed to rewrite multiplication: {domain_func_app}"); + } + } + } + self.fallible_fold_domain_func_app(domain_func_app) + .map(vir_low::Expression::DomainFuncApp) + } + + fn fallible_fold_binary_op_enum( + &mut self, + mut binary_op: vir_low::BinaryOp, + ) -> Result { + if matches!(binary_op.op_kind, vir_low::BinaryOpKind::Mul) + && !binary_op.left.is_constant() + && !binary_op.right.is_constant() + { + let arguments = vec![*binary_op.left, *binary_op.right]; + // self.intern_arguments_and_saturate(&arguments)?; + let (found_constant, maybe_constants) = self.try_resolve_constants(&arguments)?; + if found_constant { + let mut binary_op_arguments = Vec::new(); + for (maybe_constant, argument) in + maybe_constants.into_iter().zip(arguments.into_iter()) + { + if let Some((constructor_name, constant)) = maybe_constant { + assert!(constructor_name.is_none()); + binary_op_arguments.push(constant); + } else { + binary_op_arguments.push(argument); + } + } + let right = binary_op_arguments.pop().unwrap(); + let left = binary_op_arguments.pop().unwrap(); + assert!(binary_op_arguments.is_empty()); + binary_op.left = Box::new(left); + binary_op.right = Box::new(right); + return Ok(vir_low::Expression::BinaryOp(binary_op)); + } else { + unimplemented!( + "failed to rewrite multiplication: {} * {}", + arguments[0], + arguments[1] + ); + } + } + self.fallible_fold_binary_op(binary_op) + .map(vir_low::Expression::BinaryOp) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/statements.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/statements.rs new file mode 100644 index 00000000000..1ebe92add44 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/statements.rs @@ -0,0 +1,381 @@ +use super::{utils::calculate_hash, ProcedureExecutor}; +use crate::encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + middle::core_proof::transformations::symbolic_execution::egg::EGraphState, +}; +use prusti_common::config; +use vir_crate::{ + common::expression::BinaryOperationHelpers, + low::{ + self as vir_low, + expression::visitors::{default_fallible_walk_expression, ExpressionFallibleWalker}, + operations::ty::Typed, + }, +}; + +impl<'a> ProcedureExecutor<'a> { + pub(super) fn execute_statement( + &mut self, + _current_block: &vir_low::Label, + statement: &vir_low::Statement, + ) -> SpannedEncodingResult<()> { + self.execution_trace_builder + .add_original_statement(statement.clone())?; + match statement { + vir_low::Statement::Comment(statement) => { + self.execute_statement_comment(statement)?; + } + vir_low::Statement::Label(statement) => { + self.execute_statement_label(statement)?; + } + vir_low::Statement::LogEvent(statement) => { + self.execute_statement_log_event(statement)?; + } + vir_low::Statement::Assume(statement) => { + self.execute_statement_assume(statement)?; + } + vir_low::Statement::Assert(statement) => { + self.execute_statement_assert(statement)?; + } + vir_low::Statement::Inhale(statement) => { + self.execute_statement_inhale(statement)?; + } + vir_low::Statement::Exhale(statement) => { + self.execute_statement_exhale(statement)?; + } + vir_low::Statement::Fold(_) => todo!(), + vir_low::Statement::Unfold(_) => todo!(), + vir_low::Statement::ApplyMagicWand(magic_wand) => { + unreachable!("magic_wand: {magic_wand}"); + } + vir_low::Statement::MethodCall(method_call) => { + unreachable!("method_call: {method_call}"); + } + vir_low::Statement::Assign(statement) => { + self.execute_statement_assign(statement)?; + } + vir_low::Statement::Conditional(_) => { + unreachable!(); + } + } + Ok(()) + } + + fn execute_statement_comment( + &mut self, + statement: &vir_low::ast::statement::Comment, + ) -> SpannedEncodingResult<()> { + self.execution_trace_builder + .heap_comment(statement.clone())?; + Ok(()) + } + + fn execute_statement_label( + &mut self, + statement: &vir_low::ast::statement::Label, + ) -> SpannedEncodingResult<()> { + self.execution_trace_builder.heap_label(statement.clone())?; + Ok(()) + } + + fn execute_statement_log_event( + &mut self, + statement: &vir_low::ast::statement::LogEvent, + ) -> SpannedEncodingResult<()> { + self.execution_trace_builder + .current_egraph_state() + .try_assume_heap_independent_conjuncts(&statement.expression)?; + self.execution_trace_builder + .heap_assume(statement.expression.clone(), statement.position)?; + Ok(()) + } + + fn execute_statement_assume( + &mut self, + statement: &vir_low::ast::statement::Assume, + ) -> SpannedEncodingResult<()> { + let expression = self.simplify_expression(&statement.expression)?; + // let expression = statement.expression.clone(); + // self.execution_trace_builder.current_egraph_state().intern_heap_independent_subexpressions(&expression)?; + self.execution_trace_builder + .current_egraph_state() + .try_assume_heap_independent_conjuncts(&expression)?; + self.execution_trace_builder + .heap_assume(expression, statement.position)?; + Ok(()) + } + + fn execute_statement_assert( + &mut self, + statement: &vir_low::ast::statement::Assert, + ) -> SpannedEncodingResult<()> { + let expression = self.simplify_expression(&statement.expression)?; + // let expression = statement.expression.clone(); + // self.execution_trace_builder.current_egraph_state().intern_heap_independent_subexpressions(&expression)?; + self.execution_trace_builder + .heap_assert(expression, statement.position)?; + // TODO: Try this: + // self.execution_trace_builder + // .current_egraph_state() + // .assume_heap_independent_conjuncts(&statement.expression)?; + Ok(()) + } + + fn execute_statement_inhale( + &mut self, + statement: &vir_low::ast::statement::Inhale, + ) -> SpannedEncodingResult<()> { + // We cannot do `let expression = + // self.simplify_expression(&statement.expression)?;` here because we + // need to take into account the predicates contained in this expression + // when simplifying it. Therefore, we simplify each conjunct separately. + + // let expression = self.simplify_expression(&statement.expression)?; + // self.execution_trace_builder.current_egraph_state().intern_heap_independent_subexpressions(&statement.expression)?; + self.execute_inhale(&statement.expression, statement.position)?; + // self.execute_inhale(&expression, statement.position)?; + Ok(()) + } + + fn execute_inhale( + &mut self, + expression: &vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + if let vir_low::Expression::BinaryOp(expression) = expression { + if expression.op_kind == vir_low::BinaryOpKind::And { + self.execute_inhale(&expression.left, position)?; + self.execute_inhale(&expression.right, position)?; + return Ok(()); + } + } + if let vir_low::Expression::PredicateAccessPredicate(predicate) = expression { + let mut arguments = Vec::new(); + for argument in &predicate.arguments { + arguments.push(self.simplify_expression(argument)?); + } + self.execution_trace_builder.heap_inhale_predicate( + vir_low::PredicateAccessPredicate::new( + predicate.name.clone(), + arguments, + (*predicate.permission).clone(), + ), + self.program_context, + position, + )?; + return Ok(()); + } + let expression = self.simplify_expression(expression)?; + // self.execution_trace_builder + // .current_egraph_state() + // .intern_heap_independent_subexpressions(&expression)?; + self.execution_trace_builder + .current_egraph_state() + .try_assume_heap_independent_conjuncts(&expression)?; + self.execution_trace_builder + .heap_inhale_generic(expression.clone(), position)?; + Ok(()) + } + + // fn is_predicate_instance_non_aliased( + // &mut self, + // predicate: &vir_low::PredicateAccessPredicate, + // ) -> SpannedEncodingResult { + // if self + // .program_context + // .is_predicate_kind_non_aliased(&predicate.name) + // { + // return Ok(true); + // } + // fn construct_predicate_address_non_aliased_call( + // predicate_address: &vir_low::Expression, + // ) -> vir_low::Expression { + // use vir_low::macros::*; + // let address_is_non_aliased = ty!(Bool); + // expr! { + // (ComputeAddress::address_is_non_aliased([predicate_address.clone()])) + // } + // } + // match self.program_context.get_predicate_kind(&predicate.name) { + // vir_low::PredicateKind::MemoryBlock => { + // let solver = self.execution_trace_builder.current_egraph_state(); + // let predicate_address = &predicate.arguments[0]; + // let predicate_address_non_aliased_call = + // construct_predicate_address_non_aliased_call(predicate_address); + // solver.intern_term(&predicate_address_non_aliased_call)?; + // solver.saturate()?; + // if solver.is_true(&predicate_address_non_aliased_call)? { + // return Ok(true); + // } + // } + // vir_low::PredicateKind::Owned => { + // let solver = self.execution_trace_builder.current_egraph_state(); + // let predicate_address = &predicate.arguments[1]; + // debug_assert_eq!(predicate_address.get_type(), &vir_low::macros::ty!(Address)); + // let predicate_address_non_aliased_call = + // construct_predicate_address_non_aliased_call(predicate_address); + // solver.intern_term(&predicate_address_non_aliased_call)?; + // solver.saturate()?; + // if solver.is_true(&predicate_address_non_aliased_call)? { + // return Ok(true); + // } + // } + // _ => {} + // } + // Ok(false) + // } + + fn execute_statement_exhale( + &mut self, + statement: &vir_low::ast::statement::Exhale, + ) -> SpannedEncodingResult<()> { + // let expression = statement.expression.clone(); + let exhale_label = format!( + "exhale_label$round{}${}", + self.executor.purification_round, self.exhale_label_generator_counter + ); + self.exhale_label_generator_counter += 1; + self.execution_trace_builder + .heap_label(vir_low::ast::statement::Label { + label: exhale_label.clone(), + position: statement.position, + })?; + self.execution_trace_builder + .register_label(vir_low::Label::new(&exhale_label))?; + self.execute_exhale(&statement.expression, statement.position, &exhale_label)?; + Ok(()) + } + + fn execute_exhale( + &mut self, + expression: &vir_low::Expression, + position: vir_low::Position, + exhale_label: &str, + ) -> SpannedEncodingResult<()> { + if let vir_low::Expression::BinaryOp(expression) = expression { + if expression.op_kind == vir_low::BinaryOpKind::And { + self.execute_exhale(&expression.left, position, exhale_label)?; + self.execute_exhale(&expression.right, position, exhale_label)?; + return Ok(()); + } + } + if let vir_low::Expression::PredicateAccessPredicate(predicate) = expression { + let mut arguments = Vec::new(); + for argument in &predicate.arguments { + let simplified_argument = self.simplify_expression(argument)?; + arguments.push(simplified_argument.wrap_in_old(exhale_label)); + } + self.execution_trace_builder.heap_exhale_predicate( + vir_low::PredicateAccessPredicate::new( + predicate.name.clone(), + arguments, + (*predicate.permission).clone(), + ), + self.program_context, + position, + )?; + return Ok(()); + } + // self.execution_trace_builder.current_egraph_state().intern_heap_independent_subexpressions(&expression)?; + let expression = self.simplify_expression(expression)?; + let expression = expression.clone().wrap_in_old(exhale_label); + self.execution_trace_builder + .current_egraph_state() + .try_intern_heap_independent_conjuncts(&expression)?; + // self.execution_trace_builder + // .current_egraph_state() + // .saturate()?; + if expression.is_heap_independent() + && self + .execution_trace_builder + .current_egraph_state() + .try_is_true(&expression)? + == Some(true) + { + if config::report_symbolic_execution_purification() { + self.execution_trace_builder + .heap_comment(vir_low::Comment::new(format!("purified out: {expression}")))?; + } + } else { + if config::report_symbolic_execution_purification() { + if self + .execution_trace_builder + .current_egraph_state() + .try_is_true(&expression)? + .is_none() + { + self.execution_trace_builder + .heap_comment(vir_low::Comment::new(format!( + "not interned: {expression}" + )))?; + } else { + self.execution_trace_builder + .heap_comment(vir_low::Comment::new(format!( + "interned, but false: {expression}" + )))?; + // let debug_info = self + // .execution_trace_builder + // .current_egraph_state() + // .get_dump_eclass_of(&expression)?; + // self.execution_trace_builder + // .heap_comment(vir_low::Comment::new(debug_info))?; + } + } + self.execution_trace_builder + .heap_exhale_generic(expression, position)?; + } + Ok(()) + } + + fn execute_statement_assign( + &mut self, + statement: &vir_low::ast::statement::Assign, + ) -> SpannedEncodingResult<()> { + assert!( + !statement.position.is_default(), + "{statement} has no position" + ); + assert!(statement.value.is_constant()); + let target_variable = self + .execution_trace_builder + .create_new_bool_variable_version(&statement.target.name)?; + let expression = + vir_low::Expression::equals(target_variable.into(), statement.value.clone()); + self.execution_trace_builder + .current_egraph_state() + .try_assume_heap_independent_conjuncts(&expression)?; + self.execution_trace_builder + .heap_assume(expression, statement.position)?; + Ok(()) + } + + fn simplify_expression( + &mut self, + expression: &vir_low::Expression, + ) -> SpannedEncodingResult { + self.execution_trace_builder + .current_egraph_state() + .intern_heap_independent_subexpressions(expression)?; + let (heap_state, solver) = self + .execution_trace_builder + .current_heap_and_egraph_state_mut(); + let purified_expression = heap_state.purify_expression_with_retry( + solver, + self.program_context, + expression.clone(), + )?; + let purified_expression_hash = calculate_hash(&purified_expression); + // let simplified_expression = purified_expression; + let simplified_expression = super::simplifier::simplify_expression( + purified_expression, + self.program_context, + solver, + )?; + if calculate_hash(&simplified_expression) != purified_expression_hash { + self.execution_trace_builder + .current_egraph_state() + .intern_heap_independent_subexpressions(&simplified_expression)?; + } + Ok(simplified_expression) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace.rs new file mode 100644 index 00000000000..9f4e16e0d3f --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace.rs @@ -0,0 +1,45 @@ +use std::collections::BTreeMap; +use vir_crate::low::{self as vir_low}; + +pub(super) struct Trace { + pub statements: Vec, + pub variables: Vec, + pub labels: Vec, +} + +impl Trace { + pub(super) fn write(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> { + for statement in &self.statements { + writeln!(writer, "{statement}")?; + } + Ok(()) + } + + pub(super) fn into_procedure( + self, + trace_index: usize, + original_procedure: &vir_low::ProcedureDecl, + ) -> vir_low::ProcedureDecl { + let entry = vir_low::Label::new("trace_start"); + let exit = vir_low::Label::new("trace_end"); + let entry_block = + vir_low::BasicBlock::new(self.statements, vir_low::Successor::Goto(exit.clone())); + let exit_block = vir_low::BasicBlock::new(Vec::new(), vir_low::Successor::Return); + let mut basic_blocks = BTreeMap::new(); + basic_blocks.insert(entry.clone(), entry_block); + basic_blocks.insert(exit.clone(), exit_block); + let mut locals = original_procedure.locals.clone(); + locals.extend(self.variables); + let mut custom_labels = original_procedure.custom_labels.clone(); + custom_labels.extend(self.labels); + vir_low::ProcedureDecl::new_with_pos( + format!("{}$trace_{}", original_procedure.name, trace_index), + locals, + custom_labels, + entry, + exit, + basic_blocks, + original_procedure.position, + ) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/heap_view.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/heap_view.rs new file mode 100644 index 00000000000..971b1a8bc50 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/heap_view.rs @@ -0,0 +1,53 @@ +use super::{ExecutionTraceBlock, ExecutionTraceBuilder}; +use crate::encoder::middle::core_proof::transformations::symbolic_execution::heap::HeapEntry; +use vir_crate::low::{self as vir_low}; + +pub(in super::super) struct ExecutionTraceHeapView<'a> { + pub(super) trace: &'a ExecutionTraceBuilder<'a>, +} + +pub(in super::super) struct BlockView<'a> { + block: &'a ExecutionTraceBlock, +} + +impl<'a> ExecutionTraceHeapView<'a> { + pub(in super::super) fn iter_blocks(&self) -> impl Iterator> { + self.trace.blocks.iter().map(|block| BlockView { block }) + } + + pub(in super::super) fn block_count(&self) -> usize { + self.trace.blocks.len() + } + + pub(in super::super) fn last_block_id(&self) -> usize { + self.trace.blocks.len() - 1 + } + + pub(in super::super) fn last_block_entry_count(&self) -> usize { + self.trace.blocks.last().unwrap().heap_statements.len() + } + + pub(in super::super) fn get_block(&self, id: usize) -> BlockView<'a> { + BlockView { + block: &self.trace.blocks[id], + } + } +} + +impl<'a> BlockView<'a> { + pub(in super::super) fn iter_entries(&self) -> impl Iterator { + self.block.heap_statements.iter() + } + + pub(in super::super) fn parent(&self) -> Option { + self.block.parent + } + + pub(in super::super) fn get_new_variables(&self) -> &[vir_low::VariableDecl] { + &self.block.new_variables + } + + pub(in super::super) fn get_new_labels(&self) -> &[vir_low::Label] { + &self.block.new_labels + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/mod.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/mod.rs new file mode 100644 index 00000000000..92d691b8a7c --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/mod.rs @@ -0,0 +1,268 @@ +mod original_view; +mod heap_view; + +use super::{ + egg::EGraphState, + heap::{HeapEntry, HeapState}, + program_context::ProgramContext, + trace::Trace, +}; +use crate::encoder::errors::SpannedEncodingResult; +use rustc_hash::FxHashMap; +use std::collections::BTreeMap; +use vir_crate::{ + common::graphviz::ToGraphviz, + low::{self as vir_low}, +}; + +pub(super) use self::{ + heap_view::ExecutionTraceHeapView, original_view::ExecutionTraceOriginalView, +}; + +pub(super) struct ExecutionTraceBuilder<'a> { + pub(super) source_filename: &'a str, + pub(super) procedure_name: &'a str, + blocks: Vec, + split_point_parents: BTreeMap, + variable_versions: FxHashMap, +} + +impl<'a> Drop for ExecutionTraceBuilder<'a> { + fn drop(&mut self) { + if prusti_common::config::dump_debug_info() && std::thread::panicking() { + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_symbex_original", + format!("{}.{}.crash.dot", self.source_filename, self.procedure_name,), + |writer| self.original_view().to_graphviz(writer).unwrap(), + ); + prusti_common::report::log::report_with_writer( + "graphviz_method_vir_symbex_optimized", + format!("{}.{}.crash.dot", self.source_filename, self.procedure_name,), + |writer| self.heap_view().to_graphviz(writer).unwrap(), + ); + let mut original_trace = Trace { + statements: Vec::new(), + variables: Vec::new(), + labels: Vec::new(), + }; + self.finalize_original_trace_for_block(&mut original_trace, self.blocks.len() - 1) + .unwrap(); + prusti_common::report::log::report_with_writer( + "vir_symbex_original_traces", + format!("{}.{}.crash.vpr", self.source_filename, self.procedure_name), + |writer| original_trace.write(writer).unwrap(), + ); + } + } +} + +impl<'a> ExecutionTraceBuilder<'a> { + pub(super) fn new( + source_filename: &'a str, + procedure_name: &'a str, + domains: &[vir_low::DomainDecl], + ) -> SpannedEncodingResult { + let initial_block = ExecutionTraceBlock::root(domains)?; + Ok(Self { + source_filename, + procedure_name, + blocks: vec![initial_block], + split_point_parents: Default::default(), + variable_versions: Default::default(), + }) + } + + fn current_block(&self) -> &ExecutionTraceBlock { + self.blocks.last().unwrap() + } + + fn current_block_mut(&mut self) -> &mut ExecutionTraceBlock { + self.blocks.last_mut().unwrap() + } + + pub(super) fn current_egraph_state(&mut self) -> &mut EGraphState { + self.current_block_mut().egraph_state.as_mut().unwrap() + } + + pub(super) fn current_heap_state_mut(&mut self) -> &mut HeapState { + &mut self.current_block_mut().heap_state + } + + pub(super) fn current_heap_state(&self) -> &HeapState { + &self.current_block().heap_state + } + + pub(super) fn steal_current_egraph_solver(&mut self) -> EGraphState { + std::mem::take(&mut self.current_block_mut().egraph_state).unwrap() + } + + pub(super) fn current_heap_and_egraph_state(&self) -> (&HeapState, &EGraphState) { + let current_block = self.current_block(); + ( + ¤t_block.heap_state, + ¤t_block.egraph_state.as_ref().unwrap(), + ) + } + + pub(super) fn current_heap_and_egraph_state_mut( + &mut self, + ) -> (&mut HeapState, &mut EGraphState) { + let current_block = self.current_block_mut(); + ( + &mut current_block.heap_state, + current_block.egraph_state.as_mut().unwrap(), + ) + } + + pub(super) fn add_original_statement( + &mut self, + statement: vir_low::Statement, + ) -> SpannedEncodingResult<()> { + let current_block = self.current_block_mut(); + current_block.original_statements.push(statement); + Ok(()) + } + + pub(super) fn add_heap_entry(&mut self, entry: HeapEntry) -> SpannedEncodingResult<()> { + let current_block = self.current_block_mut(); + current_block.heap_statements.push(entry); + Ok(()) + } + + pub(super) fn add_split_point( + &mut self, + parent_block_label: vir_low::Label, + ) -> SpannedEncodingResult<()> { + let parent_id = self.blocks.len() - 1; + let new_block = ExecutionTraceBlock::from_parent(parent_id, self.current_block()); + self.blocks.push(new_block); + self.split_point_parents + .insert(parent_block_label, parent_id); + Ok(()) + } + + pub(super) fn rollback_to_split_point( + &mut self, + split_point_label: vir_low::Label, + ) -> SpannedEncodingResult<()> { + let parent_id = self.split_point_parents[&split_point_label]; + let parent = &self.blocks[parent_id]; + let new_block = ExecutionTraceBlock::from_parent(parent_id, parent); + self.blocks.push(new_block); + Ok(()) + } + + pub(super) fn original_view(&self) -> ExecutionTraceOriginalView { + ExecutionTraceOriginalView { trace: self } + } + + pub(super) fn heap_view(&self) -> ExecutionTraceHeapView { + ExecutionTraceHeapView { trace: self } + } + + pub(super) fn create_new_bool_variable_version( + &mut self, + variable_name: &str, + ) -> SpannedEncodingResult { + let version = self + .variable_versions + .entry(variable_name.to_string()) + .or_default(); + *version += 1; + let version = *version; + let variable = + vir_low::VariableDecl::new(format!("{variable_name}${version}"), vir_low::Type::Bool); + self.current_block_mut() + .new_variables + .push(variable.clone()); + Ok(variable) + } + + pub(super) fn register_label(&mut self, label: vir_low::Label) -> SpannedEncodingResult<()> { + self.current_block_mut().new_labels.push(label); + Ok(()) + } + + pub(super) fn finalize_trace( + &mut self, + program: &ProgramContext, + ) -> SpannedEncodingResult<(Trace, Trace)> { + let mut original_trace = Trace { + statements: Vec::new(), + variables: Vec::new(), + labels: Vec::new(), + }; + self.finalize_original_trace_for_block(&mut original_trace, self.blocks.len() - 1)?; + let final_trace = self.heap_finalize_trace(program)?; + Ok((original_trace, final_trace)) + } + + fn finalize_original_trace_for_block( + &self, + trace: &mut Trace, + block_id: usize, + ) -> SpannedEncodingResult<()> { + let block = &self.blocks[block_id]; + if let Some(parent_id) = block.parent { + self.finalize_original_trace_for_block(trace, parent_id)?; + } + for statement in &block.original_statements { + trace.statements.push(statement.clone()); + } + Ok(()) + } + + /// Removes the last block from the trace. This method should be used only + /// when the last method is a freshly added unreachable branch. + pub(super) fn remove_last_block(&mut self) -> SpannedEncodingResult<()> { + let last_block = self.blocks.pop().unwrap(); + assert_eq!(last_block.original_statements.len(), 1); + assert_eq!(last_block.heap_statements.len(), 1); + Ok(()) + } +} + +struct ExecutionTraceBlock { + /// The parent of this block. The root does not have a parent. + parent: Option, + /// New variables declared while executing the trace. + new_variables: Vec, + /// New labels declared while executing the trace. + new_labels: Vec, + /// Original statements that were executed in the trace. + original_statements: Vec, + /// Statements that make the heap operations more explicit. + heap_statements: Vec, + /// The last heap state. If the block is fully executed, it is the state + /// after the last statement. + heap_state: HeapState, + /// The last e-graph state. If the block is fully executed, it is the state + /// after the last statement. + egraph_state: Option, +} + +impl ExecutionTraceBlock { + fn root(domains: &[vir_low::DomainDecl]) -> SpannedEncodingResult { + Ok(Self { + parent: None, + new_variables: Vec::new(), + new_labels: Vec::new(), + original_statements: Vec::new(), + heap_statements: Vec::new(), + heap_state: HeapState::default(), + egraph_state: Some(EGraphState::new(domains)?), + }) + } + + fn from_parent(parent_id: usize, parent: &Self) -> Self { + Self { + parent: Some(parent_id), + new_variables: Vec::new(), + new_labels: Vec::new(), + original_statements: Vec::new(), + heap_statements: Vec::new(), + heap_state: parent.heap_state.clone(), + egraph_state: parent.egraph_state.clone(), + } + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/original_view.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/original_view.rs new file mode 100644 index 00000000000..79f7614c594 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/trace_builder/original_view.rs @@ -0,0 +1,35 @@ +use super::ExecutionTraceBuilder; +use vir_crate::{ + common::graphviz::{escape_html_wrap, Graph, ToGraphviz}, + low::{self as vir_low}, +}; + +pub(in super::super) struct ExecutionTraceOriginalView<'a> { + pub(super) trace: &'a ExecutionTraceBuilder<'a>, +} + +impl<'a> ToGraphviz for ExecutionTraceOriginalView<'a> { + fn to_graph(&self) -> Graph { + let mut graph = Graph::with_columns(&["statement"]); + for (block_id, block) in self.trace.blocks.iter().enumerate() { + let mut node_builder = graph.create_node(format!("block{block_id}")); + for statement in &block.original_statements { + let statement_string = match statement { + vir_low::Statement::Comment(statement) => { + format!( + "{}", + escape_html_wrap(statement) + ) + } + _ => escape_html_wrap(statement.to_string()), + }; + node_builder.add_row_sequence(vec![statement_string]); + } + node_builder.build(); + if let Some(parent) = block.parent { + graph.add_regular_edge(format!("block{parent}"), format!("block{block_id}")); + } + } + graph + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/utils.rs b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/utils.rs new file mode 100644 index 00000000000..4a81c5741ad --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/transformations/symbolic_execution/utils.rs @@ -0,0 +1,53 @@ +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::transformations::symbolic_execution::egg::EGraphState, +}; +use std::hash::{Hash, Hasher}; +use vir_crate::low::{self as vir_low, operations::ty::Typed}; + +pub(super) fn arguments_match( + args1: &[vir_low::Expression], + args2: &[vir_low::Expression], + solver: &EGraphState, +) -> SpannedEncodingResult { + for (arg1, arg2) in args1.iter().zip(args2) { + if !solver.is_equal(arg1, arg2)? { + return Ok(false); + } + } + Ok(true) +} + +pub(super) fn all_heap_independent(arguments: &[vir_low::Expression]) -> bool { + arguments + .iter() + .all(|argument| argument.is_heap_independent()) +} + +pub(super) fn calculate_hash(t: &T) -> u64 { + let mut s = std::collections::hash_map::DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} + +pub(super) fn is_place_non_aliased(place: &vir_low::Expression) -> bool { + assert_eq!(place.get_type(), &vir_low::macros::ty!(PlaceOption)); + match place { + vir_low::Expression::DomainFuncApp(domain_func_app) + if domain_func_app.arguments.len() == 1 => + { + let argument = &domain_func_app.arguments[0]; + if domain_func_app.function_name == "place_option_some" { + true + } else { + is_place_non_aliased(argument) + } + } + vir_low::Expression::DomainFuncApp(domain_func_app) => { + assert_eq!(domain_func_app.function_name, "place_option_none"); + false + } + vir_low::Expression::LabelledOld(labelled_old) => is_place_non_aliased(&labelled_old.base), + _ => unreachable!("place: {place}"), + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/type_layouts/interface.rs b/prusti-viper/src/encoder/middle/core_proof/type_layouts/interface.rs index 90c7c1bac67..ab15a5d37aa 100644 --- a/prusti-viper/src/encoder/middle/core_proof/type_layouts/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/type_layouts/interface.rs @@ -3,7 +3,9 @@ use crate::encoder::{ high::{type_layouts::HighTypeLayoutsEncoderInterface, types::HighTypeEncoderInterface}, middle::core_proof::{ lowerer::Lowerer, - snapshots::{IntoBuiltinMethodSnapshot, IntoProcedureSnapshot, IntoSnapshot}, + snapshots::{ + IntoBuiltinMethodSnapshot, IntoProcedureSnapshot, IntoSnapshot, SnapshotValuesInterface, + }, }, }; use vir_crate::{ @@ -23,6 +25,14 @@ pub(in super::super) trait TypeLayoutsInterface { ty: &vir_mid::Type, generics: &impl WithConstArguments, ) -> SpannedEncodingResult; + /// The size multiplied by `repetitions`. + fn encode_type_size_expression_repetitions( + &mut self, + ty: &vir_mid::Type, + generics: &impl WithConstArguments, + repetitions: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult; fn encode_type_padding_size_expression( &mut self, ty: &vir_mid::Type, @@ -57,6 +67,24 @@ impl<'p, 'v: 'p, 'tcx: 'v> TypeLayoutsInterface for Lowerer<'p, 'v, 'tcx> { ); size.to_builtin_method_snapshot(self) } + fn encode_type_size_expression_repetitions( + &mut self, + ty: &vir_mid::Type, + generics: &impl WithConstArguments, + repetitions: vir_low::Expression, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let size = self.encode_type_size_expression2(ty, generics)?; + let size_type = self.size_type_mid()?; + self.construct_binary_op_snapshot( + vir_mid::BinaryOpKind::Mul, + &size_type, + &size_type, + repetitions, + size, + position, + ) + } fn encode_type_padding_size_expression( &mut self, ty: &vir_mid::Type, diff --git a/prusti-viper/src/encoder/middle/core_proof/types/interface.rs b/prusti-viper/src/encoder/middle/core_proof/types/interface.rs index aa2b046f5dd..4e9a5622b43 100644 --- a/prusti-viper/src/encoder/middle/core_proof/types/interface.rs +++ b/prusti-viper/src/encoder/middle/core_proof/types/interface.rs @@ -15,11 +15,14 @@ use crate::encoder::{ high::types::HighTypeEncoderInterface, middle::core_proof::{ addresses::AddressesInterface, + footprint::{DerefOwned, DerefOwnedRange, FootprintInterface}, lowerer::{DomainsLowererInterface, Lowerer}, snapshots::{ - IntoPureSnapshot, IntoSnapshot, SnapshotAdtsInterface, SnapshotDomainsInterface, - SnapshotValidityInterface, SnapshotValuesInterface, + IntoPureSnapshot, IntoSnapshot, IntoSnapshotLowerer, SnapshotAdtsInterface, + SnapshotDomainsInterface, SnapshotValidityInterface, SnapshotValuesInterface, + ValidityAssertionToSnapshot, }, + type_layouts::TypeLayoutsInterface, }, }; use prusti_common::config; @@ -31,7 +34,7 @@ use vir_crate::{ identifier::WithIdentifier, }, low::{self as vir_low}, - middle as vir_mid, + middle::{self as vir_mid}, }; #[derive(Default)] @@ -42,30 +45,35 @@ pub(in super::super) struct TypesState { encoded_unary_operations: FxHashSet, } -trait Private { - fn ensure_type_definition_for_decl( - &mut self, - ty: &vir_mid::Type, - type_decl: &vir_mid::TypeDecl, - ) -> SpannedEncodingResult<()>; - fn declare_simplification_axiom( - &mut self, - ty: &vir_mid::Type, - variant: &str, - parameters: Vec, - parameter_type: &vir_mid::Type, - simplification_result: vir_low::Expression, - ) -> SpannedEncodingResult<()>; - fn declare_evaluation_axiom( - &mut self, - ty: &vir_mid::Type, - variant: &str, - parameters: Vec, - evaluation_result: vir_low::Expression, - ) -> SpannedEncodingResult<()>; -} +// trait Private { +// fn ensure_type_definition_for_decl( +// &mut self, +// ty: &vir_mid::Type, +// type_decl: &vir_mid::TypeDecl, +// ) -> SpannedEncodingResult<()>; +// fn declare_simplification_axiom( +// &mut self, +// ty: &vir_mid::Type, +// variant: &str, +// parameters: Vec, +// parameter_type: &vir_mid::Type, +// simplification_result: vir_low::Expression, +// ) -> SpannedEncodingResult<()>; +// fn declare_evaluation_axiom( +// &mut self, +// ty: &vir_mid::Type, +// variant: &str, +// parameters: Vec, +// evaluation_result: vir_low::Expression, +// ) -> SpannedEncodingResult<()>; +// // fn purify_structural_invariant( +// // &mut self, +// // structural_invariant: Vec, +// // field_count: usize, +// // ) -> SpannedEncodingResult>; +// } -impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { +impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { fn ensure_type_definition_for_decl( &mut self, ty: &vir_mid::Type, @@ -130,8 +138,53 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { field.ty.to_snapshot(self)?, )); } + let parameters_with_validity = decl.fields.len(); + let invariant = if let Some(invariant) = &decl.structural_invariant { + let (deref_fields, deref_range_fields) = + self.structural_invariant_to_deref_fields(invariant)?; + for DerefOwned { + field_name, + field_type, + .. + } in &deref_fields + { + parameters.push(vir_low::VariableDecl::new(field_name, field_type.clone())); + } + for DerefOwnedRange { + field_name, + field_type, + .. + } in &deref_range_fields + { + parameters.push(vir_low::VariableDecl::new(field_name, field_type.clone())); + } + let mut validity_assertion_encoder = + ValidityAssertionToSnapshot::new((deref_fields, deref_range_fields)); + // let invariant = self.structural_invariant_to_pure_expression( + // invariant.clone(), + // ty, + // decl, + // &mut parameters, + // )?; + let mut conjuncts = Vec::new(); + for expression in invariant { + conjuncts.push( + validity_assertion_encoder + .expression_to_snapshot(self, expression, true)?, + ); + // conjuncts.push(expression.to_pure_bool_expression(self)?); + } + conjuncts.into_iter().conjoin() //.remove_acc_predicates() + } else { + true.into() + }; self.register_struct_constructor(&domain_name, parameters.clone())?; - self.encode_validity_axioms_struct(&domain_name, parameters, true.into())?; + self.encode_validity_axioms_struct_with_invariant( + &domain_name, + parameters, + parameters_with_validity, + invariant, + )?; } vir_mid::TypeDecl::Enum(decl) => { let mut variants = Vec::new(); @@ -159,27 +212,42 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { vir_mid::TypeDecl::Pointer(decl) => { self.ensure_type_definition(&decl.target_type)?; let address_type = self.address_type()?; - self.register_constant_constructor(&domain_name, address_type.clone())?; - self.encode_validity_axioms_primitive(&domain_name, address_type, true.into())?; + let mut parameters = vec![vir_low::VariableDecl::new("address", address_type)]; + if decl.target_type.is_slice() { + let len_type = self.size_type()?; + parameters.push(vir_low::VariableDecl::new("len", len_type)); + } + self.register_struct_constructor(&domain_name, parameters.clone())?; + // self.register_constant_constructor(&domain_name, address_type.clone())?; + // self.encode_validity_axioms_primitive(&domain_name, address_type, true.into())?; + self.encode_validity_axioms_struct(&domain_name, parameters)?; } - vir_mid::TypeDecl::Reference(reference) => { - self.ensure_type_definition(&reference.target_type)?; - let target_type = reference.target_type.to_snapshot(self)?; - if reference.uniqueness.is_unique() { - let parameters = vars! { + vir_mid::TypeDecl::Reference(decl) => { + self.ensure_type_definition(&decl.target_type)?; + let target_type = decl.target_type.to_snapshot(self)?; + if decl.uniqueness.is_unique() { + let mut parameters = vars! { address: Address, target_current: {target_type.clone()}, target_final: {target_type} }; + if decl.target_type.is_slice() { + let len_type = self.size_type()?; + parameters.push(vir_low::VariableDecl::new("len", len_type)); + } self.register_struct_constructor(&domain_name, parameters.clone())?; - self.encode_validity_axioms_struct(&domain_name, parameters, true.into())?; + self.encode_validity_axioms_struct(&domain_name, parameters)?; } else { - let parameters = vars! { + let mut parameters = vars! { address: Address, target_current: {target_type.clone()} }; + if decl.target_type.is_slice() { + let len_type = self.size_type()?; + parameters.push(vir_low::VariableDecl::new("len", len_type)); + } self.register_struct_constructor(&domain_name, parameters.clone())?; - self.encode_validity_axioms_struct(&domain_name, parameters, true.into())?; + self.encode_validity_axioms_struct(&domain_name, parameters)?; let no_alloc_parameters = vars! { target_current: {target_type} }; self.register_alternative_constructor_with_injectivity_axioms( &domain_name, @@ -187,17 +255,24 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { true, no_alloc_parameters.clone(), )?; + let parameters_with_validity = no_alloc_parameters.len(); self.encode_validity_axioms_struct_alternative_constructor( &domain_name, "no_alloc", no_alloc_parameters, + parameters_with_validity, true.into(), )?; } } vir_mid::TypeDecl::Never => { self.register_struct_constructor(&domain_name, Vec::new())?; - self.encode_validity_axioms_struct(&domain_name, Vec::new(), false.into())?; + self.encode_validity_axioms_struct_with_invariant( + &domain_name, + Vec::new(), + 0, + false.into(), + )?; } _ => unimplemented!("type: {:?}", type_decl), }; @@ -283,6 +358,313 @@ impl<'p, 'v: 'p, 'tcx: 'v> Private for Lowerer<'p, 'v, 'tcx> { } Ok(()) } + + /// Encodes the following axiom: + /// + /// $$$ + /// (first * mul >= second * mul) == (first >= second) + /// $$$ + /// + /// This holds because we are dealing with unsigned integers. + fn declare_usize_mul_simplification_axiom( + &mut self, + ty: &vir_mid::Type, + variant: &str, + first: vir_low::VariableDecl, + second: vir_low::VariableDecl, + mul: vir_low::VariableDecl, + parameter_type: &vir_mid::Type, + ) -> SpannedEncodingResult<()> { + let domain_name = self.encode_snapshot_domain_name(ty)?; + let bool_domain_name = self.encode_snapshot_domain_name(&vir_mid::Type::Bool)?; + let bool_type = vir_mid::Type::Bool.to_snapshot(self)?; + let snapshot_type = ty.to_snapshot(self)?; + let first_mul = vir_low::Expression::domain_function_call( + &domain_name, + self.snapshot_constructor_struct_alternative_name(&domain_name, variant)?, + vec![first.clone().into(), mul.clone().into()], + snapshot_type.clone(), + ); + let second_mul = vir_low::Expression::domain_function_call( + &domain_name, + self.snapshot_constructor_struct_alternative_name(&domain_name, variant)?, + vec![second.clone().into(), mul.clone().into()], + snapshot_type, + ); + // let body = vir_low::Expression::forall( + // vec![first, second, mul], + // vec![vir_low::Trigger::new(vec![source.clone()])], + // expr! { ([source] == [target]) }, + // ); + let ge_cmp_name = + self.encode_binary_op_variant(vir_mid::BinaryOpKind::GeCmp, parameter_type)?; + let le_cmp_name = + self.encode_binary_op_variant(vir_mid::BinaryOpKind::LeCmp, parameter_type)?; + { + let source = vir_low::Expression::domain_function_call( + &bool_domain_name, + self.snapshot_constructor_struct_alternative_name(&bool_domain_name, &ge_cmp_name)?, + vec![first_mul.clone(), second_mul.clone()], + bool_type.clone(), + ); + let target = vir_low::Expression::domain_function_call( + &bool_domain_name, + self.snapshot_constructor_struct_alternative_name(&bool_domain_name, &ge_cmp_name)?, + vec![first.clone().into(), second.clone().into()], + bool_type.clone(), + ); + let axiom = vir_low::DomainRewriteRuleDecl { + comment: None, + name: format!("{variant}$simplification_axiom_ge_cmp"), + variables: vec![first.clone(), second.clone(), mul.clone()], + egg_only: true, + triggers: None, + source, + target, + }; + // self.declare_axiom(&domain_name, axiom)?; + self.declare_rewrite_rule(&domain_name, axiom)?; + } + { + let source = vir_low::Expression::domain_function_call( + &bool_domain_name, + self.snapshot_constructor_struct_alternative_name(&bool_domain_name, &le_cmp_name)?, + vec![first_mul, second_mul], + bool_type.clone(), + ); + let target = vir_low::Expression::domain_function_call( + &bool_domain_name, + self.snapshot_constructor_struct_alternative_name(&bool_domain_name, &le_cmp_name)?, + vec![first.clone().into(), second.clone().into()], + bool_type.clone(), + ); + let axiom = vir_low::DomainRewriteRuleDecl { + comment: None, + name: format!("{variant}$simplification_axiom_le_cmp"), + variables: vec![first.clone(), second.clone(), mul], + egg_only: true, + triggers: None, + source, + target, + }; + // self.declare_axiom(&domain_name, axiom)?; + self.declare_rewrite_rule(&domain_name, axiom)?; + } + { + let source = vir_low::Expression::domain_function_call( + &bool_domain_name, + self.snapshot_constructor_struct_alternative_name(&bool_domain_name, &le_cmp_name)?, + vec![first.clone().into(), second.clone().into()], + bool_type.clone(), + ); + let target = vir_low::Expression::domain_function_call( + &bool_domain_name, + self.snapshot_constructor_struct_alternative_name(&bool_domain_name, &ge_cmp_name)?, + vec![second.clone().into(), first.clone().into()], + bool_type.clone(), + ); + let axiom = vir_low::DomainRewriteRuleDecl { + comment: None, + name: format!("{variant}$simplification_axiom_le_ge_commute"), + variables: vec![first.clone(), second.clone()], + egg_only: true, + triggers: None, + source, + target, + }; + // self.declare_axiom(&domain_name, axiom)?; + self.declare_rewrite_rule(&domain_name, axiom)?; + } + { + let source = vir_low::Expression::domain_function_call( + &bool_domain_name, + self.snapshot_constructor_struct_alternative_name(&bool_domain_name, &ge_cmp_name)?, + vec![first.clone().into(), second.clone().into()], + bool_type.clone(), + ); + let target = vir_low::Expression::domain_function_call( + &bool_domain_name, + self.snapshot_constructor_struct_alternative_name(&bool_domain_name, &le_cmp_name)?, + vec![second.clone().into(), first.clone().into()], + bool_type, + ); + let axiom = vir_low::DomainRewriteRuleDecl { + comment: None, + name: format!("{variant}$simplification_axiom_ge_le_commute"), + variables: vec![first, second], + egg_only: true, + triggers: None, + source, + target, + }; + // self.declare_axiom(&domain_name, axiom)?; + self.declare_rewrite_rule(&domain_name, axiom)?; + } + Ok(()) + } + + fn declare_commutativity_axiom( + &mut self, + ty: &vir_mid::Type, + variant: &str, + left: vir_low::VariableDecl, + right: vir_low::VariableDecl, + ) -> SpannedEncodingResult<()> { + let domain_name = self.encode_snapshot_domain_name(ty)?; + let op_constructor_1 = vir_low::Expression::domain_function_call( + &domain_name, + self.snapshot_constructor_struct_alternative_name(&domain_name, variant)?, + vec![left.clone().into(), right.clone().into()], + ty.to_snapshot(self)?, + ); + let op_constructor_2 = vir_low::Expression::domain_function_call( + &domain_name, + self.snapshot_constructor_struct_alternative_name(&domain_name, variant)?, + vec![right.clone().into(), left.clone().into()], + ty.to_snapshot(self)?, + ); + let axiom = vir_low::DomainRewriteRuleDecl { + comment: None, + name: format!("{variant}$commutativity_axiom"), + egg_only: true, + variables: vec![left, right], + triggers: None, + source: op_constructor_1, + target: op_constructor_2, + }; + self.declare_rewrite_rule(&domain_name, axiom)?; + Ok(()) + } + + fn declare_zero_axioms( + &mut self, + op: vir_low::BinaryOpKind, + ty: &vir_mid::Type, + variant: &str, + variable: vir_low::VariableDecl, + ) -> SpannedEncodingResult<()> { + let domain_name = self.encode_snapshot_domain_name(ty)?; + let zero = self.construct_constant_snapshot(ty, 0.into(), Default::default())?; + let (source, target) = match op { + vir_low::BinaryOpKind::Add | vir_low::BinaryOpKind::Sub => { + let source = vir_low::Expression::domain_function_call( + &domain_name, + self.snapshot_constructor_struct_alternative_name(&domain_name, variant)?, + vec![variable.clone().into(), zero], + ty.to_snapshot(self)?, + ); + let target = variable.clone().into(); + (source, target) + } + vir_low::BinaryOpKind::Mul + | vir_low::BinaryOpKind::Div + | vir_low::BinaryOpKind::Mod => { + let source = vir_low::Expression::domain_function_call( + &domain_name, + self.snapshot_constructor_struct_alternative_name(&domain_name, variant)?, + vec![zero.clone(), variable.clone().into()], + ty.to_snapshot(self)?, + ); + let target = zero; + (source, target) + } + _ => { + return Ok(()); // No zero axiom for non-numeric operators. + } + }; + let axiom = vir_low::DomainRewriteRuleDecl { + comment: None, + name: format!("{variant}$zero_axiom"), + egg_only: true, + variables: vec![variable], + triggers: None, + source, + target, + }; + self.declare_rewrite_rule(&domain_name, axiom)?; + Ok(()) + } + + // fn purify_structural_invariant( + // &mut self, + // structural_invariant: Vec, + // field_count: usize, + // ) -> SpannedEncodingResult> { + + // // TODO: Create deref fields in vir_high together with a required + // // structural invariant that links their values? Probably does not work + // // because I need different treatment in predicate and snapshot + // // encoders. + + // // TODO: Maybe a better idea would be to have code that computes a + // // footprint of an expression? Then I could also use it for pure + // // functions. + + // struct Purifier<'l, 'p, 'v, 'tcx> { + // lowerer: &'l mut Lowerer<'p, 'v, 'tcx>, + // field_count: usize, + // } + // impl<'l, 'p, 'v, 'tcx> vir_mid::visitors::ExpressionFolder for Purifier<'l, 'p, 'v, 'tcx> { + // fn fold_acc_predicate_enum( + // &mut self, + // acc_predicate: vir_mid::AccPredicate, + // ) -> vir_mid::Expression { + // match *acc_predicate.predicate { + // vir_mid::Predicate::LifetimeToken(_) => { + // unimplemented!() + // } + // vir_mid::Predicate::MemoryBlockStack(_) + // | vir_mid::Predicate::MemoryBlockStackDrop(_) + // | vir_mid::Predicate::MemoryBlockHeap(_) + // | vir_mid::Predicate::MemoryBlockHeapDrop(_) => true.into(), + // vir_mid::Predicate::OwnedNonAliased(predicate) => { + // match predicate.place { + // vir_mid::Expression::Deref(vir_mid::Deref { + // base: + // box vir_mid::Expression::Field(vir_mid::Field { + // box base, + // field, + // .. + // }), + // ty, + // position, + // }) => { + // // let parameter = vir_mid::VariableDecl::new( + // // format!("{}$deref", field.name), + // // ty, + // // ); + // let app = vir_mid::Expression::builtin_func_app( + // vir_mid::BuiltinFunc::IsValid, + // Vec::new(), + // vec![ + // vir_mid::Expression::field( + // base, + // vir_mid::FieldDecl { + // name: format!("{}$deref", field.name), + // index: self.field_count, + // ty, + // }, + // position, + // )], + // vir_mid::Type::Bool, + // position, + // ); + // self.field_count += 1; + // app + // // self.lowerer.encode_snapshot_valid_call_for_type(parameter.into(), ty)? + // } + // _ => unimplemented!(), + // } + // } + // } + // } + // } + // let mut purifier = Purifier { lowerer: self, field_count }; + // Ok(structural_invariant + // .into_iter() + // .map(|expression| purifier.fold_expression(expression)) + // .collect()) + // } } pub(in super::super) trait TypesInterface { @@ -310,6 +692,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> TypesInterface for Lowerer<'p, 'v, 'tcx> { | vir_mid::Type::MInt | vir_mid::Type::MPerm | vir_mid::Type::Lifetime + | vir_mid::Type::MByte + | vir_mid::Type::MBytes ) { // Natively supported types, nothing to do. return Ok(()); @@ -332,10 +716,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> TypesInterface for Lowerer<'p, 'v, 'tcx> { } fn encode_unary_op_variant( &mut self, - op: vir_mid::UnaryOpKind, + op_mid: vir_mid::UnaryOpKind, argument_type: &vir_mid::Type, ) -> SpannedEncodingResult { - let variant_name = format!("{}_{}", op, argument_type.get_identifier()); + let variant_name = format!("{}_{}", op_mid, argument_type.get_identifier()); if !self .types_state .encoded_unary_operations @@ -348,14 +732,16 @@ impl<'p, 'v: 'p, 'tcx: 'v> TypesInterface for Lowerer<'p, 'v, 'tcx> { let snapshot_type = argument_type.to_snapshot(self)?; let result_type = argument_type; let result_domain = self.encode_snapshot_domain_name(result_type)?; + let op = op_mid.to_snapshot(self)?; self.register_alternative_constructor( &result_domain, &variant_name, + Some(op), + None, false, vars! { argument: {snapshot_type.clone()} }, )?; // Simplification axioms. - let op = op.to_snapshot(self)?; let simplification = match argument_type { vir_mid::Type::Bool => { assert_eq!(op, vir_low::UnaryOpKind::Not); @@ -395,10 +781,11 @@ impl<'p, 'v: 'p, 'tcx: 'v> TypesInterface for Lowerer<'p, 'v, 'tcx> { } fn encode_binary_op_variant( &mut self, - op: vir_mid::BinaryOpKind, + op_mid: vir_mid::BinaryOpKind, argument_type: &vir_mid::Type, ) -> SpannedEncodingResult { - let variant_name = format!("{}_{}", op, argument_type.get_identifier()); + let variant_name = format!("{}_{}", op_mid, argument_type.get_identifier()); + // format!("{}_{}", op, argument_type.get_identifier()); if !self .types_state .encoded_binary_operations @@ -409,16 +796,18 @@ impl<'p, 'v: 'p, 'tcx: 'v> TypesInterface for Lowerer<'p, 'v, 'tcx> { .insert(variant_name.clone()); use vir_low::macros::*; let snapshot_type = argument_type.to_snapshot(self)?; - let result_type = op.get_result_type(argument_type); + let result_type = op_mid.get_result_type(argument_type); let result_domain = self.encode_snapshot_domain_name(result_type)?; + let op = op_mid.to_snapshot(self)?; self.register_alternative_constructor( &result_domain, &variant_name, + None, + Some(op), false, vars! { left: {snapshot_type.clone()}, right: {snapshot_type.clone()} }, )?; // Simplification axioms. - let op = op.to_snapshot(self)?; let constant_type = match argument_type { vir_mid::Type::Bool => Some(ty! { Bool }), vir_mid::Type::Int(_) => Some(ty! {Int}), @@ -426,32 +815,58 @@ impl<'p, 'v: 'p, 'tcx: 'v> TypesInterface for Lowerer<'p, 'v, 'tcx> { _ => None, }; if let Some(constant_type) = constant_type { - var_decls! { left: {constant_type.clone()}, right: {constant_type} }; - let result = vir_low::Expression::binary_op_no_pos(op, expr! {left}, expr! {right}); - self.declare_simplification_axiom( - result_type, - &variant_name, - vec![left, right], - argument_type, - result, - )?; - var_decls! {left: {snapshot_type.clone()}, right: {snapshot_type}}; + var_decls! { left_const: {constant_type.clone()}, right_const: {constant_type} }; + let result = vir_low::Expression::binary_op_no_pos( + op, + expr! {left_const}, + expr! {right_const}, + ); + var_decls! {left_snap: {snapshot_type.clone()}, right_snap: {snapshot_type.clone()}}; let destructor_left = self.obtain_constant_value( argument_type, - left.clone().into(), + left_snap.clone().into(), Default::default(), )?; let destructor_right = self.obtain_constant_value( argument_type, - right.clone().into(), + right_snap.clone().into(), Default::default(), )?; + self.declare_simplification_axiom( + result_type, + &variant_name, + vec![left_const, right_const], + argument_type, + result, + )?; self.declare_evaluation_axiom( result_type, &variant_name, - vec![left, right], + vec![left_snap.clone(), right_snap.clone()], vir_low::Expression::binary_op_no_pos(op, destructor_left, destructor_right), )?; + if argument_type == &vir_mid::Type::Int(vir_mid::ty::Int::Usize) + && op == vir_low::BinaryOpKind::Mul + { + var_decls! { mul_snap: {snapshot_type} }; + self.declare_usize_mul_simplification_axiom( + result_type, + &variant_name, + left_snap.clone(), + right_snap.clone(), + mul_snap, + argument_type, + )?; + } + if matches!(op, vir_low::BinaryOpKind::Add | vir_low::BinaryOpKind::Mul) { + self.declare_commutativity_axiom( + result_type, + &variant_name, + left_snap.clone(), + right_snap, + )?; + } + self.declare_zero_axioms(op, result_type, &variant_name, left_snap)?; } else if op == vir_low::BinaryOpKind::EqCmp { // FIXME: For now, we treat Rust's == as bit equality. var_decls! { left: {snapshot_type.clone()}, right: {snapshot_type} }; diff --git a/prusti-viper/src/encoder/middle/core_proof/utils/place_domain_encoder.rs b/prusti-viper/src/encoder/middle/core_proof/utils/place_domain_encoder.rs index d5d85b9b1ec..879474b5c92 100644 --- a/prusti-viper/src/encoder/middle/core_proof/utils/place_domain_encoder.rs +++ b/prusti-viper/src/encoder/middle/core_proof/utils/place_domain_encoder.rs @@ -23,6 +23,11 @@ pub(in super::super) trait PlaceExpressionDomainEncoder { lowerer: &mut Lowerer, arg: vir_low::Expression, ) -> SpannedEncodingResult; + fn encode_labelled_old( + &mut self, + expression: &vir_mid::expression::LabelledOld, + lowerer: &mut Lowerer, + ) -> SpannedEncodingResult; fn encode_array_index_axioms( &mut self, base_type: &vir_mid::Type, @@ -82,6 +87,12 @@ pub(in super::super) trait PlaceExpressionDomainEncoder { *position, )? } + vir_mid::Expression::LabelledOld(expression) => { + self.encode_labelled_old(expression, lowerer)? + } + vir_mid::Expression::EvalIn(expression) => { + self.encode_expression(&expression.body, lowerer)? + } x => unimplemented!("{}", x), }; Ok(result) diff --git a/prusti-viper/src/encoder/middle/core_proof/viewshifts/interface.rs b/prusti-viper/src/encoder/middle/core_proof/viewshifts/interface.rs new file mode 100644 index 00000000000..2d831e1f881 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/viewshifts/interface.rs @@ -0,0 +1,246 @@ +use super::state::{ViewShiftBody, ViewShiftSignature}; +use crate::encoder::{ + errors::SpannedEncodingResult, + middle::core_proof::lowerer::{Lowerer, MethodsLowererInterface, PredicatesLowererInterface}, +}; +use rustc_hash::FxHashMap; +use vir_crate::low::{self as vir_low, operations::ty::Typed}; + +pub(in super::super) trait ViewShiftsInterface { + /// Encode a viewshift to be used in the postcondition of a method and + /// declare its dependencies: + /// + /// 1. An opaque predicate used as a resource for the viewshift. + /// 2. A helper method that applies the viewshift. + fn encode_view_shift_return( + &mut self, + name: &str, + arguments: Vec, + precondition: Vec, + postcondition: Vec, + position: vir_low::Position, + ) -> SpannedEncodingResult; + + fn encode_apply_view_shift( + &mut self, + name: &str, + condition: Option, + arguments: Vec, + position: vir_low::Position, + ) -> SpannedEncodingResult; +} + +impl<'p, 'v: 'p, 'tcx: 'v> Lowerer<'p, 'v, 'tcx> { + fn encode_predicate_name(&mut self, name: &str) -> String { + format!("view_shift${name}") + } + + fn encode_method_name(&mut self, name: &str) -> String { + format!("apply_view_shift${name}") + } + + fn encode_view_shift_predciate_and_apply_method( + &mut self, + name: &str, + arguments: Vec, + mut precondition: Vec, + mut postcondition: Vec, + position: vir_low::Position, + ) -> SpannedEncodingResult<()> { + let mut parameters = Vec::new(); + let mut replacements = FxHashMap::default(); + for (i, argument) in arguments.iter().enumerate() { + if let vir_low::Expression::Local(local) = argument { + parameters.push(local.variable.clone()); + } else { + let parameter = + vir_low::VariableDecl::new(format!("arg${i}"), argument.get_type().clone()); + let parameter_expression = parameter.clone().into(); + replacements.insert(argument.clone(), parameter_expression); + parameters.push(parameter); + } + } + // precondition = precondition + // .into_iter() + // .map(|expression| expression.replace_subexpressions(&replacements)) + // .collect(); + // postcondition = postcondition + // .into_iter() + // .map(|expression| expression.replace_subexpressions(&replacements)) + // .collect(); + precondition = self.apply_replacements(precondition, &replacements); + postcondition = self.apply_replacements(postcondition, &replacements); + let predicate_name = self.encode_predicate_name(name); + let view_shift_predicate = vir_low::Expression::predicate_access_predicate( + predicate_name.clone(), + parameters + .iter() + .map(|parameter| parameter.clone().into()) + .collect(), + vir_low::Expression::full_permission(), + position, + ); + let view_shift_predicate_decl = vir_low::PredicateDecl::new( + predicate_name, + vir_low::PredicateKind::WithoutSnapshotWhole, + parameters.clone(), + None, + ); + self.declare_predicate(view_shift_predicate_decl)?; + precondition.push(view_shift_predicate); + let apply_view_shift_method = vir_low::MethodDecl::new( + self.encode_method_name(name), + vir_low::MethodKind::MirOperation, + parameters, + Vec::new(), + precondition, + postcondition, + None, + ); + self.declare_method(apply_view_shift_method)?; + Ok(()) + } + + fn construct_view_shift_signature( + &mut self, + name: &str, + arguments: &[vir_low::Expression], + ) -> ViewShiftSignature { + let types = arguments + .iter() + .map(|argument| argument.get_type().clone()) + .collect(); + (name.to_string(), types) + } + + fn construct_replacements( + &self, + arguments: &[vir_low::Expression], + ) -> FxHashMap { + let mut replacements = FxHashMap::default(); + for (i, argument) in arguments.iter().enumerate() { + let parameter = + vir_low::VariableDecl::new(format!("arg${i}"), argument.get_type().clone()); + let parameter_expression = parameter.clone().into(); + replacements.insert(argument.clone(), parameter_expression); + } + replacements + } + + fn apply_replacements( + &self, + expression: Vec, + replacements: &FxHashMap, + ) -> Vec { + expression + .into_iter() + .map(|expression| expression.replace_subexpressions(replacements)) + .collect() + } + + fn construct_view_shift_body( + &mut self, + _name: &str, + arguments: &[vir_low::Expression], + precondition: &[vir_low::Expression], + postcondition: &[vir_low::Expression], + ) -> ViewShiftBody { + let replacements = self.construct_replacements(arguments); + let precondition = self.apply_replacements(precondition.to_vec(), &replacements); + let postcondition = self.apply_replacements(postcondition.to_vec(), &replacements); + (precondition, postcondition) + } + + fn register_view_shift_body( + &mut self, + name: &str, + arguments: &[vir_low::Expression], + precondition: &[vir_low::Expression], + postcondition: &[vir_low::Expression], + ) { + if !cfg!(debug_assertions) { + return; + } + let signature = self.construct_view_shift_signature(name, arguments); + let body = self.construct_view_shift_body(name, arguments, precondition, postcondition); + self.view_shifts_state + .encoded_view_content + .insert(signature, body); + } + + fn assert_same_view_shift( + &mut self, + name: &str, + arguments: &[vir_low::Expression], + precondition: &[vir_low::Expression], + postcondition: &[vir_low::Expression], + ) { + if !cfg!(debug_assertions) { + return; + } + let signature = self.construct_view_shift_signature(name, arguments); + let body = self.construct_view_shift_body(name, arguments, precondition, postcondition); + let old_body = self + .view_shifts_state + .encoded_view_content + .get(&signature) + .unwrap(); + assert_eq!(body, *old_body); + } +} + +impl<'p, 'v: 'p, 'tcx: 'v> ViewShiftsInterface for Lowerer<'p, 'v, 'tcx> { + fn encode_view_shift_return( + &mut self, + name: &str, + arguments: Vec, + precondition: Vec, + postcondition: Vec, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let signature = self.construct_view_shift_signature(name, &arguments); + if !self + .view_shifts_state + .encoded_view_shifts + .contains(&signature) + { + self.register_view_shift_body(name, &arguments, &precondition, &postcondition); + self.encode_view_shift_predciate_and_apply_method( + name, + arguments.clone(), + precondition, + postcondition, + position, + )?; + self.view_shifts_state.encoded_view_shifts.insert(signature); + } else { + self.assert_same_view_shift(name, &arguments, &precondition, &postcondition); + } + let predicate_name = self.encode_predicate_name(name); + let view_shift_predicate = vir_low::Expression::predicate_access_predicate( + predicate_name, + arguments, + vir_low::Expression::full_permission(), + position, + ); + Ok(view_shift_predicate) + } + + fn encode_apply_view_shift( + &mut self, + name: &str, + condition: Option, + arguments: Vec, + position: vir_low::Position, + ) -> SpannedEncodingResult { + let method_name = self.encode_method_name(name); + let method_call = + vir_low::Statement::method_call(method_name, arguments, Vec::new(), position); + let statement = if let Some(condition) = condition { + vir_low::Statement::conditional(condition, vec![method_call], Vec::new(), position) + } else { + method_call + }; + Ok(statement) + } +} diff --git a/prusti-viper/src/encoder/middle/core_proof/viewshifts/mod.rs b/prusti-viper/src/encoder/middle/core_proof/viewshifts/mod.rs new file mode 100644 index 00000000000..6ae972ca5f0 --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/viewshifts/mod.rs @@ -0,0 +1,4 @@ +mod interface; +mod state; + +pub(super) use self::{interface::ViewShiftsInterface, state::ViewShiftsState}; diff --git a/prusti-viper/src/encoder/middle/core_proof/viewshifts/state.rs b/prusti-viper/src/encoder/middle/core_proof/viewshifts/state.rs new file mode 100644 index 00000000000..1da31037ddf --- /dev/null +++ b/prusti-viper/src/encoder/middle/core_proof/viewshifts/state.rs @@ -0,0 +1,12 @@ +use rustc_hash::{FxHashMap, FxHashSet}; +use vir_crate::low::{self as vir_low}; + +pub(super) type ViewShiftSignature = (String, Vec); +pub(super) type ViewShiftBody = (Vec, Vec); + +#[derive(Default)] +pub(in super::super) struct ViewShiftsState { + pub(super) encoded_view_shifts: FxHashSet, + /// Used to debug assert that signature uniquely identifies the viewshift. + pub(super) encoded_view_content: FxHashMap, +} diff --git a/prusti-viper/src/encoder/mir/contracts/contracts.rs b/prusti-viper/src/encoder/mir/contracts/contracts.rs index 55477369c19..4025b9f7a29 100644 --- a/prusti-viper/src/encoder/mir/contracts/contracts.rs +++ b/prusti-viper/src/encoder/mir/contracts/contracts.rs @@ -2,10 +2,7 @@ use super::borrows::BorrowInfo; use crate::encoder::places; use prusti_interface::{environment::Environment, specs::typed}; use prusti_rustc_interface::{ - hir::{ - def_id::{DefId, LocalDefId}, - Mutability, - }, + hir::{def_id::DefId, Mutability}, middle::{mir, ty::subst::SubstsRef}, }; use rustc_hash::FxHashMap; @@ -109,7 +106,7 @@ impl ProcedureContractGeneric { &'a self, env: &'a Environment<'tcx>, substs: SubstsRef<'tcx>, - ) -> Option<(LocalDefId, SubstsRef<'tcx>)> { + ) -> Option<(DefId, SubstsRef<'tcx>)> { match self.specification.terminates { typed::SpecificationItem::Empty => None, typed::SpecificationItem::Inherent(t) | typed::SpecificationItem::Refined(_, t) => { diff --git a/prusti-viper/src/encoder/mir/errors/interface.rs b/prusti-viper/src/encoder/mir/errors/interface.rs index 180c08dacaf..0c4373f0cc7 100644 --- a/prusti-viper/src/encoder/mir/errors/interface.rs +++ b/prusti-viper/src/encoder/mir/errors/interface.rs @@ -25,12 +25,12 @@ pub(crate) trait ErrorInterface { error_ctxt: ErrorCtxt, ) -> vir_high::Position; fn set_surrounding_error_context( - &mut self, + &self, position: vir_high::Position, error_ctxt: ErrorCtxt, ) -> vir_high::Position; fn set_surrounding_error_context_for_expression( - &mut self, + &self, expression: vir_high::Expression, default_position: vir_high::Position, error_ctxt: ErrorCtxt, @@ -91,7 +91,7 @@ impl<'v, 'tcx: 'v> ErrorInterface for super::super::super::Encoder<'v, 'tcx> { new_position.into() } fn set_surrounding_error_context( - &mut self, + &self, position: vir_high::Position, error_ctxt: ErrorCtxt, ) -> vir_high::Position { @@ -104,14 +104,14 @@ impl<'v, 'tcx: 'v> ErrorInterface for super::super::super::Encoder<'v, 'tcx> { /// 1. `default_position` if `position.is_default()`. /// 2. With surrounding error context otherwise. fn set_surrounding_error_context_for_expression( - &mut self, + &self, expression: vir_high::Expression, default_position: vir_high::Position, error_ctxt: ErrorCtxt, ) -> vir_high::Expression { assert!(!default_position.is_default()); struct Visitor<'p, 'v: 'p, 'tcx: 'v> { - encoder: &'p mut super::super::super::Encoder<'v, 'tcx>, + encoder: &'p super::super::super::Encoder<'v, 'tcx>, default_position: vir_high::Position, error_ctxt: ErrorCtxt, } diff --git a/prusti-viper/src/encoder/mir/places/interface.rs b/prusti-viper/src/encoder/mir/places/interface.rs index 3690cf45413..2b5dc3158ac 100644 --- a/prusti-viper/src/encoder/mir/places/interface.rs +++ b/prusti-viper/src/encoder/mir/places/interface.rs @@ -188,7 +188,9 @@ impl<'v, 'tcx: 'v> PlacesEncoderInterface<'tcx> for super::super::super::Encoder .with_span(declaration_span)?; if parent_type.is_union() { // We treat union fields as variants. - let union_decl = self.encode_type_def_high(&parent_type)?.unwrap_union(); + let union_decl = self + .encode_type_def_high(&parent_type, false)? + .unwrap_union(); let variant = &union_decl.variants[field.index()]; let variant_index: vir_high::ty::VariantIndex = variant.name.clone().into(); let variant_type = parent_type.variant(variant_index.clone()); diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/builtin_function_encoder.rs b/prusti-viper/src/encoder/mir/procedures/encoder/builtin_function_encoder.rs index adc3be26a3b..bdea6156c1c 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/builtin_function_encoder.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/builtin_function_encoder.rs @@ -35,95 +35,96 @@ impl<'p, 'v, 'tcx> BuiltinFuncAppEncoder<'p, 'v, 'tcx> for super::ProcedureEncod .name .get_absolute_item_name(called_def_id); - let make_manual_assign = - |encoder: &mut Self, - block_builder: &mut BasicBlockBuilder, - rhs_gen: &mut dyn FnMut(_, Vec, _) -> vir_high::Expression| - -> SpannedEncodingResult<()> { - let (target_place, target_block) = (destination, target.unwrap()); - let position = encoder - .encoder - .error_manager() - .register_error(span, ErrorCtxt::WritePlace, encoder.def_id) - .into(); - let encoded_target_place = encoder - .encoder - .encode_place_high(encoder.mir, target_place, None)? - .set_default_position(position); - let encoded_args = args - .iter() - .map(|arg| encoder.encode_statement_operand(location, arg)) - .collect::, _>>()?; - for encoded_arg in encoded_args.iter() { - let statement = vir_high::Statement::consume_no_pos(encoded_arg.clone()); - block_builder.add_statement(encoder.encoder.set_statement_error_ctxt( - statement, - span, - ErrorCtxt::ProcedureCall, - encoder.def_id, - )?); - } - let target_place_local = if let Some(target_place_local) = target_place.as_local() { - target_place_local - } else { - unimplemented!() - }; - let size = encoder.encoder.encode_type_size_expression( - encoder - .encoder - .get_local_type(encoder.mir, target_place_local)?, - )?; - let target_memory_block = vir_high::Predicate::memory_block_stack_no_pos( - encoded_target_place.clone(), - size, - ); - block_builder.add_statement(encoder.encoder.set_statement_error_ctxt( - vir_high::Statement::exhale_no_pos(target_memory_block), - span, - ErrorCtxt::ProcedureCall, - encoder.def_id, - )?); - let inhale_statement = vir_high::Statement::inhale_no_pos( - vir_high::Predicate::owned_non_aliased_no_pos(encoded_target_place.clone()), - ); + let make_manual_assign = |encoder: &mut Self, + block_builder: &mut BasicBlockBuilder, + rhs_gen: &mut dyn FnMut( + _, + Vec, + _, + ) -> vir_high::Expression| + -> SpannedEncodingResult<()> { + let (target_place, target_block) = (destination, target.unwrap()); + let position = encoder + .encoder + .error_manager() + .register_error(span, ErrorCtxt::WritePlace, encoder.def_id) + .into(); + let encoded_target_place = encoder + .encoder + .encode_place_high(encoder.mir, target_place, None)? + .set_default_position(position); + let encoded_args = args + .iter() + .map(|arg| encoder.encode_statement_operand_no_refs(block_builder, location, arg)) + .collect::, _>>()?; + for encoded_arg in encoded_args.iter() { + let statement = vir_high::Statement::consume_no_pos(encoded_arg.clone()); block_builder.add_statement(encoder.encoder.set_statement_error_ctxt( - inhale_statement, + statement, span, ErrorCtxt::ProcedureCall, encoder.def_id, )?); - let type_arguments = encoder + } + let target_place_local = if let Some(target_place_local) = target_place.as_local() { + target_place_local + } else { + unimplemented!() + }; + let size = encoder.encoder.encode_type_size_expression( + encoder .encoder - .encode_generic_arguments_high(called_def_id, call_substs) - .with_span(span)?; + .get_local_type(encoder.mir, target_place_local)?, + )?; + let target_memory_block = + vir_high::Predicate::memory_block_stack_no_pos(encoded_target_place.clone(), size); + block_builder.add_statement(encoder.encoder.set_statement_error_ctxt( + vir_high::Statement::exhale_predicate_no_pos(target_memory_block), + span, + ErrorCtxt::ProcedureCall, + encoder.def_id, + )?); + let inhale_statement = vir_high::Statement::inhale_predicate_no_pos( + vir_high::Predicate::owned_non_aliased_no_pos(encoded_target_place.clone()), + ); + block_builder.add_statement(encoder.encoder.set_statement_error_ctxt( + inhale_statement, + span, + ErrorCtxt::ProcedureCall, + encoder.def_id, + )?); + let type_arguments = encoder + .encoder + .encode_generic_arguments_high(called_def_id, call_substs) + .with_span(span)?; - let encoded_arg_expressions = - encoded_args.into_iter().map(|arg| arg.expression).collect(); + let encoded_arg_expressions = + encoded_args.into_iter().map(|arg| arg.expression).collect(); - let target_type = encoded_target_place.get_type().clone(); + let target_type = encoded_target_place.get_type().clone(); - let expression = vir_high::Expression::equals( - encoded_target_place, - rhs_gen(type_arguments, encoded_arg_expressions, target_type), - ); - let assume_statement = encoder.encoder.set_statement_error_ctxt( - vir_high::Statement::assume_no_pos(expression), - span, - ErrorCtxt::UnexpectedAssumeMethodPostcondition, - encoder.def_id, - )?; - block_builder.add_statement(encoder.encoder.set_statement_error_ctxt( - assume_statement, - span, - ErrorCtxt::ProcedureCall, - encoder.def_id, - )?); - encoder.encode_lft_for_block(target_block, location, block_builder)?; - let target_label = encoder.encode_basic_block_label(target_block); - let successor = vir_high::Successor::Goto(target_label); - block_builder.set_successor_jump(successor); - Ok(()) - }; + let expression = vir_high::Expression::equals( + encoded_target_place, + rhs_gen(type_arguments, encoded_arg_expressions, target_type), + ); + let assume_statement = encoder.encoder.set_statement_error_ctxt( + vir_high::Statement::assume_no_pos(expression), + span, + ErrorCtxt::UnexpectedAssumeMethodPostcondition, + encoder.def_id, + )?; + block_builder.add_statement(encoder.encoder.set_statement_error_ctxt( + assume_statement, + span, + ErrorCtxt::ProcedureCall, + encoder.def_id, + )?); + encoder.encode_lft_for_block(target_block, location, block_builder)?; + let target_label = encoder.encode_basic_block_label(target_block); + let successor = vir_high::Successor::Goto(target_label); + block_builder.set_successor_jump(successor); + Ok(()) + }; let make_builtin_call = |encoder: &mut Self, block_builder: &mut BasicBlockBuilder, @@ -155,7 +156,7 @@ impl<'p, 'v, 'tcx> BuiltinFuncAppEncoder<'p, 'v, 'tcx> for super::ProcedureEncod }) { let lhs = self - .encode_statement_operand(location, &args[0])? + .encode_statement_operand_no_refs(block_builder, location, &args[0])? .expression; if lhs.get_type() == &vir_high::Type::Int(vir_high::ty::Int::Unbounded) { use vir_high::BinaryOpKind::*; @@ -199,6 +200,9 @@ impl<'p, 'v, 'tcx> BuiltinFuncAppEncoder<'p, 'v, 'tcx> for super::ProcedureEncod unimplemented!(); } } + "prusti_contracts::prusti_take_lifetime" => { + make_builtin_call(self, block_builder, vir_high::BuiltinFunc::TakeLifetime)? + } "prusti_contracts::Int::new" => { make_builtin_call(self, block_builder, vir_high::BuiltinFunc::NewInt)? } @@ -243,7 +247,7 @@ impl<'p, 'v, 'tcx> BuiltinFuncAppEncoder<'p, 'v, 'tcx> for super::ProcedureEncod } "std::ops::Index::index" | "core::ops::Index::index" => { let lhs = self - .encode_statement_operand(location, &args[0])? + .encode_statement_operand_no_refs(block_builder, location, &args[0])? .expression; let typ = match lhs.get_type() { vir_high::Type::Reference(vir_high::ty::Reference { target_type, .. }) => { @@ -263,7 +267,7 @@ impl<'p, 'v, 'tcx> BuiltinFuncAppEncoder<'p, 'v, 'tcx> for super::ProcedureEncod } "std::cmp::PartialEq::eq" => { let lhs = self - .encode_statement_operand(location, &args[0])? + .encode_statement_operand_no_refs(block_builder, location, &args[0])? .expression; if matches!( lhs.get_type(), diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/check_mode_converters.rs b/prusti-viper/src/encoder/mir/procedures/encoder/check_mode_converters.rs new file mode 100644 index 00000000000..0f26b73ccab --- /dev/null +++ b/prusti-viper/src/encoder/mir/procedures/encoder/check_mode_converters.rs @@ -0,0 +1,230 @@ +use super::ProcedureEncoder; +use crate::encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + mir::{specifications::SpecificationsInterface, types::MirTypeEncoderInterface}, + Encoder, +}; + +use vir_crate::{ + common::{ + check_mode::CheckMode, + expression::{BinaryOperationHelpers, ExpressionIterator}, + position::Positioned, + }, + high::{self as vir_high, operations::ty::Typed}, +}; + +impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { + /// Convert expression to the one usable for the current check mode: + /// + /// * For `Both` and `Specifications`: keep the expression unchanged. + /// * For `CoreProof` keep only the raw pointer dereferences because we need + /// to check that they are framed. + /// + /// If `disallow_permissions` is true, then checks that the expression does + /// not contain accesibility predicates. + pub(super) fn convert_expression_to_check_mode( + &mut self, + expression: vir_high::Expression, + disallow_permissions: bool, + framing_variables: &[vir_high::VariableDecl], + ) -> SpannedEncodingResult> { + if disallow_permissions && !expression.is_pure() { + let span = self + .encoder + .error_manager() + .position_manager() + .get_span(expression.position().into()) + .cloned() + .unwrap(); + return Err(SpannedEncodingError::incorrect( + "only unsafe functions can use permissions in their contracts", + span, + )); + } + match self.check_mode { + CheckMode::MemorySafety => { + // Unsafe functions are checked with `CheckMode::UnsafeSafety`. For all + // other functions it is forbidden to have accessibility + // predicates in their contracts. + assert!(disallow_permissions); + // Framing will be checked with `CheckMode::MemorySafetyWithFunctional`. + Ok(None) + } + CheckMode::MemorySafetyWithFunctional => { + // Unsafe functions are checked with `CheckMode::UnsafeSafety`. For all + // other functions it is forbidden to have accessibility + // predicates in their contracts. + assert!(disallow_permissions); + // Framing is checked automatically by the encoding. + Ok(Some(expression)) + } + CheckMode::PurificationFunctional => { + // Unsafe functions are checked with `CheckMode::UnsafeSafety`. For all + // other functions it is forbidden to have accessibility + // predicates in their contracts. + assert!(disallow_permissions); + Ok(Some(expression)) + } + CheckMode::PurificationSoudness => { + // Check comment for `CheckMode::PurificationFunctional`. + assert!(disallow_permissions); + // Even though we forbid accessibility predicates in safe + // functions, we may still have raw pointers in specifications + // that are framed by type invariants. + let dereferenced_places = expression.collect_guarded_dereferenced_places(); + if dereferenced_places.is_empty() { + Ok(None) + } else { + let framing_places: Vec = framing_variables + .iter() + .map(|var| var.clone().into()) + .collect(); + let check = construct_framing_assertion( + self.encoder, + dereferenced_places, + &framing_places, + )?; + Ok(Some(check)) + } + } + CheckMode::UnsafeSafety => { + // Framing is checked automatically by the encoding. + Ok(Some(expression)) + } + } + } + + pub(super) fn convert_expression_to_check_mode_call_site( + &mut self, + expression: vir_high::Expression, + is_unsafe: bool, + framing_arguments: &[vir_high::Expression], + ) -> SpannedEncodingResult> { + match self.check_mode { + CheckMode::MemorySafety => { + if is_unsafe { + // We are calling an unsafe function from a safe one. + Ok(Some(expression)) + } else { + Ok(None) + } + } + CheckMode::MemorySafetyWithFunctional + | CheckMode::PurificationFunctional + | CheckMode::UnsafeSafety => Ok(Some(expression)), + CheckMode::PurificationSoudness => { + let dereferenced_places = expression.collect_guarded_dereferenced_places(); + let check = if dereferenced_places.is_empty() { + if is_unsafe { + Some(expression) + } else { + None + } + } else { + let check = construct_framing_assertion( + self.encoder, + dereferenced_places, + framing_arguments, + )?; + if is_unsafe { + Some(vir_high::Expression::and(expression, check)) + } else { + Some(check) + } + }; + Ok(check) + } + } + } +} + +fn construct_framing_assertion( + encoder: &mut Encoder, + dereferenced_places: Vec<(vir_high::Expression, vir_high::Expression)>, + framing_places: &[vir_high::Expression], +) -> SpannedEncodingResult { + let type_invariant_framing_places = + construct_type_invariant_framing_places(encoder, framing_places)?; + let mut type_invariant_framed_places = Vec::new(); + for (guard, place) in dereferenced_places { + if is_framed(&place, &type_invariant_framing_places) { + let function = vir_high::Expression::builtin_func_app( + vir_high::BuiltinFunc::EnsureOwnedPredicate, + Vec::new(), + vec![place.clone()], + vir_high::Type::Bool, + place.position(), + ); + let check = vir_high::Expression::implies(guard, function); + type_invariant_framed_places.push(check); + } else { + unimplemented!("Outdated code."); + // let span = encoder + // .error_manager() + // .position_manager() + // .get_span(place.position().into()) + // .cloned() + // .unwrap(); + // return Err(SpannedEncodingError::incorrect( + // "the place must be framed by permissions", + // span, + // )); + } + } + Ok(type_invariant_framed_places.into_iter().conjoin()) +} + +fn construct_type_invariant_framing_places( + encoder: &mut Encoder, + framing_places: &[vir_high::Expression], +) -> SpannedEncodingResult> { + let type_invariant_framing_places = Vec::new(); + for framing_place in framing_places { + if framing_place.get_type().is_struct() { + let type_decl = encoder + .encode_type_def_high(framing_place.get_type(), true)? + .unwrap_struct(); + if let Some(invariants) = type_decl.structural_invariant { + for expression in invariants { + let _expression = expression.replace_self(framing_place); + unimplemented!("Outdated code?"); + // type_invariant_framing_places.extend(expression.collect_owned_places()); + } + } + } + } + Ok(type_invariant_framing_places) +} + +fn is_framed( + place: &vir_high::Expression, + type_invariant_framing_places: &[vir_high::Expression], +) -> bool { + for framing_place in type_invariant_framing_places { + if is_framed_rec(framing_place, place, type_invariant_framing_places) { + return true; + } + } + false +} + +fn is_framed_rec( + framing_place: &vir_high::Expression, + place: &vir_high::Expression, + type_invariant_framing_places: &[vir_high::Expression], +) -> bool { + if framing_place == place { + if let Some(pointer_place) = place.get_last_dereferenced_pointer() { + is_framed(pointer_place, type_invariant_framing_places) + } else { + true + } + } else if place.is_deref() { + false + } else if let Some(parent) = place.get_parent_ref() { + is_framed_rec(framing_place, parent, type_invariant_framing_places) + } else { + true + } +} diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/elaborate_drops/mod.rs b/prusti-viper/src/encoder/mir/procedures/encoder/elaborate_drops/mod.rs index 0cf7b1bd989..15f6e679bf8 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/elaborate_drops/mod.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/elaborate_drops/mod.rs @@ -1,3 +1,4 @@ +use self::pointer_reborrow::add_pointer_reborrow_facts; use crate::encoder::{ errors::{SpannedEncodingError, SpannedEncodingResult}, Encoder, @@ -18,8 +19,9 @@ use prusti_rustc_interface::{hir::def_id::DefId, middle::mir}; mod mir_dataflow; pub(super) mod mir_transform; +mod pointer_reborrow; -pub(super) fn elaborate_drops<'v, 'tcx: 'v>( +pub(super) fn get_and_elaborate_mir<'v, 'tcx: 'v>( encoder: &mut Encoder<'v, 'tcx>, def_id: DefId, procedure: &Procedure<'tcx>, @@ -69,6 +71,28 @@ pub(super) fn elaborate_drops<'v, 'tcx: 'v>( validate(&input_facts, &location_table, &mir); + // When reborrowing a place whose last component is a raw pointer + // dereference, add a constraint that the lifetime for which the place is + // borrowed is shorter than the lifetime of the last reference. + + add_pointer_reborrow_facts( + encoder.env().tcx(), + &mut input_facts, + &mut location_table, + &mir, + )?; + + if config::dump_debug_info() { + let local_def_id = def_id.expect_local(); + let def_path = encoder.env().query.hir().def_path(local_def_id); + let graph = to_graphviz(&input_facts, &location_table, &mir); + prusti_common::report::log::report_with_writer( + "graphviz_mir_dump_after_pointer_reborrow_facts", + format!("{}.dot", def_path.to_filename_friendly_no_crate()), + |writer| graph.write(writer).unwrap(), + ); + } + let lifetimes = Lifetimes::new(input_facts, location_table); Ok((mir, lifetimes)) diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/elaborate_drops/pointer_reborrow.rs b/prusti-viper/src/encoder/mir/procedures/encoder/elaborate_drops/pointer_reborrow.rs new file mode 100644 index 00000000000..a3c20e59ba3 --- /dev/null +++ b/prusti-viper/src/encoder/mir/procedures/encoder/elaborate_drops/pointer_reborrow.rs @@ -0,0 +1,65 @@ +use crate::encoder::errors::SpannedEncodingResult; +use prusti_interface::environment::mir_body::borrowck::facts::{ + AllInputFacts, LocationTable, RichLocation, +}; +use prusti_rustc_interface::middle::{ + mir, + ty::{self, TyCtxt}, +}; + +pub(super) fn add_pointer_reborrow_facts<'v, 'tcx: 'v>( + tcx: TyCtxt<'tcx>, + borrowck_input_facts: &mut AllInputFacts, + location_table: &LocationTable, + body: &mir::Body<'tcx>, +) -> SpannedEncodingResult<()> { + for (block, data) in body.basic_blocks.iter_enumerated() { + for (statement_index, stmt) in data.statements.iter().enumerate() { + if let mir::StatementKind::Assign(box (_, source)) = &stmt.kind { + if let mir::Rvalue::Ref(reborrow_lifetime, _, place) = &source { + if let Some(reference_lifetime) = raw_pointer_reborrow(tcx, body, *place) { + let point = + location_table.location_to_point(RichLocation::Mid(mir::Location { + block, + statement_index, + })); + let ty::RegionKind::ReVar(reborrow_lifetime_id) = **reborrow_lifetime else { + unreachable!("reborrow_lifetime: {:?}", reborrow_lifetime); + }; + let ty::RegionKind::ReVar(reference_lifetime_id) = *reference_lifetime else { + unreachable!("reference_lifetime: {:?}", reference_lifetime); + }; + borrowck_input_facts.subset_base.push(( + reference_lifetime_id, + reborrow_lifetime_id, + point, + )); + } + } + } + } + } + + Ok(()) +} + +fn raw_pointer_reborrow<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, + place: mir::Place<'tcx>, +) -> Option> { + let mut projections = place.iter_projections().rev(); + while let Some((place, projection)) = projections.next() { + if projection == mir::ProjectionElem::Deref && place.ty(body, tcx).ty.is_unsafe_ptr() { + break; + } + } + while let Some((place, projection)) = projections.next() { + if projection == mir::ProjectionElem::Deref { + if let ty::TyKind::Ref(reference_region, _, _) = place.ty(body, tcx).ty.kind() { + return Some(*reference_region); + } + } + } + None +} diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/initialisation.rs b/prusti-viper/src/encoder/mir/procedures/encoder/initialisation.rs index 8cc9784515d..772c8a7ead9 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/initialisation.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/initialisation.rs @@ -1,6 +1,6 @@ use prusti_rustc_interface::{ dataflow::{ - impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces}, + impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces}, move_paths::MoveData, Analysis, MoveDataParamEnv, ResultsCursor, }, @@ -8,9 +8,12 @@ use prusti_rustc_interface::{ middle::{mir, ty::TyCtxt}, }; +// FIXME: Remove this file. It is not used anymore. pub(super) struct InitializationData<'mir, 'tcx> { + liveness: ResultsCursor<'mir, 'tcx, MaybeLiveLocals>, inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>, + move_env: &'mir MoveDataParamEnv<'tcx>, } impl<'mir, 'tcx> InitializationData<'mir, 'tcx> { @@ -19,30 +22,45 @@ impl<'mir, 'tcx> InitializationData<'mir, 'tcx> { body: &'mir mir::Body<'tcx>, move_env: &'mir MoveDataParamEnv<'tcx>, ) -> Self { - let dead_unwinds = - super::elaborate_drops::mir_transform::find_dead_unwinds(tcx, body, move_env); + let liveness = MaybeLiveLocals + .into_engine(tcx, body) + .pass_name("prusti_encoding") + .iterate_to_fixpoint() + .into_results_cursor(body); + // let dead_unwinds = + // super::elaborate_drops::mir_transform::find_dead_unwinds(tcx, body, move_env); let inits = MaybeInitializedPlaces::new(tcx, body, move_env) .into_engine(tcx, body) - .dead_unwinds(&dead_unwinds) - .pass_name("elaborate_drops") + .pass_name("prusti_encoding") .iterate_to_fixpoint() .into_results_cursor(body); let uninits = MaybeUninitializedPlaces::new(tcx, body, move_env) .mark_inactive_variants_as_uninit() .into_engine(tcx, body) - .dead_unwinds(&dead_unwinds) - .pass_name("elaborate_drops") + .pass_name("prusti_encoding") .iterate_to_fixpoint() .into_results_cursor(body); - Self { inits, uninits } + Self { + inits, + uninits, + liveness, + move_env, + } } pub(super) fn seek_before(&mut self, loc: mir::Location) { + self.liveness.seek_before_primary_effect(loc); self.inits.seek_before_primary_effect(loc); self.uninits.seek_before_primary_effect(loc); } + pub(super) fn is_local_initialized_and_alive(&self, local: mir::Local) -> bool { + let path = self.move_env.move_data.rev_lookup.find_local(local); + self.liveness.get().contains(local) + && self.inits.get().contains(path) + && self.uninits.get().contains(path) + } } pub(super) fn create_move_data_param_env<'tcx>( diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/lifetimes.rs b/prusti-viper/src/encoder/mir/procedures/encoder/lifetimes.rs index d989380f42d..559d799754c 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/lifetimes.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/lifetimes.rs @@ -1,3 +1,4 @@ +use super::ProcedureEncodingKind; use crate::encoder::{ errors::{ErrorCtxt, SpannedEncodingResult}, mir::{ @@ -10,7 +11,10 @@ use prusti_interface::environment::{ }; use prusti_rustc_interface::middle::mir; use std::collections::{BTreeMap, BTreeSet}; -use vir_crate::high::{self as vir_high, builders::procedure::BasicBlockBuilder}; +use vir_crate::high::{ + self as vir_high, + builders::procedure::{BasicBlockBuilder, StatementSequenceBuilder}, +}; pub(super) trait LifetimesEncoder<'tcx> { fn encode_lft_for_statement_start( @@ -33,6 +37,7 @@ pub(super) trait LifetimesEncoder<'tcx> { &mut self, statement: Option<&mir::Statement<'tcx>>, ) -> SpannedEncodingResult)>>; + fn get_last_reference_deref(&self, place: mir::Place<'tcx>) -> Option>; fn reborrow_operand_lifetime( &mut self, statement: Option<&mir::Statement<'tcx>>, @@ -66,7 +71,7 @@ pub(super) trait LifetimesEncoder<'tcx> { &mut self, old_original_lifetimes: &BTreeSet, new_derived_lifetimes: &BTreeMap>, - new_reborrow_lifetime_to_remove: String, + new_reborrow_lifetime_to_remove: &str, lifetimes_to_create: &BTreeSet, ); fn remove_reborrow_lifetimes_set(&mut self, set: &mut BTreeSet); @@ -141,6 +146,9 @@ pub(super) trait LifetimesEncoder<'tcx> { from: RichLocation, to: RichLocation, ) -> SpannedEncodingResult<()>; + fn encode_dead_references_for_parameters( + &mut self, + ) -> SpannedEncodingResult>; fn encode_lft_assert_subset( &mut self, block_builder: &mut BasicBlockBuilder, @@ -278,10 +286,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' if let (Some(target_lifetime), Some(value_lifetime)) = (target_lifetime, value_lifetime) { - let values = [operand_lifetime, target_lifetime] - .iter() - .cloned() - .collect(); + let values = [operand_lifetime, target_lifetime].into_iter().collect(); return Ok(Some((value_lifetime, values))); } } @@ -290,6 +295,18 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' Ok(None) } + fn get_last_reference_deref(&self, place: mir::Place<'tcx>) -> Option> { + super::utils::get_last_reference_deref(self.encoder.env().tcx(), self.mir, place) + // place + // .iter_projections() + // .filter(|(place, projection)| { + // projection == &mir::ProjectionElem::Deref + // && place.ty(self.mir, self.encoder.env().tcx()).ty.is_ref() + // }) + // .last() + // .map(|(place, _)| place) + } + fn reborrow_operand_lifetime( &mut self, statement: Option<&mir::Statement<'tcx>>, @@ -300,12 +317,17 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' mir::Rvalue::Ref(region, _borrow_kind, place), )) = &statement.kind { - let region_name: String = region.to_text(); - if let Some((_ref, projection)) = place.iter_projections().last() { - if projection == mir::ProjectionElem::Deref { - return Some(region_name); - } + let is_reborrow = self.get_last_reference_deref(*place).is_some(); + if is_reborrow { + let region_name: String = region.to_text(); + return Some(region_name); } + // let region_name: String = region.to_text(); + // if let Some((_ref, projection)) = place.iter_projections().last() { + // if projection == mir::ProjectionElem::Deref { + // return Some(region_name); + // } + // } } } None @@ -322,6 +344,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' self.lifetimes.get_origin_contains_loan_at_mid(location); let mut current_original_lifetimes = self.lifetimes.get_loan_live_at_start(location); block_builder.add_comment(format!("Prepare lifetimes for block {target:?}")); + self.encode_lifetimes_dead_on_edge( + block_builder, + RichLocation::Mid(location), + RichLocation::Start(mir::Location { + block: target, + statement_index: 0, + }), + )?; self.encode_lft( block_builder, location, @@ -332,18 +362,16 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' None, None, )?; - self.reborrow_lifetimes_to_remove_for_block - .entry(target) - .or_insert_with(BTreeSet::new); let mut values = self .reborrow_lifetimes_to_remove_for_block .get(&self.current_basic_block.unwrap()) .unwrap() .clone(); - self.reborrow_lifetimes_to_remove_for_block - .get_mut(&target) - .unwrap() - .append(&mut values); + let target_entry = self + .reborrow_lifetimes_to_remove_for_block + .entry(target) + .or_insert_with(BTreeSet::new); + target_entry.append(&mut values); Ok(()) } @@ -362,6 +390,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' let mut intermediate_block_builder = block_builder.create_basic_block_builder(fresh_destination_label.clone()); intermediate_block_builder.add_comment(format!("Prepare lifetimes for block {target:?}")); + self.encode_lifetimes_dead_on_edge( + &mut intermediate_block_builder, + RichLocation::Mid(location), + RichLocation::Start(mir::Location { + block: target, + statement_index: 0, + }), + )?; self.encode_lft( &mut intermediate_block_builder, location, @@ -409,7 +445,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' let mut lifetimes_to_create = self.lifetimes_to_create(old_original_lifetimes, &new_original_lifetimes); let mut lifetime_backups: BTreeMap = BTreeMap::new(); - if let Some(new_reborrow_lifetime_to_remove) = new_reborrow_lifetime_to_remove { + if let Some(new_reborrow_lifetime_to_remove) = &new_reborrow_lifetime_to_remove { self.update_lifetimes_to_remove( old_original_lifetimes, new_derived_lifetimes, @@ -465,26 +501,24 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' &mut self, old_original_lifetimes: &BTreeSet, new_derived_lifetimes: &BTreeMap>, - lifetime_to_ignore: String, + lifetime_to_ignore: &str, lifetimes_to_create: &BTreeSet, ) { let mut new_lifetimes_to_ignore: BTreeSet = BTreeSet::new(); - for (lifetime, derived_from) in new_derived_lifetimes.clone() { + for (lifetime, derived_from) in new_derived_lifetimes { // NOTE: if the lifetime is not derived from at least one already existing // original lifetime, we can not delete the lifetimes it is derived from. - let can_remove_lifetimes = !derived_from + let can_remove_lifetimes = derived_from .iter() - .filter(|&x| old_original_lifetimes.contains(x)) - .cloned() - .collect::>() - .is_empty(); + .any(|x| old_original_lifetimes.contains(x)); if lifetime == lifetime_to_ignore && can_remove_lifetimes { - new_lifetimes_to_ignore = derived_from - .clone() - .iter() - .filter(|x| lifetimes_to_create.contains(*x)) - .cloned() - .collect(); + assert!(new_lifetimes_to_ignore.is_empty()); + new_lifetimes_to_ignore.extend( + derived_from + .iter() + .filter(|x| lifetimes_to_create.contains(*x)) + .cloned(), + ); } } self.reborrow_lifetimes_to_remove_for_block @@ -494,40 +528,56 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' } fn remove_reborrow_lifetimes_set(&mut self, set: &mut BTreeSet) { - *set = set - .clone() - .iter() - .filter(|&lft| { + set.retain(|lft| { + !self + .reborrow_lifetimes_to_remove_for_block + .get(&self.current_basic_block.unwrap()) + .unwrap() + .contains(lft) + }); + // *set = set + // .clone() + // .iter() + // .filter(|&lft| { + // !self + // .reborrow_lifetimes_to_remove_for_block + // .get(&self.current_basic_block.unwrap()) + // .unwrap() + // .contains(lft) + // }) + // .cloned() + // .collect(); + } + + fn remove_reborrow_lifetimes_map(&mut self, map: &mut BTreeMap>) { + for (lifetime, derived_from) in map { + derived_from.retain(|lft| { !self .reborrow_lifetimes_to_remove_for_block .get(&self.current_basic_block.unwrap()) .unwrap() .contains(lft) - }) - .cloned() - .collect(); - } - - fn remove_reborrow_lifetimes_map(&mut self, map: &mut BTreeMap>) { - *map = map - .clone() - .iter() - .map(|(lifetime, derived_from)| { - let updated_derived_from: BTreeSet = derived_from - .clone() - .iter() - .filter(|&lft| { - !self - .reborrow_lifetimes_to_remove_for_block - .get(&self.current_basic_block.unwrap()) - .unwrap() - .contains(lft) - }) - .cloned() - .collect(); - (lifetime.clone(), updated_derived_from) - }) - .collect(); + }); + } + // *map = map + // .clone() + // .iter() + // .map(|(lifetime, derived_from)| { + // let updated_derived_from: BTreeSet = derived_from + // .clone() + // .iter() + // .filter(|&lft| { + // !self + // .reborrow_lifetimes_to_remove_for_block + // .get(&self.current_basic_block.unwrap()) + // .unwrap() + // .contains(lft) + // }) + // .cloned() + // .collect(); + // (lifetime.clone(), updated_derived_from) + // }) + // .collect(); } fn encode_bor_shorten( @@ -788,6 +838,34 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' Ok(()) } + fn encode_dead_references_for_parameters( + &mut self, + ) -> SpannedEncodingResult> { + // FIXME: This is apparently not needed because we generate the + // necessary dead-lifetime statement even for function parameters. + let statements = Vec::new(); + // if self.encoding_kind == ProcedureEncodingKind::PostconditionFrameCheck { + // return Ok(statements); + // } + // for mir_arg in self.mir.args_iter() { + // let parameter = self.encode_local(mir_arg)?; + // if let vir_high::Type::Reference(reference_type) = ¶meter.variable.ty { + // let position = parameter.position; + // let target_type = (*reference_type.target_type).clone(); + // let target = vir_high::Expression::deref_no_pos(parameter.into(), target_type); + // let statement = vir_high::Statement::dead_reference_no_pos(target); + // statements.add_statement( + // self.encoder.set_surrounding_error_context_for_statement( + // statement, + // position, + // ErrorCtxt::LifetimeEncoding, + // )?, + // ); + // } + // } + Ok(statements) + } + fn encode_lft_assert_subset( &mut self, block_builder: &mut BasicBlockBuilder, @@ -1100,10 +1178,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' permission_amount: vir_high::Expression, ) -> SpannedEncodingResult { self.encoder.set_statement_error_ctxt( - vir_high::Statement::inhale_no_pos(vir_high::Predicate::lifetime_token_no_pos( - lifetime_const, - permission_amount, - )), + vir_high::Statement::inhale_predicate_no_pos( + vir_high::Predicate::lifetime_token_no_pos(lifetime_const, permission_amount), + ), self.mir.span, ErrorCtxt::LifetimeInhale, self.def_id, @@ -1136,10 +1213,9 @@ impl<'p, 'v: 'p, 'tcx: 'v> LifetimesEncoder<'tcx> for ProcedureEncoder<'p, 'v, ' permission_amount: vir_high::Expression, ) -> SpannedEncodingResult { self.encoder.set_statement_error_ctxt( - vir_high::Statement::exhale_no_pos(vir_high::Predicate::lifetime_token_no_pos( - lifetime_const, - permission_amount, - )), + vir_high::Statement::exhale_predicate_no_pos( + vir_high::Predicate::lifetime_token_no_pos(lifetime_const, permission_amount), + ), self.mir.span, ErrorCtxt::LifetimeExhale, self.def_id, diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/loops.rs b/prusti-viper/src/encoder/mir/procedures/encoder/loops.rs index 7af06a785b5..33dd6b65ee5 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/loops.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/loops.rs @@ -51,6 +51,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> super::ProcedureEncoder<'p, 'v, 'tcx> { block, self.def_id, cl_substs, + false, )?, span, err_ctxt, @@ -69,7 +70,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> super::ProcedureEncoder<'p, 'v, 'tcx> { .map(|back_edge| self.encode_basic_block_label(*back_edge)) .collect() }; - self.init_data.seek_before(invariant_location); + // self.init_data.seek_before(invariant_location); // Encode permissions. let initialized_places = self.initialization.get_after_statement(invariant_location); diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/mod.rs b/prusti-viper/src/encoder/mir/procedures/encoder/mod.rs index 289010692c4..3951aacc8d8 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/mod.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/mod.rs @@ -18,20 +18,24 @@ use crate::encoder::{ spans::SpanInterface, specifications::SpecificationsInterface, type_layouts::MirTypeLayoutsEncoderInterface, + types::MirTypeEncoderInterface, }, mir_encoder::PRECONDITION_LABEL, Encoder, }; -use log::debug; +use log::{debug, trace}; use prusti_common::config; -use prusti_interface::environment::{ - debug_utils::to_text::ToText, - mir_analyses::{ - allocation::{compute_definitely_allocated, DefinitelyAllocatedAnalysisResult}, - initialization::{compute_definitely_initialized, DefinitelyInitializedAnalysisResult}, +use prusti_interface::{ + environment::{ + debug_utils::to_text::ToText, + mir_analyses::{ + allocation::{compute_definitely_allocated, DefinitelyAllocatedAnalysisResult}, + initialization::{compute_definitely_initialized, DefinitelyInitializedAnalysisResult}, + }, + mir_body::borrowck::{facts::RichLocation, lifetimes::Lifetimes}, + Procedure, }, - mir_body::borrowck::{facts::RichLocation, lifetimes::Lifetimes}, - Procedure, + specs::typed::Pledge, }; use prusti_rustc_interface::{ data_structures::graph::WithStartNode, @@ -44,19 +48,21 @@ use std::collections::{BTreeMap, BTreeSet}; use vir_crate::{ common::{ check_mode::CheckMode, - expression::{BinaryOperationHelpers, UnaryOperationHelpers}, + expression::{BinaryOperationHelpers, ExpressionIterator, UnaryOperationHelpers}, position::Positioned, }, high::{ self as vir_high, builders::procedure::{ - BasicBlockBuilder, ProcedureBuilder, SuccessorBuilder, SuccessorExitKind, + BasicBlockBuilder, ProcedureBuilder, StatementSequenceBuilder, SuccessorBuilder, + SuccessorExitKind, }, operations::{lifetimes::WithLifetimes, ty::Typed}, }, }; mod builtin_function_encoder; +mod check_mode_converters; mod elaborate_drops; mod ghost; mod initialisation; @@ -65,21 +71,32 @@ mod loops; mod scc; pub mod specification_blocks; mod termination; +mod specifications; +mod utils; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(super) enum ProcedureEncodingKind { + Regular, + PostconditionFrameCheck, +} pub(super) fn encode_procedure<'v, 'tcx: 'v>( encoder: &mut Encoder<'v, 'tcx>, def_id: DefId, check_mode: CheckMode, + encoding_kind: ProcedureEncodingKind, ) -> SpannedEncodingResult { let procedure = Procedure::new(encoder.env(), def_id); let tcx = encoder.env().tcx(); - let (mir, lifetimes) = self::elaborate_drops::elaborate_drops(encoder, def_id, &procedure)?; + let (mir, lifetimes) = + self::elaborate_drops::get_and_elaborate_mir(encoder, def_id, &procedure)?; let mir = &mir; // Mark body as immutable. + let is_unsafe_function = encoder.env().query.is_unsafe_function(def_id); let move_env = self::initialisation::create_move_data_param_env(tcx, mir, def_id); let init_data = InitializationData::new(tcx, mir, &move_env); let locals_without_explicit_allocation: BTreeSet<_> = mir.vars_and_temps_iter().collect(); let specification_blocks = - SpecificationBlocks::build(encoder.env().query, mir, &procedure, true); + SpecificationBlocks::build(encoder.env().query, mir, Some(&procedure), true); let initialization = compute_definitely_initialized(def_id, mir, encoder.env().tcx()); let allocation = compute_definitely_allocated(def_id, mir); let lifetime_count = lifetimes.lifetime_count(); @@ -95,6 +112,7 @@ pub(super) fn encode_procedure<'v, 'tcx: 'v>( encoder, def_id, check_mode, + is_unsafe_function, procedure: &procedure, mir, init_data, @@ -105,7 +123,7 @@ pub(super) fn encode_procedure<'v, 'tcx: 'v>( specification_blocks, specification_block_encoding: Default::default(), loop_invariant_encoding: Default::default(), - check_panics: config::check_panics() && check_mode != CheckMode::CoreProof, + check_panics: config::check_panics() && check_mode.check_specifications(), locals_without_explicit_allocation, used_locals: Default::default(), fresh_id_generator: 0, @@ -118,6 +136,13 @@ pub(super) fn encode_procedure<'v, 'tcx: 'v>( reborrow_lifetimes_to_remove_for_block, current_basic_block, termination_variable: None, + encoding_kind, + opened_reference_place_permissions: Default::default(), + opened_reference_witnesses: Default::default(), + user_named_lifetimes: Default::default(), + manually_managed_places: Default::default(), + stashed_ranges: Default::default(), + specification_expressions: Default::default(), }; procedure_encoder.encode() } @@ -126,6 +151,8 @@ struct ProcedureEncoder<'p, 'v: 'p, 'tcx: 'v> { encoder: &'p mut Encoder<'v, 'tcx>, def_id: DefId, check_mode: CheckMode, + encoding_kind: ProcedureEncodingKind, + is_unsafe_function: bool, procedure: &'p Procedure<'tcx>, mir: &'p mir::Body<'tcx>, init_data: InitializationData<'p, 'tcx>, @@ -158,30 +185,61 @@ struct ProcedureEncoder<'p, 'v: 'p, 'tcx: 'v> { reborrow_lifetimes_to_remove_for_block: BTreeMap>, current_basic_block: Option, termination_variable: Option, + /// A map from opened reference place to the corresponding permission + /// variable. + opened_reference_place_permissions: + BTreeMap>, + /// A map from opened reference witnesses to the corresponding places and lifetimes. + opened_reference_witnesses: + BTreeMap, + /// The lifetimes extracted by the user by using `take_lifetime!` macro. + user_named_lifetimes: BTreeMap, + /// Places that are manually managed by the user and for which we should not + /// automatically generate open/close/fold/unfold statements. + manually_managed_places: BTreeSet, + /// Information about stashed ranges with a given name: `(pointer, + /// start_index, end_index)`. + stashed_ranges: BTreeMap< + String, + ( + vir_high::Expression, + vir_high::Expression, + vir_high::Expression, + ), + >, + /// The encoded Prusti specification expressions used in specification + /// blocks. + /// + /// Specification ID → expresssion. + specification_expressions: BTreeMap, } impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { fn encode(&mut self) -> SpannedEncodingResult { self.pure_sanity_checks()?; let name = format!( - "{}${}", + "{}${}${:?}", self.encoder.encode_item_name(self.def_id), - self.check_mode + self.check_mode, + self.encoding_kind, ); let (allocate_parameters, deallocate_parameters) = self.encode_parameters()?; let (allocate_returns, deallocate_returns) = self.encode_returns()?; self.lifetime_token_permission = Some(self.fresh_ghost_variable("lifetime_token_perm_amount", vir_high::Type::MPerm)); - let (assume_preconditions, assert_postconditions) = match self.check_mode { - CheckMode::CoreProof => { - // Unsafe functions will come with CheckMode::Both because they - // are allowed to have preconditions. - (Vec::new(), Vec::new()) - } - CheckMode::Both | CheckMode::Specifications => { - self.encode_functional_specifications()? - } - }; + let (assume_preconditions, assert_postconditions) = + self.encode_functional_specifications()?; + let dead_references = self.encode_dead_references_for_parameters()?; + // match self.check_mode { + // CheckMode::CoreProof => { + // // Unsafe functions will come with CheckMode::Both because they + // // are allowed to have preconditions. + // (Vec::new(), Vec::new()) + // } + // CheckMode::Both | CheckMode::Specifications => { + // self.encode_functional_specifications()? + // } + // }; let (assume_lifetime_preconditions, assert_lifetime_postconditions) = self.encode_lifetime_specifications()?; let termination_initialization = self.encode_termination_initialization()?; @@ -197,13 +255,35 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { pre_statements.push(old_label); pre_statements.extend(termination_initialization); pre_statements.extend(allocate_returns); - let mut post_statements = assert_postconditions; + let mut post_statements = dead_references; + post_statements.extend(assert_postconditions); post_statements.extend(deallocate_parameters); post_statements.extend(deallocate_returns); post_statements.extend(assert_lifetime_postconditions); - let mut procedure_builder = - ProcedureBuilder::new(name, self.check_mode, pre_statements, post_statements); - self.encode_body(&mut procedure_builder)?; + let procedure_position = + self.encoder + .register_error(self.mir.span, ErrorCtxt::Unexpected, self.def_id); + let mut procedure_builder = ProcedureBuilder::new( + name, + self.check_mode, + procedure_position, + pre_statements, + post_statements, + ); + for local_index in 0..self.mir.local_decls.len() { + // We do not use `encode_local` to avoid marking the variable as + // used. + let variable = self + .encoder + .encode_local_high(self.mir, local_index.into())?; + procedure_builder.add_non_aliased_place(variable.into()); + } + match self.encoding_kind { + ProcedureEncodingKind::Regular => self.encode_body(&mut procedure_builder)?, + ProcedureEncodingKind::PostconditionFrameCheck => { + self.encode_postcondition_frame_check(&mut procedure_builder)?; + } + } self.encode_implicit_allocations(&mut procedure_builder)?; Ok(procedure_builder.build()) } @@ -256,7 +336,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { )]; for mir_arg in self.mir.args_iter() { let parameter = self.encode_local(mir_arg)?; - let alloc_statement = vir_high::Statement::inhale_no_pos( + let alloc_statement = vir_high::Statement::inhale_predicate_no_pos( vir_high::Predicate::owned_non_aliased_no_pos(parameter.clone().into()), ); allocation.push(self.encoder.set_surrounding_error_context_for_statement( @@ -266,7 +346,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { )?); let mir_type = self.encoder.get_local_type(self.mir, mir_arg)?; let size = self.encoder.encode_type_size_expression(mir_type)?; - let dealloc_statement = vir_high::Statement::exhale_no_pos( + let dealloc_statement = vir_high::Statement::exhale_predicate_no_pos( vir_high::Predicate::memory_block_stack_no_pos(parameter.clone().into(), size), ); deallocation.push(self.encoder.set_surrounding_error_context_for_statement( @@ -285,17 +365,16 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let mir_type = self.encoder.get_local_type(self.mir, mir::RETURN_PLACE)?; let size = self.encoder.encode_type_size_expression(mir_type)?; let alloc_statement = self.encoder.set_surrounding_error_context_for_statement( - vir_high::Statement::inhale_no_pos(vir_high::Predicate::memory_block_stack_no_pos( - return_local.clone().into(), - size, - )), + vir_high::Statement::inhale_predicate_no_pos( + vir_high::Predicate::memory_block_stack_no_pos(return_local.clone().into(), size), + ), return_local.position, ErrorCtxt::UnexpectedStorageLive, )?; let dealloc_statement = self.encoder.set_surrounding_error_context_for_statement( - vir_high::Statement::exhale_no_pos(vir_high::Predicate::owned_non_aliased_no_pos( - return_local.clone().into(), - )), + vir_high::Statement::exhale_predicate_no_pos( + vir_high::Predicate::owned_non_aliased_no_pos(return_local.clone().into()), + ), return_local.position, ErrorCtxt::UnexpectedStorageDead, )?; @@ -365,6 +444,46 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { self.def_id, assertion_substs, )?; + let expression = + self.desugar_pledges_in_postcondition(precondition_label, result, expression)?; + postconditions.push(expression); + } + let pledges = procedure_contract.pledges(); + for Pledge { + reference, + lhs: body_lhs, + rhs: body_rhs, + } in pledges + { + trace!( + "pledge reference={:?} lhs={:?} rhs={:?}", + reference, + body_lhs, + body_rhs + ); + assert!( + reference.is_none(), + "The reference should be none in postcondition." + ); + assert!(body_lhs.is_none(), "assert on expiry is not supported yet."); + let assertion_rhs = self.encoder.encode_assertion_high( + *body_rhs, + Some(precondition_label), + &arguments_in_old, + Some(result), + self.def_id, + call_substs, + )?; + let position = assertion_rhs.position(); + let expression = vir_high::Expression::builtin_func_app( + vir_high::BuiltinFunc::AfterExpiry, + Vec::new(), + vec![assertion_rhs], + vir_high::Type::Bool, + position, + ); + let expression = + self.desugar_pledges_in_postcondition(precondition_label, result, expression)?; postconditions.push(expression); } Ok(postconditions) @@ -383,25 +502,55 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let mut preconditions = vec![vir_high::Statement::comment( "Assume functional preconditions.".to_string(), )]; + let mut precondition_conjuncts = Vec::new(); let mut arguments: Vec = Vec::new(); + let mut framing_variables = Vec::new(); for local in self.mir.args_iter() { - arguments.push(self.encode_local(local)?.into()); + let parameter = self.encode_local(local)?; + framing_variables.push(parameter.variable.clone()); + arguments.push(parameter.into()); } for expression in self.encode_precondition_expressions(&procedure_contract, substs, &arguments)? { - let assume_statement = self.encoder.set_statement_error_ctxt( - vir_high::Statement::assume_no_pos(expression), - mir_span, - ErrorCtxt::UnexpectedAssumeMethodPrecondition, - self.def_id, - )?; - preconditions.push(assume_statement); + if let Some(expression) = self.convert_expression_to_check_mode( + expression, + !self.is_unsafe_function, + &framing_variables, + )? { + let expression_with_pos = self.encoder.set_expression_error_ctxt( + expression, + mir_span, + ErrorCtxt::UnexpectedAssumeMethodPrecondition, + self.def_id, + ); + // let inhale_statement = self.encoder.set_statement_error_ctxt( + // vir_high::Statement::inhale_expression_no_pos(expression), + // mir_span, + // ErrorCtxt::UnexpectedAssumeMethodPrecondition, + // self.def_id, + // )?; + precondition_conjuncts.push(expression_with_pos); + } } + let inhale_statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::inhale_expression_no_pos( + precondition_conjuncts.into_iter().conjoin(), + Some(PRECONDITION_LABEL.to_string()), + ), + mir_span, + ErrorCtxt::UnexpectedAssumeMethodPrecondition, + self.def_id, + )?; + preconditions.push(inhale_statement); + let mut postconditions = vec![vir_high::Statement::comment( "Assert functional postconditions.".to_string(), )]; - let result: vir_high::Expression = self.encode_local(mir::RETURN_PLACE)?.into(); + let mut postcondition_conjuncts = Vec::new(); + let result_variable = self.encode_local(mir::RETURN_PLACE)?; + framing_variables.push(result_variable.variable.clone()); + let result: vir_high::Expression = result_variable.into(); for expression in self.encode_postcondition_expressions( &procedure_contract, substs, @@ -409,14 +558,37 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { &result, PRECONDITION_LABEL, )? { - let assert_statement = self.encoder.set_statement_error_ctxt( - vir_high::Statement::assert_no_pos(expression), - mir_span, - ErrorCtxt::AssertMethodPostcondition, - self.def_id, - )?; - postconditions.push(assert_statement); + if let Some(expression) = self.convert_expression_to_check_mode( + expression, + !self.is_unsafe_function, + &framing_variables, + )? { + let expression_with_pos = self.encoder.set_expression_error_ctxt( + expression, + mir_span, + ErrorCtxt::AssertMethodPostcondition, + self.def_id, + ); + // let exhale_statement = self.encoder.set_statement_error_ctxt( + // vir_high::Statement::exhale_expression_no_pos(expression), + // mir_span, + // ErrorCtxt::AssertMethodPostcondition, + // self.def_id, + // )?; + // postconditions.push(exhale_statement); + postcondition_conjuncts.push(expression_with_pos); + } } + let exhale_statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::exhale_expression_no_pos( + postcondition_conjuncts.into_iter().conjoin(), + None, + ), + mir_span, + ErrorCtxt::AssertMethodPostcondition, + self.def_id, + )?; + postconditions.push(exhale_statement); Ok((preconditions, postconditions)) } @@ -438,14 +610,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ); procedure_builder.add_alloc_statement( self.encoder.set_surrounding_error_context_for_statement( - vir_high::Statement::inhale_no_pos(predicate.clone()), + vir_high::Statement::inhale_predicate_no_pos(predicate.clone()), encoded_local.position, ErrorCtxt::UnexpectedStorageLive, )?, ); procedure_builder.add_dealloc_statement( self.encoder.set_surrounding_error_context_for_statement( - vir_high::Statement::exhale_no_pos(predicate.clone()), + vir_high::Statement::exhale_predicate_no_pos(predicate.clone()), encoded_local.position, ErrorCtxt::UnexpectedStorageLive, )?, @@ -490,6 +662,179 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { Ok(()) } + fn encode_postcondition_frame_check( + &mut self, + procedure_builder: &mut ProcedureBuilder, + ) -> SpannedEncodingResult<()> { + // FIXME: code duplication with encode_function_call. + let entry_label = vir_high::BasicBlockId::new("label_entry".to_string()); + let mut block_builder = procedure_builder.create_basic_block_builder(entry_label.clone()); + block_builder.set_successor_exit(SuccessorExitKind::Return); + let location = mir::Location { + block: 0usize.into(), + statement_index: 0, + }; + let span = self.mir.span; + let called_def_id = self.def_id; + let call_substs = self.encoder.env().query.identity_substs(called_def_id); + let args: Vec<_> = self + .mir + .args_iter() + .map(|arg| mir::Operand::Move(arg.into())) + .collect(); + let target_place_local = mir::RETURN_PLACE; + let destination: mir::Place = target_place_local.into(); + // let target = Some(1usize.into()); + // let cleanup = Some(1usize.into()); + + let is_unsafe = self.encoder.env().query.is_unsafe_function(called_def_id); + + // self.encode_function_call(&mut block_builder, location, span, called_def_id, call_substs, &args, destination, &target, &cleanup)?; + + let old_label = self.fresh_old_label(); + block_builder.add_statement(self.encoder.set_statement_error_ctxt( + vir_high::Statement::old_label_no_pos(old_label.clone()), + span, + ErrorCtxt::ProcedureCall, + self.def_id, + )?); + + let mut arguments = Vec::new(); + for arg in &args { + arguments.push( + self.encoder + .encode_operand_high(self.mir, arg, span) + .with_span(span)?, + ); + let encoded_arg = + self.encode_statement_operand_no_refs(&mut block_builder, location, arg)?; + let statement = vir_high::Statement::consume_no_pos(encoded_arg); + block_builder.add_statement(self.encoder.set_statement_error_ctxt( + statement, + span, + ErrorCtxt::ProcedureCall, + self.def_id, + )?); + } + + let procedure_contract = self + .encoder + .get_mir_procedure_contract_for_call(self.def_id, called_def_id, call_substs) + .with_span(span)?; + + let precondition_expressions = + self.encode_precondition_expressions(&procedure_contract, call_substs, &arguments)?; + let mut precondition_conjuncts = Vec::new(); + for expression in precondition_expressions { + if let Some(expression) = + self.convert_expression_to_check_mode_call_site(expression, is_unsafe, &arguments)? + { + // let exhale_statement = self.encoder.set_statement_error_ctxt( + // vir_high::Statement::exhale_expression_no_pos(expression), + // span, + // ErrorCtxt::ExhaleMethodPrecondition, + // self.def_id, + // )?; + // block_builder.add_statement(exhale_statement); + let conjunct = self.encoder.set_expression_error_ctxt( + expression, + span, + ErrorCtxt::ExhaleMethodPrecondition, + self.def_id, + ); + precondition_conjuncts.push(conjunct); + } + } + let exhale_statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::exhale_expression_no_pos( + precondition_conjuncts.into_iter().conjoin(), + Some(old_label.clone()), + ), + span, + ErrorCtxt::ExhaleMethodPrecondition, + self.def_id, + )?; + block_builder.add_statement(exhale_statement); + + let position = self.register_error(location, ErrorCtxt::ProcedureCall); + let encoded_target_place = self + .encode_place(destination, None)? + .set_default_position(position); + let postcondition_expressions = self.encode_postcondition_expressions( + &procedure_contract, + call_substs, + arguments.clone(), + &encoded_target_place, + &old_label, + )?; + let size = self.encoder.encode_type_size_expression( + self.encoder.get_local_type(self.mir, target_place_local)?, + )?; + let target_memory_block = + vir_high::Predicate::memory_block_stack_no_pos(encoded_target_place.clone(), size); + block_builder.add_statement(self.encoder.set_statement_error_ctxt( + vir_high::Statement::exhale_predicate_no_pos(target_memory_block), + span, + ErrorCtxt::ProcedureCall, + self.def_id, + )?); + let statement = vir_high::Statement::inhale_predicate_no_pos( + vir_high::Predicate::owned_non_aliased_no_pos(encoded_target_place.clone()), + ); + block_builder.add_statement(self.encoder.set_statement_error_ctxt( + statement, + span, + ErrorCtxt::ProcedureCall, + self.def_id, + )?); + let result_place = vec![encoded_target_place.clone()]; + let mut postcondition_conjuncts = Vec::new(); + for expression in postcondition_expressions { + if let Some(expression) = self.convert_expression_to_check_mode_call_site( + expression, + is_unsafe, + &result_place, + )? { + // let inhale_statement = self.encoder.set_statement_error_ctxt( + // vir_high::Statement::inhale_expression_no_pos(expression), + // span, + // ErrorCtxt::MethodPostconditionFraming, + // self.def_id, + // )?; + // block_builder.add_statement(inhale_statement); + let conjunct = self.encoder.set_expression_error_ctxt( + expression, + span, + ErrorCtxt::MethodPostconditionFraming, + self.def_id, + ); + postcondition_conjuncts.push(conjunct); + } + } + let inhale_statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::inhale_expression_no_pos( + postcondition_conjuncts.into_iter().conjoin(), + None, + ), + span, + ErrorCtxt::MethodPostconditionFraming, + self.def_id, + )?; + block_builder.add_statement(inhale_statement); + + let assume_statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::assume_no_pos(false.into()), + span, + ErrorCtxt::UnexpectedAssumeEndMethodPostconditionFraming, + self.def_id, + )?; + block_builder.add_statement(assume_statement); + + block_builder.build(); + procedure_builder.set_entry(entry_label); + Ok(()) + } + fn encode_basic_block( &mut self, procedure_builder: &mut ProcedureBuilder, @@ -598,7 +943,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { block_builder.add_statement(self.set_statement_error( location, ErrorCtxt::UnexpectedStorageLive, - vir_high::Statement::inhale_no_pos(memory_block), + vir_high::Statement::inhale_predicate_no_pos(memory_block), )?); let memory_block_drop = self .encoder @@ -606,7 +951,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { block_builder.add_statement(self.set_statement_error( location, ErrorCtxt::UnexpectedStorageLive, - vir_high::Statement::inhale_no_pos(memory_block_drop), + vir_high::Statement::inhale_predicate_no_pos(memory_block_drop), )?); } mir::StatementKind::StorageDead(local) => { @@ -617,7 +962,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { block_builder.add_statement(self.set_statement_error( location, ErrorCtxt::UnexpectedStorageDead, - vir_high::Statement::exhale_no_pos(memory_block), + vir_high::Statement::exhale_predicate_no_pos(memory_block), )?); let memory_block_drop = self .encoder @@ -625,7 +970,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { block_builder.add_statement(self.set_statement_error( location, ErrorCtxt::UnexpectedStorageDead, - vir_high::Statement::exhale_no_pos(memory_block_drop), + vir_high::Statement::exhale_predicate_no_pos(memory_block_drop), )?); } mir::StatementKind::Assign(box (target, source)) => { @@ -655,7 +1000,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { self.encode_assign_operand(block_builder, location, encoded_target, operand)?; } mir::Rvalue::Repeat(operand, count) => { - let encoded_operand = self.encode_statement_operand(location, operand)?; + let encoded_operand = + self.encode_statement_operand_no_refs(block_builder, location, operand)?; let encoded_count = self.encoder.compute_array_len(*count).with_span(span)?; let encoded_rvalue = vir_high::Rvalue::repeat(encoded_operand, encoded_count); let assign_statement = vir_high::Statement::assign( @@ -670,10 +1016,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { )?); } mir::Rvalue::Ref(region, borrow_kind, place) => { - let is_reborrow = place - .iter_projections() - .filter(|(_ref, projection)| projection == &mir::ProjectionElem::Deref) - .last(); + // let is_reborrow = place + // .iter_projections() + // .filter(|(place, projection)| { + // projection == &mir::ProjectionElem::Deref + // && place.ty(self.mir, self.encoder.env().tcx()).ty.is_ref() + // }) + // .last(); + let is_reborrow = self.get_last_reference_deref(*place); let uniquness = match borrow_kind { mir::BorrowKind::Mut { .. } => vir_high::ty::Uniqueness::Unique, _ => vir_high::ty::Uniqueness::Shared, @@ -682,7 +1032,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let region_name = region.to_text(); let new_borrow_lifetime = vir_high::ty::LifetimeConst { name: region_name }; - let encoded_rvalue = if let Some((place, _)) = is_reborrow { + let encoded_rvalue = if let Some(place) = is_reborrow { let reference_type = place.ty(self.mir, self.encoder.env().tcx()); let deref_lifetime = match reference_type.ty.kind() { ty::TyKind::Ref(region, _, _) => vir_high::ty::LifetimeConst { @@ -738,10 +1088,26 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { vir_high::Statement::assign_no_pos(encoded_target, encoded_rvalue), )?); } - // mir::Rvalue::Cast(CastKind, Operand<'tcx>, Ty<'tcx>), + mir::Rvalue::Cast(_kind, operand, ty) => { + let encoded_operand = + self.encode_statement_operand_no_refs(block_builder, location, operand)?; + let ty = self.encoder.encode_type_high(*ty)?; + let encoded_rvalue = vir_high::Rvalue::cast(encoded_operand, ty); + block_builder.add_statement(self.set_statement_error( + location, + ErrorCtxt::Assign, + vir_high::Statement::assign_no_pos(encoded_target, encoded_rvalue), + )?); + // self.encode_assign_cast(block_builder, location, encoded_target, *kind, operand, *ty)?; + // TODO: For raw pointers do nothing because we care only about + // the type of the target. + // unimplemented!("kind={kind:?} operand={operand:?} ty={ty:?}"); + } mir::Rvalue::BinaryOp(op, box (left, right)) => { - let encoded_left = self.encode_statement_operand(location, left)?; - let encoded_right = self.encode_statement_operand(location, right)?; + let (encoded_left, left_post_statements) = + self.encode_statement_operand(block_builder, location, left)?; + let (encoded_right, right_post_statements) = + self.encode_statement_operand(block_builder, location, right)?; let kind = self.encode_binary_op_kind(*op, encoded_target.get_type())?; let encoded_rvalue = vir_high::Rvalue::binary_op(kind, encoded_left, encoded_right); block_builder.add_statement(self.set_statement_error( @@ -749,10 +1115,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ErrorCtxt::Assign, vir_high::Statement::assign_no_pos(encoded_target, encoded_rvalue), )?); + block_builder.add_statements(left_post_statements); + block_builder.add_statements(right_post_statements); } mir::Rvalue::CheckedBinaryOp(op, box (left, right)) => { - let encoded_left = self.encode_statement_operand(location, left)?; - let encoded_right = self.encode_statement_operand(location, right)?; + let (encoded_left, left_post_statements) = + self.encode_statement_operand(block_builder, location, left)?; + let (encoded_right, right_post_statements) = + self.encode_statement_operand(block_builder, location, right)?; let kind = self.encode_binary_op_kind(*op, encoded_target.get_type())?; let encoded_rvalue = vir_high::Rvalue::checked_binary_op(kind, encoded_left, encoded_right); @@ -761,10 +1131,13 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ErrorCtxt::Assign, vir_high::Statement::assign_no_pos(encoded_target, encoded_rvalue), )?); + block_builder.add_statements(left_post_statements); + block_builder.add_statements(right_post_statements); } // mir::Rvalue::NullaryOp(NullOp, Ty<'tcx>), mir::Rvalue::UnaryOp(op, operand) => { - let encoded_operand = self.encode_statement_operand(location, operand)?; + let (encoded_operand, post_statements) = + self.encode_statement_operand(block_builder, location, operand)?; let kind = match op { mir::UnOp::Not => vir_high::UnaryOpKind::Not, mir::UnOp::Neg => vir_high::UnaryOpKind::Minus, @@ -775,15 +1148,16 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ErrorCtxt::Assign, vir_high::Statement::assign_no_pos(encoded_target, encoded_rvalue), )?); + block_builder.add_statements(post_statements); } mir::Rvalue::Discriminant(place) => { let encoded_place = self.encode_place(*place, None)?; - let deref_base = encoded_place.get_dereference_base().cloned(); - let source_permission = self.encode_open_reference( + // let deref_base = encoded_place.get_dereference_base().cloned(); + let source_permission = self.encode_automatic_open_reference( block_builder, location, - &deref_base, + // &deref_base, encoded_place.clone(), )?; @@ -797,10 +1171,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { vir_high::Statement::assign_no_pos(encoded_target, encoded_rvalue), )?); - self.encode_close_reference( + self.encode_automatic_close_reference( block_builder, location, - &deref_base, + // &deref_base, encoded_place, source_permission, )?; @@ -871,7 +1245,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let mut encoded_operands = Vec::new(); for operand in operands { - let mut encoded_operand = self.encode_statement_operand(location, operand)?; + let mut encoded_operand = + self.encode_statement_operand_no_refs(block_builder, location, operand)?; let new_expression = encoded_operand .expression .clone() @@ -903,95 +1278,175 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { Ok(()) } + fn is_manually_managed(&self, place: &vir_high::Expression) -> bool { + for manual_place in &self.manually_managed_places { + if place.has_prefix(manual_place) { + return true; + } + } + false + } + fn encode_close_reference( &mut self, - block_builder: &mut BasicBlockBuilder, location: mir::Location, deref_base: &Option, place: vir_high::Expression, permission: Option, - ) -> SpannedEncodingResult<()> { + ) -> SpannedEncodingResult> { + let mut statement = None; if let Some(base) = deref_base { - if let vir_high::ty::Type::Reference(vir_high::ty::Reference { - lifetime, - uniqueness, - .. - }) = base.get_type() - { - if *uniqueness == vir_high::ty::Uniqueness::Unique { - block_builder.add_statement(self.set_statement_error( - location, - ErrorCtxt::CloseMutRef, - vir_high::Statement::close_mut_ref_no_pos( - lifetime.clone(), - self.lifetime_token_fractional_permission(self.lifetime_count), - place, - ), - )?); - } else { - block_builder.add_statement(self.set_statement_error( - location, - ErrorCtxt::CloseFracRef, - vir_high::Statement::close_frac_ref_no_pos( - lifetime.clone(), - self.lifetime_token_fractional_permission(self.lifetime_count), - place, - permission.unwrap(), - ), - )?); + match base.get_type() { + vir_high::ty::Type::Reference(vir_high::ty::Reference { + lifetime, + uniqueness, + .. + }) => { + if *uniqueness == vir_high::ty::Uniqueness::Unique { + statement = Some(self.set_statement_error( + location, + ErrorCtxt::CloseMutRef, + vir_high::Statement::close_mut_ref_no_pos( + lifetime.clone(), + self.lifetime_token_fractional_permission(self.lifetime_count), + place, + ), + )?); + } else { + statement = Some(self.set_statement_error( + location, + ErrorCtxt::CloseFracRef, + vir_high::Statement::close_frac_ref_no_pos( + lifetime.clone(), + self.lifetime_token_fractional_permission(self.lifetime_count), + place, + permission.unwrap(), + ), + )?); + } } - } else { - unreachable!(); - }; + vir_high::ty::Type::Pointer(_) => {} + _ => unreachable!(), + } + } + Ok(statement) + } + + fn encode_automatic_close_reference( + &mut self, + block_builder: &mut impl StatementSequenceBuilder, + location: mir::Location, + place: vir_high::Expression, + permission: Option, + ) -> SpannedEncodingResult<()> { + if self.is_manually_managed(&place) { + return Ok(()); + } + let deref_base = place.get_dereference_base().cloned(); + let statement = self.encode_close_reference(location, &deref_base, place, permission)?; + if let Some(statement) = statement { + block_builder.add_statement(statement); } Ok(()) } fn encode_open_reference( &mut self, - block_builder: &mut BasicBlockBuilder, location: mir::Location, deref_base: &Option, place: vir_high::Expression, - ) -> SpannedEncodingResult> { + ) -> SpannedEncodingResult<(Option, Option)> { let mut variable = None; + let mut statement = None; if let Some(base) = deref_base { - if let vir_high::ty::Type::Reference(vir_high::ty::Reference { - lifetime, - uniqueness, - .. - }) = base.get_type() - { - if *uniqueness == vir_high::ty::Uniqueness::Unique { - block_builder.add_statement(self.set_statement_error( - location, - ErrorCtxt::OpenMutRef, - vir_high::Statement::open_mut_ref_no_pos( - lifetime.clone(), - self.lifetime_token_fractional_permission(self.lifetime_count), - place, - ), - )?); - } else { - let permission = - self.fresh_ghost_variable("tmp_frac_ref_perm", vir_high::Type::MPerm); - variable = Some(permission.clone()); - block_builder.add_statement(self.set_statement_error( - location, - ErrorCtxt::OpenFracRef, - vir_high::Statement::open_frac_ref_no_pos( - lifetime.clone(), - permission, - self.lifetime_token_fractional_permission(self.lifetime_count), - place, - ), - )?); + match base.get_type() { + vir_high::ty::Type::Reference(vir_high::ty::Reference { + lifetime, + uniqueness, + .. + }) => { + if *uniqueness == vir_high::ty::Uniqueness::Unique { + statement = Some(self.set_statement_error( + location, + ErrorCtxt::OpenMutRef, + vir_high::Statement::open_mut_ref_no_pos( + lifetime.clone(), + self.lifetime_token_fractional_permission(self.lifetime_count), + place, + ), + )?); + } else { + let permission = + self.fresh_ghost_variable("tmp_frac_ref_perm", vir_high::Type::MPerm); + variable = Some(permission.clone()); + statement = Some(self.set_statement_error( + location, + ErrorCtxt::OpenFracRef, + vir_high::Statement::open_frac_ref_no_pos( + lifetime.clone(), + permission, + self.lifetime_token_fractional_permission(self.lifetime_count), + place, + ), + )?); + } } - } else { - unreachable!("place: {} deref_base: {:?}", place, deref_base); + vir_high::ty::Type::Pointer(_) => { + // Note: if the dereferenced place is behind a raw pointer + // and reference, we require the user to manually open the + // reference. + } + _ => unreachable!("place: {} deref_base: {:?}", place, base), } }; - Ok(variable) + Ok((variable, statement)) + } + + fn encode_automatic_open_reference( + &mut self, + block_builder: &mut BasicBlockBuilder, + location: mir::Location, + // deref_base: &Option, + place: vir_high::Expression, + ) -> SpannedEncodingResult> { + if self.is_manually_managed(&place) { + return Ok(None); + } + let deref_place = place.get_dereference_base().cloned(); + let (variable, statement) = + self.encode_open_reference(location, &deref_place, place.clone())?; + if let Some(statement) = statement { + block_builder.add_statement(statement); + } + if variable.is_some() { + Ok(variable) + } else { + Ok(self.lookup_opened_reference_place_permission(&place)) + // // Check whether the place was manually opened. FIXME: The + // // permission amount is cotrol-flow dependent and, therefore, should + // // be inserted by the fold-unfold algorithm. + // for (opened_place, variable) in &self.opened_reference_place_permissions { + // if place.has_prefix(opened_place) { + // return Ok(variable.clone()); + // } + // } + // Ok(None) + } + } + + /// Check whether the place was manually opened. FIXME: The + /// permission amount is cotrol-flow dependent and, therefore, should + /// be inserted by the fold-unfold algorithm. + fn lookup_opened_reference_place_permission( + &self, + place: &vir_high::Expression, + ) -> Option { + for (opened_place, variable) in &self.opened_reference_place_permissions { + if place.has_prefix(opened_place) { + return variable.clone(); + } + } + None } fn encode_assign_operand( @@ -1003,11 +1458,11 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ) -> SpannedEncodingResult<()> { let span = self.encoder.get_span_of_location(self.mir, location); - let deref_base = encoded_target.get_dereference_base().cloned(); - let target_permission = self.encode_open_reference( + // let deref_base = encoded_target.get_dereference_base().cloned(); + let target_permission = self.encode_automatic_open_reference( block_builder, location, - &deref_base, + // &deref_base, encoded_target.clone(), )?; match operand { @@ -1033,11 +1488,11 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { "{encoded_source} is not place (encoded from: {source:?}" ); - let deref_base = encoded_source.get_dereference_base().cloned(); - let source_permission = self.encode_open_reference( + // let deref_base = encoded_source.get_dereference_base().cloned(); + let source_permission = self.encode_automatic_open_reference( block_builder, location, - &deref_base, + // &deref_base, encoded_source.clone(), )?; @@ -1051,10 +1506,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ), )?); - self.encode_close_reference( + self.encode_automatic_close_reference( block_builder, location, - &deref_base, + // &deref_base, encoded_source, source_permission, )?; @@ -1075,10 +1530,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { } } - self.encode_close_reference( + self.encode_automatic_close_reference( block_builder, location, - &deref_base, + // &deref_base, encoded_target, target_permission, )?; @@ -1086,11 +1541,26 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { Ok(()) } + // fn encode_assign_cast( + // &mut self, + // block_builder: &mut BasicBlockBuilder, + // location: mir::Location, + // encoded_target: vir_crate::high::Expression, + // kind: mir::CastKind, + // operand: &mir::Operand<'tcx>, + // ty: ty::Ty<'tcx>, + // ) -> SpannedEncodingResult<()> { + // let span = self.encoder.get_span_of_location(self.mir, location); + // match ty {} + // } + fn encode_statement_operand( &mut self, + block_builder: &mut BasicBlockBuilder, location: mir::Location, operand: &mir::Operand<'tcx>, - ) -> SpannedEncodingResult { + ) -> SpannedEncodingResult<(vir_high::Operand, Vec)> { + let mut post_statements = Vec::new(); let span = self.encoder.get_span_of_location(self.mir, location); let encoded_operand = match operand { mir::Operand::Move(source) => { @@ -1105,6 +1575,17 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let encoded_source = self .encode_place(*source, Some(span))? .set_default_position(position); + let source_permission = self.encode_automatic_open_reference( + block_builder, + location, + encoded_source.clone(), + )?; + self.encode_automatic_close_reference( + &mut post_statements, + location, + encoded_source.clone(), + source_permission, + )?; vir_high::Operand::new(vir_high::OperandKind::Copy, encoded_source) } mir::Operand::Constant(constant) => { @@ -1117,7 +1598,19 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { vir_high::Operand::new(vir_high::OperandKind::Constant, encoded_constant) } }; - Ok(encoded_operand) + Ok((encoded_operand, post_statements)) + } + + fn encode_statement_operand_no_refs( + &mut self, + block_builder: &mut BasicBlockBuilder, + location: mir::Location, + operand: &mir::Operand<'tcx>, + ) -> SpannedEncodingResult { + let (operand, post_statements) = + self.encode_statement_operand(block_builder, location, operand)?; + assert!(post_statements.is_empty(), "unimplemented"); + Ok(operand) } fn encode_binary_op_kind( @@ -1306,6 +1799,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { block_builder.add_statements(statements); } self.encode_lft_for_block(real_target, location, block_builder)?; + // self.encode_lifetimes_dead_on_edge( + // block_builder, + // RichLocation::Mid(location), + // RichLocation::Start(mir::Location { + // block: real_target, + // statement_index: 0, + // }), + // )?; return Ok(SuccessorBuilder::jump(vir_high::Successor::Goto( self.encode_basic_block_label(real_target), ))); @@ -1322,6 +1823,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ); let mut successors = Vec::new(); for (value, target) in targets.iter() { + // self.encode_lifetimes_dead_on_edge( + // block_builder, + // RichLocation::Mid(location), + // RichLocation::Start(mir::Location { + // block: target, + // statement_index: 0, + // }), + // )?; let encoded_condition = match switch_ty.kind() { ty::TyKind::Bool => { if value == 0 { @@ -1351,6 +1860,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { )?; successors.push((encoded_condition, encoded_target)); } + // self.encode_lifetimes_dead_on_edge( + // block_builder, + // RichLocation::Mid(location), + // RichLocation::Start(mir::Location { + // block: targets.otherwise(), + // statement_index: 0, + // }), + // )?; let otherwise = self.encode_basic_block_label(targets.otherwise()); let otherwise = self.encode_lft_for_block_with_edge( targets.otherwise(), @@ -1487,6 +2004,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let query = self.encoder.env().query; let (called_def_id, call_substs) = query.resolve_method_call(self.def_id, called_def_id, call_substs); + let is_unsafe = query.is_unsafe_function(called_def_id); // find static lifetime to exhale let mut lifetimes_to_exhale_inhale: Vec = Vec::new(); @@ -1574,7 +2092,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { .encode_operand_high(self.mir, arg, span) .with_span(span)?, ); - let encoded_arg = self.encode_statement_operand(location, arg)?; + let encoded_arg = + self.encode_statement_operand_no_refs(block_builder, location, arg)?; let statement = vir_high::Statement::consume_no_pos(encoded_arg); block_builder.add_statement(self.encoder.set_statement_error_ctxt( statement, @@ -1604,18 +2123,50 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { )?; } - for expression in - self.encode_precondition_expressions(&procedure_contract, call_substs, &arguments)? - { - let assert_statement = self.encoder.set_statement_error_ctxt( - vir_high::Statement::assert_no_pos(expression), + let precondition_expressions = + self.encode_precondition_expressions(&procedure_contract, call_substs, &arguments)?; + let has_no_precondition = precondition_expressions.is_empty(); + let mut precondition_conjuncts = Vec::new(); + for expression in precondition_expressions { + if let Some(expression) = + self.convert_expression_to_check_mode_call_site(expression, is_unsafe, &arguments)? + { + // let exhale_statement = self.encoder.set_statement_error_ctxt( + // vir_high::Statement::exhale_expression_no_pos(expression), + // span, + // ErrorCtxt::ExhaleMethodPrecondition, + // self.def_id, + // )?; + // block_builder.add_statement(exhale_statement); + let conjunct = self.encoder.set_expression_error_ctxt( + expression, + span, + ErrorCtxt::ExhaleMethodPrecondition, + self.def_id, + ); + precondition_conjuncts.push(conjunct); + } + } + let exhale_statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::exhale_expression_no_pos( + precondition_conjuncts.into_iter().conjoin(), + Some(old_label.clone()), + ), + span, + ErrorCtxt::ExhaleMethodPrecondition, + self.def_id, + )?; + block_builder.add_statement(exhale_statement); + + let is_pure = self.encoder.is_pure(called_def_id, Some(call_substs)); + if !is_pure && self.check_mode.is_purification_group() { + let heap_havoc_statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::heap_havoc_no_pos(), span, ErrorCtxt::ExhaleMethodPrecondition, self.def_id, )?; - if self.check_mode != CheckMode::CoreProof { - block_builder.add_statement(assert_statement); - } + block_builder.add_statement(heap_havoc_statement); } if self.encoder.env().query.is_closure(called_def_id) { @@ -1646,7 +2197,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { size, ); block_builder.add_statement(self.encoder.set_statement_error_ctxt( - vir_high::Statement::exhale_no_pos(target_memory_block.clone()), + vir_high::Statement::exhale_predicate_no_pos(target_memory_block.clone()), span, ErrorCtxt::ProcedureCall, self.def_id, @@ -1657,7 +2208,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let mut post_call_block_builder = block_builder.create_basic_block_builder(fresh_destination_label.clone()); post_call_block_builder.set_successor_jump(vir_high::Successor::Goto(target_label)); - let statement = vir_high::Statement::inhale_no_pos( + let statement = vir_high::Statement::inhale_predicate_no_pos( vir_high::Predicate::owned_non_aliased_no_pos(encoded_target_place.clone()), ); post_call_block_builder.add_statement(self.encoder.set_statement_error_ctxt( @@ -1687,18 +2238,44 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { self.encode_lft_for_block(*target_block, location, &mut post_call_block_builder)?; + let result_place = vec![encoded_target_place.clone()]; + let mut postcondition_conjuncts = Vec::new(); for expression in postcondition_expressions { - let assume_statement = self.encoder.set_statement_error_ctxt( - vir_high::Statement::assume_no_pos(expression), - span, - ErrorCtxt::UnexpectedAssumeMethodPostcondition, - self.def_id, - )?; - if self.check_mode != CheckMode::CoreProof { - post_call_block_builder.add_statement(assume_statement); + if let Some(expression) = self.convert_expression_to_check_mode_call_site( + expression, + is_unsafe || + // If we have no precondition, then we can soundly + // allways include the function postcondition. + has_no_precondition, + &result_place, + )? { + // let inhale_statement = self.encoder.set_statement_error_ctxt( + // vir_high::Statement::inhale_expression_no_pos(expression), + // span, + // ErrorCtxt::UnexpectedAssumeMethodPostcondition, + // self.def_id, + // )?; + // post_call_block_builder.add_statement(inhale_statement); + let conjunct = self.encoder.set_expression_error_ctxt( + expression, + span, + ErrorCtxt::UnexpectedAssumeMethodPostcondition, + self.def_id, + ); + postcondition_conjuncts.push(conjunct); } } - if self.encoder.is_pure(called_def_id, Some(call_substs)) + let inhale_statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::inhale_expression_no_pos( + postcondition_conjuncts.into_iter().conjoin(), + None, + ), + span, + ErrorCtxt::UnexpectedAssumeMethodPostcondition, + self.def_id, + )?; + post_call_block_builder.add_statement(inhale_statement); + if is_pure && !self.encoder.env().callee_reaches_caller( self.def_id, called_def_id, @@ -1733,9 +2310,42 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ErrorCtxt::UnexpectedAssumeMethodPostcondition, self.def_id, )?; - if self.check_mode != CheckMode::CoreProof { + if self.check_mode.check_specifications() || + // If we have no precondition, then we can soundly + // allways include the function postcondition. + has_no_precondition + { post_call_block_builder.add_statement(assume_statement); } + } else { + // // FIXME: We do this because extern specs do not support primitive + // // types. + // let func_name = self.encoder.env().name.get_unique_item_name(called_def_id); + // if func_name.starts_with("std::ptr::mut_ptr::::is_null") + // || func_name.starts_with("core::std::ptr::mut_ptr::::is_null") { + // let type_arguments = self + // .encoder + // .encode_generic_arguments_high(called_def_id, call_substs) + // .with_span(span)?; + // let expression = vir_high::Expression::equals( + // encoded_target_place, + // vir_high::Expression::builtin_func_app_no_pos( + // vir_high::BuiltinFunc::IsNull, + // type_arguments, + // arguments, + // vir_high::Type::Bool, + // ), + // ); + // let assume_statement = self.encoder.set_statement_error_ctxt( + // vir_high::Statement::assume_no_pos(expression), + // span, + // ErrorCtxt::UnexpectedAssumeMethodPostcondition, + // self.def_id, + // )?; + // if self.check_mode != CheckMode::CoreProof { + // post_call_block_builder.add_statement(assume_statement); + // } + // } } post_call_block_builder.build(); @@ -1747,7 +2357,8 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { cleanup_block_builder .set_successor_jump(vir_high::Successor::Goto(encoded_cleanup_block)); - let statement = vir_high::Statement::inhale_no_pos(target_memory_block); + let statement = + vir_high::Statement::inhale_predicate_no_pos(target_memory_block); cleanup_block_builder.add_statement(self.encoder.set_statement_error_ctxt( statement, span, @@ -1910,8 +2521,21 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { .collect(); // Encode the specification blocks. + + // First, encode all specification expressions because they are sometimes used before they are declared. + let mut encoded_blocks = FxHashSet::default(); + for bb in entry_points.keys() { + let block = &self.mir[*bb]; + if self.try_encode_specification_expression(*bb, block)? { + encoded_blocks.insert(*bb); + } + } + + // Encode the remaining specification blocks. for (bb, statements) in &mut entry_points { - self.encode_specification_block(*bb, statements)?; + if !encoded_blocks.contains(bb) { + self.encode_specification_block(*bb, statements)?; + } } assert!(self.specification_block_encoding.is_empty()); self.specification_block_encoding = entry_points; @@ -1944,6 +2568,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { ) -> SpannedEncodingResult<()> { let block = &self.mir[bb]; if false + // || self.try_encode_specification_expression(bb, block)? || self.try_encode_assert(bb, block, encoded_statements)? || self.try_encode_assume(bb, block, encoded_statements)? || self.try_encode_ghost_markers(bb, block, encoded_statements)? @@ -1955,6 +2580,60 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { } } + /// Check whether this basic block defines a Prusti specification + /// expression. If it does, encoding it and save it under the given + /// specification id. + fn try_encode_specification_expression( + &mut self, + bb: mir::BasicBlock, + block: &mir::BasicBlockData<'tcx>, + ) -> SpannedEncodingResult { + for stmt in &block.statements { + if let mir::StatementKind::Assign(box ( + _, + mir::Rvalue::Aggregate(box mir::AggregateKind::Closure(cl_def_id, cl_substs), _), + )) = stmt.kind + { + let def_id = cl_def_id.to_def_id(); + let expression = match self.encoder.get_prusti_specification_expression(def_id) { + Some(spec) => spec, + None => return Ok(false), + }; + + let span = self + .encoder + .get_definition_span(expression.expression.to_def_id()); + + // We do not know the error context here, so we use a dummy one. + let error_ctxt = ErrorCtxt::UnexpectedSpecificationExpression; + + let expression = self.encoder.set_expression_error_ctxt( + self.encoder.encode_loop_spec_high( + self.mir, + bb, + self.def_id, + cl_substs, + true, + )?, + span, + error_ctxt, + self.def_id, + ); + + let attrs = self.encoder.env().query.get_attributes(def_id); + let Some(raw_spec_id) = prusti_interface::utils::read_prusti_attr("spec_id", attrs) else { + unreachable!(); + }; + + self.specification_expressions + .insert(raw_spec_id, expression); + + return Ok(true); + } + } + Ok(false) + } + fn try_encode_assert( &mut self, bb: mir::BasicBlock, @@ -1979,8 +2658,13 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let error_ctxt = ErrorCtxt::Panic(PanicCause::Assert); let assert_expr = self.encoder.set_expression_error_ctxt( - self.encoder - .encode_loop_spec_high(self.mir, bb, self.def_id, cl_substs)?, + self.encoder.encode_loop_spec_high( + self.mir, + bb, + self.def_id, + cl_substs, + false, + )?, span, error_ctxt.clone(), self.def_id, @@ -1994,7 +2678,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { self.def_id, )?; - if self.check_mode != CheckMode::CoreProof { + if self.check_mode.check_specifications() { encoded_statements.push(assert_stmt); } @@ -2029,8 +2713,13 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let error_ctxt = ErrorCtxt::Assumption; let expr = self.encoder.set_expression_error_ctxt( - self.encoder - .encode_loop_spec_high(self.mir, bb, self.def_id, cl_substs)?, + self.encoder.encode_loop_spec_high( + self.mir, + bb, + self.def_id, + cl_substs, + false, + )?, span, error_ctxt.clone(), self.def_id, @@ -2041,7 +2730,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { self.encoder .set_statement_error_ctxt(stmt, span, error_ctxt, self.def_id)?; - if self.check_mode != CheckMode::CoreProof { + if assumption.is_structural || self.check_mode.check_specifications() { encoded_statements.push(stmt); } @@ -2071,6 +2760,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { Ok(false) } + // TODO: Move this function to a separate file and extract nested functions. fn try_encode_specification_function_call( &mut self, bb: mir::BasicBlock, @@ -2078,6 +2768,10 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { encoded_statements: &mut Vec, ) -> SpannedEncodingResult { let span = self.encoder.get_mir_terminator_span(block.terminator()); + let location = mir::Location { + block: bb, + statement_index: block.statements.len(), + }; match &block.terminator().kind { mir::TerminatorKind::Call { func: mir::Operand::Constant(box mir::Constant { literal, .. }), @@ -2091,10 +2785,34 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { if let ty::TyKind::FnDef(def_id, _substs) = literal.ty().kind() { let full_called_function_name = self.encoder.env().name.get_absolute_item_name(*def_id); - match full_called_function_name.as_ref() { - "prusti_contracts::prusti_set_union_active_field" => { - assert_eq!(args.len(), 1); - let argument_place = if let mir::Operand::Move(place) = args[0] { + enum ArgKind { + Place(vir_high::Expression), + String(String), + } + fn extract_args<'p, 'v: 'p, 'tcx: 'v>( + mir: &mir::Body<'tcx>, + args: &[mir::Operand<'tcx>], + block: &mir::BasicBlockData<'tcx>, + encoder: &mut ProcedureEncoder<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult> { + // assert_eq!(args.len(), 1); + let mut encoded_args = Vec::new(); + for arg in args { + match arg { + mir::Operand::Move(_) => {} + mir::Operand::Constant(constant) => { + // FIXME: There should be a proper way of doing this. + let value = format!("{constant:?}"); + let value = + value.trim_start_matches("const \"").trim_end_matches('\"'); + encoded_args.push(ArgKind::String(value.to_string())); + continue; // FIXME: Do proper control flow. + } + _ => { + unreachable!() + } + } + let argument_place = if let mir::Operand::Move(place) = arg { place } else { unreachable!() @@ -2102,15 +2820,30 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { // Find the place whose address was stored in the argument by // iterating backwards through statements. let mut statement_index = block.statements.len() - 1; - let union_variant_place = loop { + let place = loop { if let Some(statement) = block.statements.get(statement_index) { - if let mir::StatementKind::Assign(box ( - target_place, - mir::Rvalue::AddressOf(_, union_variant_place), - )) = &statement.kind + if let mir::StatementKind::Assign(box (target_place, rvalue)) = + &statement.kind { - if *target_place == argument_place { - break union_variant_place; + if target_place == argument_place { + match rvalue { + mir::Rvalue::AddressOf(_, place) => { + break encoder.encode_place(*place, None)?; + } + mir::Rvalue::Use(operand) => { + break encoder + .encoder + .encode_operand_high( + mir, + operand, + statement.source_info.span, + ) + .with_span(statement.source_info.span)?; + } + _ => { + unimplemented!("rvalue: {:?}", rvalue); + } + } } } statement_index -= 1; @@ -2118,8 +2851,65 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { unreachable!(); } }; - let encoded_variant_place = - self.encode_place(*union_variant_place, None)?; + encoded_args.push(ArgKind::Place(place)); + } + Ok(encoded_args) + } + fn extract_places<'p, 'v: 'p, 'tcx: 'v>( + mir: &mir::Body<'tcx>, + args: &[mir::Operand<'tcx>], + block: &mir::BasicBlockData<'tcx>, + encoder: &mut ProcedureEncoder<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult> { + let places = extract_args(mir, args, block, encoder)? + .into_iter() + .map(|arg| match arg { + ArgKind::Place(place) => place, + ArgKind::String(_) => unreachable!(), + }) + .collect(); + Ok(places) + } + fn extract_place<'p, 'v: 'p, 'tcx: 'v>( + mir: &mir::Body<'tcx>, + args: &[mir::Operand<'tcx>], + block: &mir::BasicBlockData<'tcx>, + encoder: &mut ProcedureEncoder<'p, 'v, 'tcx>, + ) -> SpannedEncodingResult { + assert_eq!(args.len(), 1); + Ok(extract_places(mir, args, block, encoder)?.pop().unwrap()) + } + match full_called_function_name.as_ref() { + "prusti_contracts::prusti_set_union_active_field" => { + assert_eq!(args.len(), 1); + // assert_eq!(args.len(), 1); + // let argument_place = if let mir::Operand::Move(place) = args[0] { + // place + // } else { + // unreachable!() + // }; + // // Find the place whose address was stored in the argument by + // // iterating backwards through statements. + // let mut statement_index = block.statements.len() - 1; + // let union_variant_place = loop { + // if let Some(statement) = block.statements.get(statement_index) { + // if let mir::StatementKind::Assign(box ( + // target_place, + // mir::Rvalue::AddressOf(_, union_variant_place), + // )) = &statement.kind + // { + // if *target_place == argument_place { + // break union_variant_place; + // } + // } + // statement_index -= 1; + // } else { + // unreachable!(); + // } + // }; + // let encoded_variant_place = + // self.encode_place(*union_variant_place, None)?; + let encoded_variant_place = extract_place(self.mir, args, block, self)?; let statement = self.encoder.set_statement_error_ctxt( vir_high::Statement::set_union_variant_no_pos( encoded_variant_place, @@ -2132,7 +2922,635 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { encoded_statements.push(statement); Ok(true) } - _ => unreachable!(), + "prusti_contracts::prusti_manually_manage" => { + let encoded_place = extract_place(self.mir, args, block, self)?; + assert!(self.manually_managed_places.insert(encoded_place)); + Ok(true) + } + "prusti_contracts::prusti_pack_place" => { + let encoded_place = extract_place(self.mir, args, block, self)?; + let permission_amount = + self.lookup_opened_reference_place_permission(&encoded_place); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::pack_no_pos( + encoded_place, + vir_high::PredicateKind::Owned, + permission_amount, + ), + span, + ErrorCtxt::Pack, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_unpack_place" => { + let encoded_place = extract_place(self.mir, args, block, self)?; + let permission_amount = + self.lookup_opened_reference_place_permission(&encoded_place); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::unpack_no_pos( + encoded_place, + vir_high::PredicateKind::Owned, + permission_amount, + ), + span, + ErrorCtxt::Unpack, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_pack_ref_place" => { + assert_eq!(args.len(), 2); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::Place(place) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::String(lifetime_name) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + assert!(encoded_args.is_empty()); + let lifetime = self + .user_named_lifetimes + .get(&lifetime_name) + .unwrap() + .clone(); + let permission_amount = + self.lookup_opened_reference_place_permission(&place); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::pack_no_pos( + place, + vir_high::PredicateKind::frac_ref(lifetime), + permission_amount, + ), + span, + ErrorCtxt::Pack, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_unpack_ref_place" => { + assert_eq!(args.len(), 2); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::Place(place) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::String(lifetime_name) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + assert!(encoded_args.is_empty()); + let lifetime = self + .user_named_lifetimes + .get(&lifetime_name) + .unwrap() + .clone(); + // let encoded_place = extract_place(self.mir, args, block, self)?; + let permission_amount = + self.lookup_opened_reference_place_permission(&place); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::unpack_no_pos( + place, + vir_high::PredicateKind::frac_ref(lifetime), + permission_amount, + ), + span, + ErrorCtxt::Unpack, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_pack_mut_ref_place" => { + assert_eq!(args.len(), 2); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::Place(place) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::String(lifetime_name) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + assert!(encoded_args.is_empty()); + let lifetime = self + .user_named_lifetimes + .get(&lifetime_name) + .unwrap() + .clone(); + // let encoded_place = extract_place(self.mir, args, block, self)?; + let permission_amount = + self.lookup_opened_reference_place_permission(&place); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::pack_no_pos( + place, + vir_high::PredicateKind::unique_ref(lifetime), + permission_amount, + ), + span, + ErrorCtxt::Pack, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_unpack_mut_ref_place" => { + assert_eq!(args.len(), 2); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::Place(place) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::String(lifetime_name) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + assert!(encoded_args.is_empty()); + let lifetime = self + .user_named_lifetimes + .get(&lifetime_name) + .unwrap() + .clone(); + let permission_amount = + self.lookup_opened_reference_place_permission(&place); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::unpack_no_pos( + place, + vir_high::PredicateKind::unique_ref(lifetime), + permission_amount, + ), + span, + ErrorCtxt::Unpack, + self.def_id, + )?; + // let encoded_place = extract_place(self.mir, args, block, self)?; + // let statement = self.encoder.set_statement_error_ctxt( + // vir_high::Statement::unpack_no_pos( + // encoded_place, + // vir_high::PredicateKind::UniqueRef, + // ), + // span, + // ErrorCtxt::Unpack, + // self.def_id, + // )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_take_lifetime" => { + assert_eq!(args.len(), 2); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::String(lifetime_name) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(place) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + assert!(encoded_args.is_empty()); + let vir_high::ty::Type::Reference(ref_type) = place.get_type() else { + unimplemented!("FIXME: A proper error message."); + }; + let lifetime = ref_type.lifetime.clone(); + assert!(self + .user_named_lifetimes + .insert(lifetime_name, lifetime) + .is_none()); + Ok(true) + } + "prusti_contracts::prusti_join_place" => { + let encoded_place = extract_place(self.mir, args, block, self)?; + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::join_no_pos(encoded_place), + span, + ErrorCtxt::Pack, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_join_range" => { + assert_eq!(args.len(), 3); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::Place(end_index) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(start_index) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(pointer) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::join_range_no_pos( + pointer, + start_index, + end_index, + ), + span, + ErrorCtxt::JoinRange, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_split_place" => { + let encoded_place = extract_place(self.mir, args, block, self)?; + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::split_no_pos(encoded_place), + span, + ErrorCtxt::Unpack, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_split_range" => { + assert_eq!(args.len(), 3); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::Place(end_index) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(start_index) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(pointer) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::split_range_no_pos( + pointer, + start_index, + end_index, + ), + span, + ErrorCtxt::SplitRange, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_stash_range" => { + assert_eq!(args.len(), 4); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::String(stash_name) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(end_index) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(start_index) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(pointer) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + encoded_statements.push(vir_high::Statement::old_label( + stash_name.clone(), + self.encoder.register_error( + span, + ErrorCtxt::StashRange, + self.def_id, + ), + )); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::stash_range_no_pos( + pointer.clone(), + start_index.clone(), + end_index.clone(), + stash_name.clone(), + ), + span, + ErrorCtxt::StashRange, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + encoded_statements.push(vir_high::Statement::old_label( + format!("{stash_name}$post"), + self.encoder.register_error( + span, + ErrorCtxt::StashRange, + self.def_id, + ), + )); + assert!(self + .stashed_ranges + .insert(stash_name, (pointer, start_index, end_index)) + .is_none()); + Ok(true) + } + "prusti_contracts::prusti_restore_stash_range" => { + assert_eq!(args.len(), 3); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::String(stash_name) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(new_start_index) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let ArgKind::Place(new_pointer) = encoded_args.pop().unwrap() else { + unreachable!("Wrong function parameters?"); + }; + let (old_pointer, old_start_index, old_end_index) = + self.stashed_ranges.get(&stash_name).unwrap().clone(); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::stash_range_restore_no_pos( + old_pointer, + old_start_index, + old_end_index, + stash_name, + new_pointer, + new_start_index, + ), + span, + ErrorCtxt::RestoreStashRange, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_close_ref_place" => { + assert_eq!(args.len(), 2); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::String(witness) = encoded_args.pop().unwrap() else { + unreachable!() + }; + let ArgKind::String(place_spec_id) = encoded_args.pop().unwrap() else { + unreachable!() + }; + let user_place = self + .specification_expressions + .get(&place_spec_id) + .expect("FIXME: A proper error message") + .clone(); + let vir_high::Expression::AddrOf(vir_high::AddrOf { base: box user_place, .. }) = + user_place else { + unreachable!("place: {user_place}"); + }; + assert!(encoded_args.is_empty()); + // FIXME: These should actually remove the + // witnesses. However, since specification blocks + // are processed before all other blocks, the state + // cannot be easily transfered. A proper solution + // would be to check whether the state that uses the + // opened permission is dominated by the statement + // that opens the reference. Alternatively, we could + // have annotations that specify which permission + // amount to use for copy statements. Another + // alternative (probably the easiest) would be to + // make a static analysis that inserts the right + // permission amount into the copy statement. + // + // A proper solution probably would be to integrate + // this into fold-unfold algorithm. + let (place, lifetime) = self + .opened_reference_witnesses + .get(&witness) + .expect("FIXME: a proper error message"); + assert_eq!(place, &user_place, "FIXME: a proper error message"); + let variable = self + .opened_reference_place_permissions + .get(place) + .expect("FIXME: A proper error message"); + // let deref_base = place.get_last_dereferenced_reference().cloned(); + // let statement = self.encode_close_reference( + // location, + // &deref_base, + // place.clone(), + // variable.clone(), + // )?; + let statement = self.set_statement_error( + location, + ErrorCtxt::CloseFracRef, + vir_high::Statement::close_frac_ref_no_pos( + lifetime.clone(), + self.lifetime_token_fractional_permission(self.lifetime_count), + place.clone(), + variable.clone().unwrap(), + ), + )?; + encoded_statements.push(statement); + // encoded_statements.push(statement.expect( + // "FIXME: A proper error message for closing not a reference", + // )); + Ok(true) + } + "prusti_contracts::prusti_open_ref_place" => { + assert_eq!(args.len(), 3); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::String(witness) = encoded_args.pop().unwrap() else { + unreachable!() + }; + let ArgKind::String(place_spec_id) = encoded_args.pop().unwrap() else { + unreachable!() + }; + let place = self + .specification_expressions + .get(&place_spec_id) + .unwrap() + .clone(); + let vir_high::Expression::AddrOf(vir_high::AddrOf { base: box place, .. }) = + place else { + unreachable!("place: {place}"); + }; + let ArgKind::String(lifetime_name) = encoded_args.pop().unwrap() else { + unreachable!() + }; + place.check_no_erased_lifetime(); + assert!(encoded_args.is_empty()); + let Some(lifetime) = self + .user_named_lifetimes + .get(&lifetime_name) + .cloned() else { + return Err(SpannedEncodingError::incorrect( + format!("Lifetime name `{lifetime_name}` not defined"), span)); + }; + let permission = self + .fresh_ghost_variable("tmp_frac_ref_perm", vir_high::Type::MPerm); + let variable = Some(permission.clone()); + let statement = self.set_statement_error( + location, + ErrorCtxt::OpenFracRef, + vir_high::Statement::open_frac_ref_no_pos( + lifetime.clone(), + permission, + self.lifetime_token_fractional_permission(self.lifetime_count), + place.clone(), + ), + )?; + + // let deref_place = place.get_last_dereferenced_reference().cloned(); + // let (variable, statement) = + // self.encode_open_reference(location, &deref_place, place.clone())?; + encoded_statements.push(statement); + assert!(self + .opened_reference_place_permissions + .insert(place.clone(), variable) + .is_none()); + assert!(self + .opened_reference_witnesses + .insert(witness, (place, lifetime)) + .is_none()); + Ok(true) + } + "prusti_contracts::prusti_close_mut_ref_place" => { + assert_eq!(args.len(), 2); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::String(witness) = encoded_args.pop().unwrap() else { + unreachable!() + }; + let ArgKind::String(place_spec_id) = encoded_args.pop().unwrap() else { + unreachable!() + }; + let user_place = self + .specification_expressions + .get(&place_spec_id) + .expect("FIXME: A proper error message") + .clone(); + let vir_high::Expression::AddrOf(vir_high::AddrOf { base: box user_place, .. }) = + user_place else { + unreachable!("place: {user_place}"); + }; + assert!(encoded_args.is_empty()); + // FIXME: These should actually remove the + // witnesses. However, since specification blocks + // are processed before all other blocks, the state + // cannot be easily transfered. A proper solution + // would be to check whether the state that uses the + // opened permission is dominated by the statement + // that opens the reference. Alternatively, we could + // have annotations that specify which permission + // amount to use for copy statements. Another + // alternative (probably the easiest) would be to + // make a static analysis that inserts the right + // permission amount into the copy statement. + // + // A proper solution probably would be to integrate + // this into fold-unfold algorithm. + let (place, lifetime) = self + .opened_reference_witnesses + .get(&witness) + .expect("FIXME: a proper error message"); + assert_eq!(place, &user_place, "FIXME: a proper error message"); + // let variable = self + // .opened_reference_place_permissions + // .get(&place) + // .expect("FIXME: A proper error message"); + // let deref_base = place.get_last_dereferenced_reference().cloned(); + let statement = self.set_statement_error( + location, + ErrorCtxt::CloseMutRef, + vir_high::Statement::close_mut_ref_no_pos( + lifetime.clone(), + self.lifetime_token_fractional_permission(self.lifetime_count), + place.clone(), + ), + )?; + // let statement = self.encode_close_reference( + // location, + // &deref_base, + // place.clone(), + // variable.clone(), + // )?; + // encoded_statements.push(statement.expect( + // "FIXME: A proper error message for closing not a reference", + // )); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_open_mut_ref_place" => { + assert_eq!(args.len(), 3); + let mut encoded_args = extract_args(self.mir, args, block, self)?; + let ArgKind::String(witness) = encoded_args.pop().unwrap() else { + unreachable!() + }; + let ArgKind::String(place_spec_id) = encoded_args.pop().unwrap() else { + unreachable!() + }; + let place = self + .specification_expressions + .get(&place_spec_id) + .unwrap() + .clone(); + let vir_high::Expression::AddrOf(vir_high::AddrOf { base: box place, .. }) = + place else { + unreachable!("place: {place}"); + }; + let ArgKind::String(lifetime_name) = encoded_args.pop().unwrap() else { + unreachable!() + }; + place.check_no_erased_lifetime(); + assert!(encoded_args.is_empty()); + // let lifetime = self + // .user_named_lifetimes + // .get(&lifetime_name) + // .unwrap() + // .clone(); + let Some(lifetime) = self + .user_named_lifetimes + .get(&lifetime_name) + .cloned() else { + return Err(SpannedEncodingError::incorrect( + format!("Lifetime name `{lifetime_name}` not defined"), span)); + }; + let statement = self.set_statement_error( + location, + ErrorCtxt::OpenMutRef, + vir_high::Statement::open_mut_ref_no_pos( + lifetime.clone(), + self.lifetime_token_fractional_permission(self.lifetime_count), + place.clone(), + ), + )?; + encoded_statements.push(statement); + assert!(self + .opened_reference_place_permissions + .insert(place.clone(), None) + .is_none()); + assert!(self + .opened_reference_witnesses + .insert(witness, (place, lifetime)) + .is_none()); + Ok(true) + } + "prusti_contracts::prusti_forget_initialization" => { + let encoded_place = extract_place(self.mir, args, block, self)?; + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::forget_initialization_no_pos(encoded_place), + span, + ErrorCtxt::ForgetInitialization, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + "prusti_contracts::prusti_restore_place" => { + assert_eq!(args.len(), 2); + let mut encoded_places = extract_places(self.mir, args, block, self)?; + let restored_place = encoded_places.pop().unwrap(); + let borrowing_place = encoded_places.pop().unwrap(); + let statement = self.encoder.set_statement_error_ctxt( + vir_high::Statement::restore_raw_borrowed_no_pos( + borrowing_place, + restored_place, + ), + span, + ErrorCtxt::RestoreRawBorrowed, + self.def_id, + )?; + statement.check_no_default_position(); + encoded_statements.push(statement); + Ok(true) + } + function_name => unreachable!("function: {}", function_name), } } else { unreachable!(); @@ -2141,4 +3559,15 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { _ => unreachable!("block: {:?}", bb), } } + + fn is_pure(&self, def_id: DefId, substs: Option>) -> bool { + self.encoder.is_pure(def_id, substs) + // || { + // // FIXME: We do this because extern specs do not support primitive + // // types. + // let func_name = self.encoder.env().name.get_unique_item_name(def_id); + // func_name.starts_with("std::ptr::mut_ptr::::is_null") + // || func_name.starts_with("core::std::ptr::mut_ptr::::is_null") + // } + } } diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/specification_blocks.rs b/prusti-viper/src/encoder/mir/procedures/encoder/specification_blocks.rs index f3b84ed5886..6afd8d7725a 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/specification_blocks.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/specification_blocks.rs @@ -32,7 +32,7 @@ impl SpecificationBlocks { pub fn build<'tcx>( env_query: EnvQuery<'tcx>, body: &mir::Body<'tcx>, - procedure: &Procedure<'tcx>, + procedure: Option<&Procedure<'tcx>>, collect_loop_invariants: bool, ) -> Self { // Blocks that contain closures marked with `#[spec_only]` attributes. @@ -77,11 +77,13 @@ impl SpecificationBlocks { } // Collect loop invariant blocks. - let loop_info = procedure.loop_info(); - let predecessors = body.basic_blocks.predecessors(); let mut loop_invariant_blocks = BTreeMap::<_, LoopInvariantBlocks>::new(); let mut loop_spec_blocks_flat = BTreeSet::new(); if collect_loop_invariants { + let loop_info = procedure + .expect("procedure needs to be Some when collect_loop_invariants is true") + .loop_info(); + let predecessors = body.basic_blocks.predecessors(); // We use reverse_postorder here because we need to make sure that we // preserve the order of invariants in which they were specified by the // user. @@ -176,7 +178,7 @@ impl SpecificationBlocks { self.specification_entry_blocks.iter().cloned() } - pub(super) fn is_specification_block(&self, bb: mir::BasicBlock) -> bool { + pub fn is_specification_block(&self, bb: mir::BasicBlock) -> bool { self.specification_blocks.contains(&bb) } diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/specifications.rs b/prusti-viper/src/encoder/mir/procedures/encoder/specifications.rs new file mode 100644 index 00000000000..59b76a1cf1f --- /dev/null +++ b/prusti-viper/src/encoder/mir/procedures/encoder/specifications.rs @@ -0,0 +1,126 @@ +use super::ProcedureEncoder; +use crate::{ + encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + Encoder, + }, + error_incorrect, +}; +use vir_crate::high::{ + self as vir_high, + operations::ty::Typed, + visitors::{default_fallible_fold_labelled_old, ExpressionFallibleFolder}, +}; + +impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { + pub(super) fn desugar_pledges_in_postcondition( + &mut self, + precondition_label: &str, + result: &vir_high::Expression, + expression: vir_high::Expression, + ) -> SpannedEncodingResult { + let mut rewriter = Rewriter { + encoder: self.encoder, + precondition_label, + result, + current_state: CurrentState::Postcondition, + }; + rewriter.fallible_fold_expression(expression) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum CurrentState { + Precondition, + Postcondition, + AfterExpiry, + BeforeExpiry, +} + +struct Rewriter<'a, 'v, 'tcx> { + encoder: &'a mut Encoder<'v, 'tcx>, + precondition_label: &'a str, + result: &'a vir_high::Expression, + current_state: CurrentState, +} + +impl<'a, 'v, 'tcx> ExpressionFallibleFolder for Rewriter<'a, 'v, 'tcx> { + type Error = SpannedEncodingError; + + fn fallible_fold_labelled_old( + &mut self, + labelled_old: vir_high::LabelledOld, + ) -> Result { + let old_state = self.current_state; + if labelled_old.label == self.precondition_label { + self.current_state = CurrentState::Precondition; + } + let labelled_old = default_fallible_fold_labelled_old(self, labelled_old)?; + self.current_state = old_state; + Ok(labelled_old) + } + + fn fallible_fold_builtin_func_app_enum( + &mut self, + mut builtin_func_app: vir_high::BuiltinFuncApp, + ) -> Result { + let old_state = self.current_state; + let expression = match builtin_func_app.function { + vir_high::BuiltinFunc::AfterExpiry => { + assert!(builtin_func_app.arguments.len() == 1); + self.current_state = CurrentState::AfterExpiry; + let expression = builtin_func_app.arguments.pop().unwrap(); + self.fallible_fold_expression(expression)? + } + vir_high::BuiltinFunc::BeforeExpiry => { + assert!(builtin_func_app.arguments.len() == 1); + self.current_state = CurrentState::BeforeExpiry; + let expression = builtin_func_app.arguments.pop().unwrap(); + self.fallible_fold_expression(expression)? + } + _ => vir_high::Expression::BuiltinFuncApp( + self.fallible_fold_builtin_func_app(builtin_func_app)?, + ), + }; + self.current_state = old_state; + Ok(expression) + } + + fn fallible_fold_deref_enum( + &mut self, + deref: vir_high::Deref, + ) -> Result { + let deref = self.fallible_fold_deref(deref)?; + let expression = if deref.base.get_type().is_unique_reference() { + match self.current_state { + CurrentState::Precondition => { + if deref.base.has_prefix(self.result) { + let span = self + .encoder + .error_manager() + .position_manager() + .get_span(deref.position.into()) + .unwrap() + .clone(); + error_incorrect!(span => "Function result cannot be dereferenced in precondition state"); + } else { + vir_high::Expression::Deref(deref) + } + } + CurrentState::Postcondition => { + if deref.base.has_prefix(self.result) { + vir_high::Expression::Deref(deref) + } else { + vir_high::Expression::final_(*deref.base, deref.ty, deref.position) + } + } + CurrentState::AfterExpiry | CurrentState::BeforeExpiry => { + vir_high::Expression::final_(*deref.base, deref.ty, deref.position) + } + } + } else { + vir_high::Expression::Deref(deref) + }; + Ok(expression) + } +} diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/termination.rs b/prusti-viper/src/encoder/mir/procedures/encoder/termination.rs index 06376864ac8..0371e40a006 100644 --- a/prusti-viper/src/encoder/mir/procedures/encoder/termination.rs +++ b/prusti-viper/src/encoder/mir/procedures/encoder/termination.rs @@ -12,8 +12,11 @@ use prusti_rustc_interface::{ span::Span, }; use vir_crate::{ - common::{check_mode::CheckMode, expression::BinaryOperationHelpers}, - high::{self as vir_high, builders::procedure::BasicBlockBuilder}, + common::expression::BinaryOperationHelpers, + high::{ + self as vir_high, + builders::procedure::{BasicBlockBuilder, StatementSequenceBuilder}, + }, }; pub(super) enum TerminationMeasure { @@ -49,7 +52,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> super::ProcedureEncoder<'p, 'v, 'tcx> { })?; let expression = self.encoder.encode_assertion_high( - expr.to_def_id(), + expr, None, arguments, None, @@ -80,7 +83,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> super::ProcedureEncoder<'p, 'v, 'tcx> { arguments.push(self.encode_local(local)?.into()); } - if self.encoder.terminates(self.def_id, None) && self.check_mode != CheckMode::CoreProof { + if self.encoder.terminates(self.def_id, None) && self.check_mode.check_specifications() { let termination_expr = self.encode_termination_expression( &procedure_contract, mir_span, diff --git a/prusti-viper/src/encoder/mir/procedures/encoder/utils.rs b/prusti-viper/src/encoder/mir/procedures/encoder/utils.rs new file mode 100644 index 00000000000..4f126546e5a --- /dev/null +++ b/prusti-viper/src/encoder/mir/procedures/encoder/utils.rs @@ -0,0 +1,15 @@ +use prusti_rustc_interface::middle::{mir, ty::TyCtxt}; + +pub(super) fn get_last_reference_deref<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mir::Body<'tcx>, + place: mir::Place<'tcx>, +) -> Option> { + place + .iter_projections() + .filter(|(place, projection)| { + projection == &mir::ProjectionElem::Deref && place.ty(body, tcx).ty.is_ref() + }) + .last() + .map(|(place, _)| place) +} diff --git a/prusti-viper/src/encoder/mir/procedures/interface.rs b/prusti-viper/src/encoder/mir/procedures/interface.rs index 68e42152cb9..d84aa552bff 100644 --- a/prusti-viper/src/encoder/mir/procedures/interface.rs +++ b/prusti-viper/src/encoder/mir/procedures/interface.rs @@ -1,6 +1,9 @@ use crate::encoder::{ errors::SpannedEncodingResult, - mir::{procedures::passes, spans::SpanInterface}, + mir::{ + procedures::{encoder::ProcedureEncodingKind, passes}, + spans::SpanInterface, + }, }; use prusti_rustc_interface::{hir::def_id::DefId, middle::mir, span::Span}; use rustc_hash::FxHashMap; @@ -17,7 +20,7 @@ pub(crate) trait MirProcedureEncoderInterface<'tcx> { &mut self, proc_def_id: DefId, check_mode: CheckMode, - ) -> SpannedEncodingResult; + ) -> SpannedEncodingResult>; fn get_span_of_location(&self, mir: &mir::Body<'tcx>, location: mir::Location) -> Span; } @@ -26,17 +29,33 @@ impl<'v, 'tcx: 'v> MirProcedureEncoderInterface<'tcx> for super::super::super::E &mut self, proc_def_id: DefId, check_mode: CheckMode, - ) -> SpannedEncodingResult { - let procedure = super::encoder::encode_procedure(self, proc_def_id, check_mode)?; + ) -> SpannedEncodingResult> { + let procedure = super::encoder::encode_procedure( + self, + proc_def_id, + check_mode, + ProcedureEncodingKind::Regular, + )?; let procedure = passes::run_passes(self, procedure)?; + let mut procedures = Vec::new(); + if check_mode.check_core_proof() { + let postcondition_check = super::encoder::encode_procedure( + self, + proc_def_id, + check_mode, + ProcedureEncodingKind::PostconditionFrameCheck, + )?; + procedures.push(postcondition_check); + } assert!( self.mir_procedure_encoder_state .encoded_procedure_def_ids .insert(procedure.name.clone(), (proc_def_id, check_mode)) .is_none(), - "The procedure was encoed twice: {proc_def_id:?}" + "The procedure was encoded twice: {proc_def_id:?}" ); - Ok(procedure) + procedures.push(procedure); + Ok(procedures) } fn get_span_of_location(&self, mir: &mir::Body<'tcx>, location: mir::Location) -> Span { self.get_mir_location_span(mir, location) diff --git a/prusti-viper/src/encoder/mir/procedures/passes/assertions.rs b/prusti-viper/src/encoder/mir/procedures/passes/assertions.rs index d50a34c4e08..02460605fa0 100644 --- a/prusti-viper/src/encoder/mir/procedures/passes/assertions.rs +++ b/prusti-viper/src/encoder/mir/procedures/passes/assertions.rs @@ -25,7 +25,7 @@ pub(in super::super) fn propagate_assertions_back<'v, 'tcx: 'v>( can_be_soundly_skipped = match &block.statements[statement_index] { vir_high::Statement::Comment(_) | vir_high::Statement::OldLabel(_) - | vir_high::Statement::Inhale(vir_high::Inhale { + | vir_high::Statement::InhalePredicate(vir_high::InhalePredicate { predicate: vir_high::Predicate::LifetimeToken(_) | vir_high::Predicate::MemoryBlockStack(_) @@ -34,10 +34,12 @@ pub(in super::super) fn propagate_assertions_back<'v, 'tcx: 'v>( | vir_high::Predicate::MemoryBlockHeapDrop(_), position: _, }) - | vir_high::Statement::Exhale(_) + | vir_high::Statement::ExhalePredicate(_) + | vir_high::Statement::ExhaleExpression(_) | vir_high::Statement::Consume(_) | vir_high::Statement::Havoc(_) | vir_high::Statement::GhostHavoc(_) + | vir_high::Statement::HeapHavoc(_) | vir_high::Statement::Assert(_) | vir_high::Statement::MovePlace(_) | vir_high::Statement::CopyPlace(_) @@ -49,6 +51,7 @@ pub(in super::super) fn propagate_assertions_back<'v, 'tcx: 'v>( | vir_high::Statement::SetUnionVariant(_) | vir_high::Statement::NewLft(_) | vir_high::Statement::EndLft(_) + | vir_high::Statement::DeadReference(_) | vir_high::Statement::DeadLifetime(_) | vir_high::Statement::DeadInclusion(_) | vir_high::Statement::LifetimeTake(_) @@ -59,7 +62,19 @@ pub(in super::super) fn propagate_assertions_back<'v, 'tcx: 'v>( | vir_high::Statement::CloseMutRef(_) | vir_high::Statement::CloseFracRef(_) | vir_high::Statement::BorShorten(_) => true, - vir_high::Statement::Assume(_) | vir_high::Statement::Inhale(_) => false, + vir_high::Statement::Pack(_) + | vir_high::Statement::Unpack(_) + | vir_high::Statement::Join(_) + | vir_high::Statement::JoinRange(_) + | vir_high::Statement::Split(_) + | vir_high::Statement::SplitRange(_) + | vir_high::Statement::ForgetInitialization(_) + | vir_high::Statement::RestoreRawBorrowed(_) + | vir_high::Statement::Assume(_) + | vir_high::Statement::InhalePredicate(_) + | vir_high::Statement::InhaleExpression(_) + | vir_high::Statement::StashRange(_) + | vir_high::Statement::StashRangeRestore(_) => false, vir_high::Statement::LoopInvariant(_) => unreachable!(), }; } diff --git a/prusti-viper/src/encoder/mir/procedures/passes/loop_desugaring.rs b/prusti-viper/src/encoder/mir/procedures/passes/loop_desugaring.rs index 287de540068..2894da84de3 100644 --- a/prusti-viper/src/encoder/mir/procedures/passes/loop_desugaring.rs +++ b/prusti-viper/src/encoder/mir/procedures/passes/loop_desugaring.rs @@ -70,8 +70,9 @@ pub(in super::super) fn desugar_loops<'v, 'tcx: 'v>( "Loop Invariant Functional Specifications".to_string(), )); for assertion in &loop_invariant.functional_specifications { + let old_label = None; // We do not have `old` that would refer to a loop invariant. let statement = encoder.set_surrounding_error_context_for_statement( - vir_high::Statement::assert_no_pos(assertion.clone()), + vir_high::Statement::exhale_expression_no_pos(assertion.clone(), old_label), loop_invariant.position, ErrorCtxt::AssertLoopInvariantOnEntry, )?; @@ -121,8 +122,9 @@ pub(in super::super) fn desugar_loops<'v, 'tcx: 'v>( } for assertion in loop_invariant.functional_specifications { + let old_label = None; // We do not have `old` that would refer to a loop invariant. let statement = encoder.set_surrounding_error_context_for_statement( - vir_high::Statement::assume_no_pos(assertion), + vir_high::Statement::inhale_expression_no_pos(assertion, old_label), loop_invariant.position, ErrorCtxt::UnexpectedAssumeLoopInvariantOnEntry, )?; @@ -197,8 +199,9 @@ fn duplicate_blocks<'v, 'tcx: 'v>( if bb == invariant_block { let loop_invariant = block.statements.pop().unwrap().unwrap_loop_invariant(); for assertion in loop_invariant.functional_specifications { + let old_label = None; // We do not have `old` that would refer to a loop invariant. let statement = encoder.set_surrounding_error_context_for_statement( - vir_high::Statement::assert_no_pos(assertion), + vir_high::Statement::exhale_expression_no_pos(assertion, old_label), loop_invariant.position, ErrorCtxt::AssertLoopInvariantAfterIteration, )?; diff --git a/prusti-viper/src/encoder/mir/pure/interpreter/interpreter_high.rs b/prusti-viper/src/encoder/mir/pure/interpreter/interpreter_high.rs index 08da906ed5e..20ddf11535f 100644 --- a/prusti-viper/src/encoder/mir/pure/interpreter/interpreter_high.rs +++ b/prusti-viper/src/encoder/mir/pure/interpreter/interpreter_high.rs @@ -17,6 +17,7 @@ use crate::encoder::{ casts::CastsEncoderInterface, generics::MirGenericsEncoderInterface, places::PlacesEncoderInterface, + procedures::encoder::specification_blocks::SpecificationBlocks, pure::{ interpreter::BackwardMirInterpreter, PureEncodingContext, PureFunctionEncoderInterface, SpecificationEncoderInterface, @@ -29,7 +30,7 @@ use crate::encoder::{ }; use log::{debug, trace}; use prusti_common::vir_high_local; -use prusti_interface::environment::mir_utils::SliceOrArrayRef; +use prusti_interface::environment::{debug_utils::to_text::ToText, mir_utils::SliceOrArrayRef}; use prusti_rustc_interface::{ hir::def_id::DefId, middle::{mir, ty, ty::subst::SubstsRef}, @@ -50,6 +51,9 @@ pub(in super::super) struct ExpressionBackwardInterpreter<'p, 'v: 'p, 'tcx: 'v> encoder: &'p Encoder<'v, 'tcx>, /// MIR of the pure function being encoded. mir: &'p mir::Body<'tcx>, + /// The specification blocks used in the pure function. When encoding + /// something else than a pure function, this is None. + specification_blocks: Option, /// MirEncoder of the pure function being encoded. mir_encoder: MirEncoder<'p, 'v, 'tcx>, /// How panics are handled depending on the encoding context. @@ -73,9 +77,20 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { caller_def_id: DefId, substs: SubstsRef<'tcx>, ) -> Self { + let specification_blocks = if pure_encoding_context == PureEncodingContext::Code { + Some(SpecificationBlocks::build( + encoder.env().query, + mir, + None, + false, + )) + } else { + None + }; Self { encoder, mir, + specification_blocks, mir_encoder: MirEncoder::new(encoder, mir, def_id), pure_encoding_context, caller_def_id, @@ -122,6 +137,30 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { arguments.push(encoded_operand); } match aggregate { + mir::AggregateKind::Closure(def_id, substs) + if self.encoder.is_spec_closure(def_id.to_def_id()) => + { + let cl_substs = substs.as_closure(); + let position = lhs.position(); + for (field_index, field_ty) in cl_substs.upvar_tys().enumerate() { + let operand = &operands[field_index]; + let encoded_operand = self.encode_operand(operand, span)?; + let field_name = format!("closure_{field_index}"); + let encoded_field_type = self.encoder.encode_type_high(field_ty)?; + let field_decl = + vir_high::FieldDecl::new(field_name, field_index, encoded_field_type); + // Note: We are using `lhs`, which is the closure variable + // outside of the closure as it was inside the closure. This + // sometimes works because we are not checking that `lhs` + // type is `Closure` not, `&Closure`. However, we need to try both + // `_1.closure_0` and `_1.*.closure_0` as substitution targets. + let closure_self_deref = lhs.clone().deref(ty.clone(), position); + let field_place = closure_self_deref.field(field_decl.clone(), position); + state.substitute_value(&field_place, encoded_operand.clone()); + let field_place = lhs.clone().field(field_decl, position); + state.substitute_value(&field_place, encoded_operand); + } + } mir::AggregateKind::Array(_) | mir::AggregateKind::Tuple | mir::AggregateKind::Closure(_, _) => { @@ -160,7 +199,13 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { rhs: &mir::Rvalue<'tcx>, span: Span, ) -> SpannedEncodingResult<()> { - let encoded_lhs = self.encode_place(lhs)?.erase_lifetime(); + let encoded_lhs = self.encode_place(lhs)?; + // Our encoding for field assignments is unsound, so just disable them + // for now. FIXME: Have a proper error message. + assert!( + encoded_lhs.is_local(), + "Currently only local variables as assignment targets are supported" + ); let ty = self .encoder .encode_type_of_place_high(self.mir, lhs) @@ -238,7 +283,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { .with_span(span)?; state.substitute_value(&encoded_lhs, expr); } - &mir::Rvalue::Ref(_, kind, place) => { + &mir::Rvalue::Ref(region, kind, place) => { if !matches!( kind, mir::BorrowKind::Unique | mir::BorrowKind::Mut { .. } | mir::BorrowKind::Shared @@ -253,7 +298,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { .encoder .encode_type_of_place_high(self.mir, place) .with_span(span)?; - let pure_lifetime = vir_high::ty::LifetimeConst::erased(); + let pure_lifetime = vir_high::ty::LifetimeConst::new(region.to_text()); let uniqueness = if matches!(kind, mir::BorrowKind::Mut { .. }) { vir_high::ty::Uniqueness::Unique } else { @@ -297,6 +342,20 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { )); } } + mir::Rvalue::Cast( + mir::CastKind::Pointer(ty::adjustment::PointerCast::MutToConstPointer), + operand, + _cast_ty, + ) => { + let arg = self.encode_operand(operand, span)?; + let expr = vir_high::Expression::builtin_func_app_no_pos( + vir_high::BuiltinFunc::CastMutToConstPointer, + Vec::new(), + vec![arg], + encoded_lhs.get_type().clone(), + ); + state.substitute_value(&encoded_lhs, expr); + } mir::Rvalue::Cast(kind, _, _) => { return Err(SpannedEncodingError::unsupported( format!("unsupported kind of cast: {kind:?}"), @@ -322,8 +381,19 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { let expr = vir_high::Expression::constructor_no_pos(ty, arguments); state.substitute_value(&encoded_lhs, expr); } + mir::Rvalue::AddressOf(_, place) => { + let encoded_place = self.encoder.encode_place_high(self.mir, *place, None)?; + let ty = self + .encoder + .encode_type_of_place_high(self.mir, *place) + .with_span(span)?; + let expr = vir_high::Expression::addr_of_no_pos( + encoded_place, + vir_high::Type::pointer(ty), + ); + state.substitute_value(&encoded_lhs, expr); + } mir::Rvalue::ThreadLocalRef(..) - | mir::Rvalue::AddressOf(..) | mir::Rvalue::ShallowInitBox(..) | mir::Rvalue::NullaryOp(..) => { return Err(SpannedEncodingError::unsupported( @@ -653,6 +723,90 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { } match proc_name { + "prusti_contracts::prusti_own" => { + assert_eq!(encoded_args.len(), 1); + let place = encoded_args[0].clone(); + let position = place.position(); + let encoded_rhs = vir_high::Expression::acc_predicate( + vir_high::Predicate::owned_non_aliased(place, position), + position, + ); + subst_with(encoded_rhs) + } + "prusti_contracts::prusti_own_range" => { + assert_eq!(encoded_args.len(), 3); + let address = encoded_args[0].clone(); + let start = encoded_args[1].clone(); + let end = encoded_args[2].clone(); + let position = address.position(); + let encoded_rhs = vir_high::Expression::acc_predicate( + vir_high::Predicate::owned_range(address, start, end, position), + position, + ); + subst_with(encoded_rhs) + } + "prusti_contracts::prusti_deref_own" => { + assert_eq!(encoded_args.len(), 2); + let ref_type = encoded_lhs.get_type().clone(); + builtin((DerefOwn, ref_type)) + } + "prusti_contracts::prusti_raw" => { + assert_eq!(encoded_args.len(), 2); + let address = encoded_args[0].clone(); + let size = encoded_args[1].clone(); + let position = address.position(); + let encoded_rhs = vir_high::Expression::acc_predicate( + vir_high::Predicate::memory_block_heap(address, size, position), + position, + ); + subst_with(encoded_rhs) + } + "prusti_contracts::prusti_raw_range" => { + assert_eq!(encoded_args.len(), 4); + let address = encoded_args[0].clone(); + let size = encoded_args[1].clone(); + let start = encoded_args[2].clone(); + let end = encoded_args[3].clone(); + let position = address.position(); + let encoded_rhs = vir_high::Expression::acc_predicate( + vir_high::Predicate::memory_block_heap_range( + address, size, start, end, position, + ), + position, + ); + subst_with(encoded_rhs) + } + "prusti_contracts::prusti_raw_dealloc" => { + assert_eq!(encoded_args.len(), 2); + let address = encoded_args[0].clone(); + let size = encoded_args[1].clone(); + let position = address.position(); + let encoded_rhs = vir_high::Expression::acc_predicate( + vir_high::Predicate::memory_block_heap_drop(address, size, position), + position, + ); + subst_with(encoded_rhs) + } + "prusti_contracts::prusti_bytes" => { + assert_eq!(encoded_args.len(), 2); + builtin((MemoryBlockBytes, vir_high::Type::MBytes)) + } + "prusti_contracts::read_byte" => { + assert_eq!(encoded_args.len(), 2); + builtin((ReadByte, vir_high::Type::MByte)) + } + "prusti_contracts::prusti_unpacking" => { + assert_eq!(encoded_args.len(), 2); + let place = encoded_args[0].clone(); + let body = encoded_args[1].clone(); + let position = place.position(); + let encoded_rhs = vir_high::Expression::unfolding( + vir_high::Predicate::owned_non_aliased(place, position), + body, + position, + ); + subst_with(encoded_rhs) + } "prusti_contracts::old" => { let argument = encoded_args.last().cloned().unwrap(); let position = argument.position(); @@ -663,6 +817,14 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { ); subst_with(encoded_rhs) } + "prusti_contracts::prusti_eval_in" => { + assert_eq!(encoded_args.len(), 2); + let predicate = encoded_args[0].clone(); + let argument = encoded_args[1].clone(); + let position = argument.position(); + let encoded_rhs = vir_high::Expression::eval_in(predicate, argument, position); + subst_with(encoded_rhs) + } "prusti_contracts::snapshot_equality" => { let position = encoded_args[0].position(); let encoded_rhs = vir_high::Expression::builtin_func_app( @@ -675,8 +837,17 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { subst_with(encoded_rhs) } "prusti_contracts::before_expiry" => { - // self.encode_call_before_expiry()? - unimplemented!(); + assert_eq!(encoded_args.len(), 1); + let position = encoded_args[0].position(); + let ty = encoded_args[0].get_type().clone(); + let encoded_rhs = vir_high::Expression::builtin_func_app( + vir_high::BuiltinFunc::BeforeExpiry, + Vec::new(), + encoded_args.into(), + ty, + position, + ); + subst_with(encoded_rhs) } "std::cmp::PartialEq::eq" | "core::cmp::PartialEq::eq" if self.has_structural_eq_impl(&args[0]).with_span(span)? => @@ -739,6 +910,22 @@ impl<'p, 'v: 'p, 'tcx: 'v> ExpressionBackwardInterpreter<'p, 'v, 'tcx> { .map(Some), } } + "std::ptr::mut_ptr::::is_null" => { + assert_eq!(encoded_args.len(), 1); + builtin((PtrIsNull, vir_high::Type::Bool)) + } + "std::ptr::mut_ptr::::wrapping_offset" => { + assert_eq!(encoded_args.len(), 2); + builtin((PtrWrappingOffset, encoded_args[0].get_type().clone())) + } + "std::mem::size_of" => { + assert_eq!(encoded_args.len(), 0); + builtin((Size, vir_high::Type::Int(vir_high::ty::Int::Usize))) + } + "std::mem::align_of" => { + assert_eq!(encoded_args.len(), 0); + builtin((Align, vir_high::Type::Int(vir_high::ty::Int::Usize))) + } // Prusti-specific syntax // TODO: check we are in a spec function @@ -873,7 +1060,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> BackwardMirInterpreter<'tcx> fn apply_terminator( &self, - _bb: mir::BasicBlock, + bb: mir::BasicBlock, terminator: &mir::Terminator<'tcx>, states: FxHashMap, ) -> Result { @@ -951,6 +1138,18 @@ impl<'p, 'v: 'p, 'tcx: 'v> BackwardMirInterpreter<'tcx> func: mir::Operand::Constant(box mir::Constant { literal, .. }), .. } => { + if self + .specification_blocks + .as_ref() + .map(|sb| sb.is_specification_block(bb)) + .unwrap_or(false) + { + if let Some(target) = target { + return Ok(states[target].clone()); + } else { + unimplemented!(); + } + } self.apply_call_terminator(args, *destination, target, literal.ty(), states, span)? } @@ -1048,6 +1247,15 @@ impl<'p, 'v: 'p, 'tcx: 'v> BackwardMirInterpreter<'tcx> state: &mut Self::State, ) -> Result<(), Self::Error> { trace!("apply_statement {:?}, state: {}", statement, state); + if self + .specification_blocks + .as_ref() + .map(|sb| sb.is_specification_block(bb)) + .unwrap_or(false) + { + trace!("Skipping statement because inside a specification block"); + return Ok(()); + } let span = statement.source_info.span; let location = mir::Location { block: bb, diff --git a/prusti-viper/src/encoder/mir/pure/interpreter/state_high.rs b/prusti-viper/src/encoder/mir/pure/interpreter/state_high.rs index 7cbc01c48e1..e0033f6b73b 100644 --- a/prusti-viper/src/encoder/mir/pure/interpreter/state_high.rs +++ b/prusti-viper/src/encoder/mir/pure/interpreter/state_high.rs @@ -69,9 +69,10 @@ impl ExprBackwardInterpreterState { if let Some(curr_expr) = self.expr.as_mut() { // Replace two times to avoid cloning `expr`, which could be big. let expr = mem::replace(curr_expr, true.into()); - target = target.erase_lifetime(); - replacement = replacement.erase_lifetime(); - let mut new_expr = expr.replace_place(&target, &replacement); //.simplify_addr_of(); + // target = target.erase_lifetime(); + // replacement = replacement.erase_lifetime(); + let new_expr = expr.replace_place(&target, &replacement); //.simplify_addr_of(); + let mut new_expr = new_expr.simplify_out_constructors(); mem::swap(curr_expr, &mut new_expr); } } diff --git a/prusti-viper/src/encoder/mir/pure/pure_functions/cleaner.rs b/prusti-viper/src/encoder/mir/pure/pure_functions/cleaner.rs new file mode 100644 index 00000000000..e8755409dd8 --- /dev/null +++ b/prusti-viper/src/encoder/mir/pure/pure_functions/cleaner.rs @@ -0,0 +1,296 @@ +use crate::encoder::{ + errors::{SpannedEncodingError, SpannedEncodingResult}, + Encoder, +}; +use prusti_interface::data::ProcedureDefId; +use prusti_rustc_interface::span::Span; +use vir_crate::{ + common::{expression::SyntacticEvaluation, position::Positioned}, + high::{ + self as vir_high, + visitors::{ + default_fallible_fold_acc_predicate, default_fallible_fold_binary_op, + default_fallible_fold_builtin_func_app, default_fallible_fold_unfolding, + ExpressionFallibleFolder, + }, + }, +}; + +/// When encoding an assertion we sometimes get strange artefacts as a result of +/// using procedural macros. This functions removes them. +pub(super) fn clean_encoding_result<'p, 'v: 'p, 'tcx: 'v>( + encoder: &'p Encoder<'v, 'tcx>, + expression: vir_high::Expression, + proc_def_id: ProcedureDefId, + span: Span, +) -> SpannedEncodingResult { + let _position = expression.position(); + let mut cleaner = Cleaner { encoder, span }; + + let expression = cleaner.fallible_fold_expression(expression)?; + let expression = expression.simplify(); + check_permission_always_positive(proc_def_id, &expression)?; + + Ok(expression) +} + +struct Cleaner<'p, 'v: 'p, 'tcx: 'v> { + encoder: &'p Encoder<'v, 'tcx>, + span: Span, +} + +fn peel_addr_of(place: vir_high::Expression) -> vir_high::Expression { + match place { + vir_high::Expression::AddrOf(vir_high::AddrOf { base, .. }) => *base, + _ => { + unreachable!("must be addr_of: {}", place) + } + } +} + +fn clean_acc_predicate(predicate: vir_high::Predicate) -> vir_high::Predicate { + match predicate { + vir_high::Predicate::OwnedNonAliased(mut predicate) => { + // FIXME: Rename OwnedNonAliased to Owned. + predicate.place = peel_addr_of(predicate.place); + if !predicate.place.is_behind_pointer_dereference() { + // FIXME: A proper error message + unimplemented!("Must be behind pointer dereference: {}", predicate.place) + } + vir_high::Predicate::OwnedNonAliased(predicate) + } + // vir_high::Predicate::OwnedNonAliased(vir_high::OwnedNonAliased { + // place: vir_high::Expression::AddrOf(vir_high::AddrOf { base, .. }), position + // }) => { + // vir_high::Predicate::owned_non_aliased(*base, position) + // } + vir_high::Predicate::MemoryBlockHeap(mut predicate) => { + predicate.address = peel_addr_of(predicate.address); + if !predicate.address.is_behind_pointer_dereference() { + // FIXME: A proper error message + unimplemented!("Must be behind pointer dereference: {}", predicate.address) + } + vir_high::Predicate::MemoryBlockHeap(predicate) + } + vir_high::Predicate::MemoryBlockHeapRange(mut predicate) => { + predicate.address = peel_addr_of(predicate.address); + vir_high::Predicate::MemoryBlockHeapRange(predicate) + } + vir_high::Predicate::MemoryBlockHeapDrop(mut predicate) => { + predicate.address = peel_addr_of(predicate.address); + if !predicate.address.is_behind_pointer_dereference() { + // FIXME: A proper error message + unimplemented!("Must be behind pointer dereference: {}", predicate.address) + } + vir_high::Predicate::MemoryBlockHeapDrop(predicate) + } + vir_high::Predicate::OwnedRange(mut predicate) => { + predicate.address = peel_addr_of(predicate.address); + vir_high::Predicate::OwnedRange(predicate) + } + _ => unimplemented!("{:?}", predicate), + } +} + +impl<'p, 'v: 'p, 'tcx: 'v> ExpressionFallibleFolder for Cleaner<'p, 'v, 'tcx> { + type Error = SpannedEncodingError; + + fn fallible_fold_acc_predicate( + &mut self, + mut acc_predicate: vir_high::AccPredicate, + ) -> Result { + // let predicate = match *acc_predicate.predicate { + // vir_high::Predicate::OwnedNonAliased(mut predicate) => { + // predicate.place = peel_addr_of(predicate.place); + // vir_high::Predicate::OwnedNonAliased(predicate) + // } + // // vir_high::Predicate::OwnedNonAliased(vir_high::OwnedNonAliased { + // // place: vir_high::Expression::AddrOf(vir_high::AddrOf { base, .. }), position + // // }) => { + // // vir_high::Predicate::owned_non_aliased(*base, position) + // // } + // vir_high::Predicate::MemoryBlockHeap(mut predicate) => { + // predicate.address = peel_addr_of(predicate.address); + // vir_high::Predicate::MemoryBlockHeap(predicate) + // } + // vir_high::Predicate::MemoryBlockHeapDrop(mut predicate) => { + // predicate.address = peel_addr_of(predicate.address); + // vir_high::Predicate::MemoryBlockHeapDrop(predicate) + // } + // _ => unimplemented!("{:?}", acc_predicate), + // }; + let predicate = clean_acc_predicate(*acc_predicate.predicate); + acc_predicate.predicate = Box::new(predicate); + + // if let box vir_high::Expression::AddrOf(vir_high::AddrOf { base, .. }) = acc_predicate.place + // { + // acc_predicate.place = base; + // } else { + // unreachable!("{:?}", acc_predicate); + // }; + default_fallible_fold_acc_predicate(self, acc_predicate) + } + + fn fallible_fold_unfolding( + &mut self, + mut unfolding: vir_high::Unfolding, + ) -> Result { + let predicate = clean_acc_predicate(*unfolding.predicate); + unfolding.predicate = Box::new(predicate); + default_fallible_fold_unfolding(self, unfolding) + } + + fn fallible_fold_conditional_enum( + &mut self, + conditional: vir_high::Conditional, + ) -> Result { + let conditional = self.fallible_fold_conditional(conditional)?; + let expression = match conditional { + _ if conditional.guard.is_true() => *conditional.then_expr, + _ if conditional.guard.is_false() => *conditional.else_expr, + vir_high::Conditional { + guard: + box vir_high::Expression::UnaryOp(vir_high::UnaryOp { + op_kind: vir_high::UnaryOpKind::Not, + argument: guard, + .. + }), + then_expr, + else_expr, + position, + } if then_expr.is_false() || then_expr.is_true() => { + // This happens due to short-circuiting in Rust. + if then_expr.is_false() { + vir_high::Expression::BinaryOp(vir_high::BinaryOp { + op_kind: vir_high::BinaryOpKind::And, + left: guard, + right: else_expr, + position, + }) + } else if then_expr.is_true() { + if !guard.is_pure() { + return Err(SpannedEncodingError::incorrect( + "permission predicates can be only in positive positions", + self.span, + )); + } + vir_high::Expression::BinaryOp(vir_high::BinaryOp { + op_kind: vir_high::BinaryOpKind::Implies, + left: guard, + right: else_expr, + position, + }) + } else { + unreachable!(); + } + } + _ if conditional.else_expr.is_true() => { + // Clean up stuff generated by `own!` expansion. + if !conditional.guard.is_pure() { + unimplemented!("TODO: A proper error message: {conditional}") + } + vir_high::Expression::BinaryOp(vir_high::BinaryOp { + op_kind: vir_high::BinaryOpKind::Implies, + left: conditional.guard, + right: conditional.then_expr, + position: conditional.position, + }) + } + _ if conditional.else_expr.is_false() => { + // Clean up stuff generated by `own!` expansion. + vir_high::Expression::BinaryOp(vir_high::BinaryOp { + op_kind: vir_high::BinaryOpKind::And, + left: conditional.guard, + right: conditional.then_expr, + position: conditional.position, + }) + } + _ => { + if !conditional.guard.is_pure() { + unimplemented!("TODO: A proper error message: {conditional}") + } + return Ok(vir_high::Expression::Conditional(conditional)); + } + }; + // let expression = + // vir_high::Expression::BinaryOp(default_fallible_fold_binary_op(self, binary_op)?); + // let expression = if conditional.else_expr.is_true() { + // let binary_op = ; + // vir_high::Expression::BinaryOp(default_fallible_fold_binary_op(self, binary_op)?) + // } else { + // }; + Ok(expression) + } + + fn fallible_fold_binary_op( + &mut self, + binary_op: vir_high::BinaryOp, + ) -> Result { + if binary_op.op_kind != vir_high::BinaryOpKind::And && !binary_op.left.is_pure() { + unimplemented!("TODO: A proper error message.") + } + if !matches!( + binary_op.op_kind, + vir_high::BinaryOpKind::And | vir_high::BinaryOpKind::Implies + ) && !binary_op.right.is_pure() + { + unimplemented!("TODO: A proper error message.") + } + default_fallible_fold_binary_op(self, binary_op) + } + + fn fallible_fold_builtin_func_app( + &mut self, + mut builtin_func_app: vir_high::BuiltinFuncApp, + ) -> Result { + if matches!( + builtin_func_app.function, + vir_high::BuiltinFunc::MemoryBlockBytes + ) { + let address = builtin_func_app.arguments[0].clone(); + builtin_func_app.arguments[0] = peel_addr_of(address); + } + default_fallible_fold_builtin_func_app(self, builtin_func_app) + } + + fn fallible_fold_quantifier( + &mut self, + quantifier: vir_high::Quantifier, + ) -> Result { + // Quantifier bodies are already cleaned. + Ok(quantifier) + } +} + +fn check_permission_always_positive( + proc_def_id: ProcedureDefId, + expression: &vir_high::Expression, +) -> SpannedEncodingResult<()> { + match expression { + vir_high::Expression::AccPredicate(_) => { + // Accessibility predicate in the positive position. + } + vir_high::Expression::BinaryOp(binary_op_expression) => { + match binary_op_expression.op_kind { + vir_high::BinaryOpKind::And => { + check_permission_always_positive(proc_def_id, &binary_op_expression.left)?; + check_permission_always_positive(proc_def_id, &binary_op_expression.right)?; + } + vir_high::BinaryOpKind::Implies => { + assert!( + binary_op_expression.left.is_pure(), + "{proc_def_id:?} {expression}" + ); + check_permission_always_positive(proc_def_id, &binary_op_expression.right)?; + } + _ => { + assert!(expression.is_pure(), "{proc_def_id:?} {expression}"); + } + } + } + _ => { + assert!(expression.is_pure(), "{proc_def_id:?} {expression}"); + } + } + Ok(()) +} diff --git a/prusti-viper/src/encoder/mir/pure/pure_functions/encoder_high.rs b/prusti-viper/src/encoder/mir/pure/pure_functions/encoder_high.rs index 2daa9e166d1..1d3e4f5fd4b 100644 --- a/prusti-viper/src/encoder/mir/pure/pure_functions/encoder_high.rs +++ b/prusti-viper/src/encoder/mir/pure/pure_functions/encoder_high.rs @@ -98,16 +98,14 @@ pub(super) fn encode_pure_expression<'p, 'v: 'p, 'tcx: 'v>( parent_def_id, substs, ); + let span = encoder.env().query.get_def_span(proc_def_id); let state = run_backward_interpretation(&mir, &interpreter)?.ok_or_else(|| { - SpannedEncodingError::incorrect( - format!("procedure {proc_def_id:?} contains a loop"), - encoder.env().query.get_def_span(proc_def_id), - ) + SpannedEncodingError::incorrect(format!("procedure {proc_def_id:?} contains a loop"), span) })?; let body = state.into_expr().ok_or_else(|| { SpannedEncodingError::internal( format!("failed to encode function's body: {proc_def_id:?}"), - encoder.env().query.get_def_span(proc_def_id), + span, ) })?; debug!( @@ -116,6 +114,7 @@ pub(super) fn encode_pure_expression<'p, 'v: 'p, 'tcx: 'v>( ); // FIXME: Traverse the encoded function and check that all used types are // Copy. Doing this before encoding causes too many false positives. + let body = super::cleaner::clean_encoding_result(encoder, body, proc_def_id, span)?; Ok(body) } diff --git a/prusti-viper/src/encoder/mir/pure/pure_functions/mod.rs b/prusti-viper/src/encoder/mir/pure/pure_functions/mod.rs index ab45f165594..422695dce1d 100644 --- a/prusti-viper/src/encoder/mir/pure/pure_functions/mod.rs +++ b/prusti-viper/src/encoder/mir/pure/pure_functions/mod.rs @@ -6,6 +6,7 @@ //! Encoders of pure functions. +mod cleaner; mod interface; mod encoder_high; mod encoder_poly; diff --git a/prusti-viper/src/encoder/mir/pure/specifications/encoder_high.rs b/prusti-viper/src/encoder/mir/pure/specifications/encoder_high.rs index 5a3a5564be4..3ac97392dba 100644 --- a/prusti-viper/src/encoder/mir/pure/specifications/encoder_high.rs +++ b/prusti-viper/src/encoder/mir/pure/specifications/encoder_high.rs @@ -14,16 +14,13 @@ use crate::encoder::{ mir_encoder::{MirEncoder, PlaceEncoder}, Encoder, }; -use prusti_common::config; + use prusti_rustc_interface::{ hir::def_id::DefId, middle::{ty, ty::subst::SubstsRef}, span::Span, }; -use vir_crate::{ - common::expression::{BinaryOperationHelpers, ExpressionIterator, QuantifierHelpers}, - high as vir_high, -}; +use vir_crate::{common::expression::QuantifierHelpers, high as vir_high}; fn simplify(expression: vir_high::Expression) -> vir_high::Expression { if prusti_common::config::unsafe_core_proof() { @@ -40,11 +37,14 @@ pub(super) fn inline_closure_high<'tcx>( args: Vec, parent_def_id: DefId, substs: SubstsRef<'tcx>, + keep_lifetimes: bool, ) -> SpannedEncodingResult { - let mir = encoder - .env() - .body - .get_closure_body(def_id.expect_local(), substs, parent_def_id); + let mir = encoder.env().body.get_closure_body_lifetimes_opt( + def_id.expect_local(), + substs, + parent_def_id, + keep_lifetimes, + ); assert_eq!(mir.arg_count, args.len() + 1); let mut body_replacements = vec![]; for (arg_idx, arg_local) in mir.args_iter().enumerate() { @@ -55,14 +55,12 @@ pub(super) fn inline_closure_high<'tcx>( } else { args[arg_idx - 1].clone().into() }; - body_replacements.push((local.erase_lifetime(), argument.erase_lifetime())); + body_replacements.push((local, argument)); } - Ok(simplify( - encoder - .encode_pure_expression_high(def_id, parent_def_id, substs)? - .erase_lifetime() - .replace_multiple_places(&body_replacements), - )) + let expression = encoder + .encode_pure_expression_high(def_id, parent_def_id, substs)? + .replace_multiple_places(&body_replacements); + Ok(simplify(expression)) } #[allow(clippy::unnecessary_unwrap)] @@ -75,15 +73,31 @@ pub(super) fn inline_spec_item_high<'tcx>( parent_def_id: DefId, substs: SubstsRef<'tcx>, ) -> SpannedEncodingResult { + assert_eq!( + substs.len(), + encoder.env().query.identity_substs(def_id).len() + ); + let mir = encoder .env() .body - .get_spec_body(def_id, substs, parent_def_id); + .get_expression_body(def_id, substs, parent_def_id); assert_eq!( mir.arg_count, target_args.len() + usize::from(target_return.is_some()), "def_id: {def_id:?}" ); + + // let mir = encoder + // .env() + // .body + // .get_spec_body(def_id, substs, parent_def_id); + // assert_eq!( + // mir.arg_count, + // target_args.len() + if target_return.is_some() { 1 } else { 0 }, + // "def_id: {:?}", + // def_id + // ); let mir_encoder = MirEncoder::new(encoder, &mir, def_id); let mut body_replacements = vec![]; for (arg_idx, arg_local) in mir.args_iter().enumerate() { @@ -107,11 +121,9 @@ pub(super) fn inline_spec_item_high<'tcx>( }, )); } - Ok(simplify( - encoder - .encode_pure_expression_high(def_id, parent_def_id, substs)? - .replace_multiple_places(&body_replacements), - )) + let expression = encoder.encode_pure_expression_high(def_id, parent_def_id, substs)?; + let expression = expression.replace_multiple_places(&body_replacements); + Ok(simplify(expression)) } pub(super) fn encode_quantifier_high<'tcx>( @@ -139,20 +151,21 @@ pub(super) fn encode_quantifier_high<'tcx>( extract_closure_from_ty(encoder.env().query, cl_type_body); let mut encoded_qvars = vec![]; - let mut bounds = vec![]; + // let mut bounds = vec![]; for (arg_idx, arg_ty) in args.into_iter().enumerate() { let qvar_ty = encoder.encode_type_high(arg_ty).unwrap(); let qvar_name = format!("_{}_quant_{}", arg_idx, body_def_id.index.index()); let encoded_qvar = vir_high::VariableDecl::new(qvar_name, qvar_ty); - if config::check_overflows() { - bounds.extend(encoder.encode_type_bounds_high(&encoded_qvar.clone().into(), arg_ty)); - } else if config::encode_unsigned_num_constraint() { - if let ty::TyKind::Uint(_) = arg_ty.kind() { - let expr = - vir_high::Expression::less_equals(0u32.into(), encoded_qvar.clone().into()); - bounds.push(expr); - } - } + // Instead of the bounds we use the snapshot validity function. + // if config::check_overflows() { + // bounds.extend(encoder.encode_type_bounds_high(&encoded_qvar.clone().into(), arg_ty)); + // } else if config::encode_unsigned_num_constraint() { + // if let ty::TyKind::Uint(_) = arg_ty.kind() { + // let expr = + // vir_high::Expression::less_equals(0u32.into(), encoded_qvar.clone().into()); + // bounds.push(expr); + // } + // } encoded_qvars.push(encoded_qvar); } @@ -175,7 +188,7 @@ pub(super) fn encode_quantifier_high<'tcx>( trigger_idx, encoder.encode_type_high(ty_trigger)?, ); - encoded_triggers.push(inline_closure_high( + let encoded_trigger = inline_closure_high( encoder, trigger_def_id, // FIXME: check whether the closure expression does not need to @@ -187,7 +200,9 @@ pub(super) fn encode_quantifier_high<'tcx>( encoded_qvars.clone(), parent_def_id, trigger_substs, - )?); + false, + )?; + encoded_triggers.push(encoded_trigger); } encoded_trigger_sets.push(vir_high::Trigger::new(encoded_triggers)); } @@ -199,28 +214,29 @@ pub(super) fn encode_quantifier_high<'tcx>( encoded_qvars.clone(), parent_def_id, body_substs, + false, )?; // TODO: implement cache-friendly qvar renaming - let final_body = if bounds.is_empty() { - encoded_body - } else if is_exists { - vir_high::Expression::and(bounds.into_iter().conjoin(), encoded_body) - } else { - vir_high::Expression::implies(bounds.into_iter().conjoin(), encoded_body) - }; + // let final_body = if bounds.is_empty() { + // encoded_body + // } else if is_exists { + // vir_high::Expression::and(bounds.into_iter().conjoin(), encoded_body) + // } else { + // vir_high::Expression::implies(bounds.into_iter().conjoin(), encoded_body) + // }; if is_exists { Ok(vir_high::Expression::exists( encoded_qvars, encoded_trigger_sets, - simplify(final_body), + simplify(encoded_body), )) } else { Ok(vir_high::Expression::forall( encoded_qvars, encoded_trigger_sets, - simplify(final_body), + simplify(encoded_body), )) } } diff --git a/prusti-viper/src/encoder/mir/pure/specifications/interface.rs b/prusti-viper/src/encoder/mir/pure/specifications/interface.rs index ff05151e2dd..dacbd173f68 100644 --- a/prusti-viper/src/encoder/mir/pure/specifications/interface.rs +++ b/prusti-viper/src/encoder/mir/pure/specifications/interface.rs @@ -64,6 +64,7 @@ pub(crate) trait SpecificationEncoderInterface<'tcx> { invariant_block: mir::BasicBlock, // in which the invariant is defined parent_def_id: DefId, substs: SubstsRef<'tcx>, + keep_lifetimes: bool, ) -> SpannedEncodingResult; fn encode_prusti_operation( @@ -121,13 +122,13 @@ impl<'v, 'tcx: 'v> SpecificationEncoderInterface<'tcx> for crate::encoder::Encod fn encode_assertion_high( &self, assertion: DefId, - _pre_label: Option<&str>, // TODO: use pre_label (map labels) + pre_label: Option<&str>, target_args: &[vir_high::Expression], target_return: Option<&vir_high::Expression>, parent_def_id: DefId, substs: SubstsRef<'tcx>, ) -> SpannedEncodingResult { - let encoded_assertion = inline_spec_item_high( + let mut encoded_assertion = inline_spec_item_high( self, assertion, target_args, @@ -136,6 +137,18 @@ impl<'v, 'tcx: 'v> SpecificationEncoderInterface<'tcx> for crate::encoder::Encod parent_def_id, substs, )?; + + // map old labels + if let Some(pre_label) = pre_label { + encoded_assertion = encoded_assertion.map_old_expression_label(|label| { + if label == PRECONDITION_LABEL { + pre_label.to_string() + } else { + label + } + }); + } + let position = self .error_manager() .register_span(parent_def_id, self.env().query.get_def_span(assertion)); @@ -148,11 +161,12 @@ impl<'v, 'tcx: 'v> SpecificationEncoderInterface<'tcx> for crate::encoder::Encod invariant_block: mir::BasicBlock, // in which the invariant is defined parent_def_id: DefId, substs: SubstsRef<'tcx>, + keep_lifetimes: bool, ) -> SpannedEncodingResult { - // identify previous block: there should only be one - let predecessors = &mir.basic_blocks.predecessors()[invariant_block]; - assert_eq!(predecessors.len(), 1); - let predecessor = predecessors[0]; + // // identify previous block: there should only be one + // let predecessors = &mir.basic_blocks.predecessors()[invariant_block]; + // assert_eq!(predecessors.len(), 1); + // let predecessor = predecessors[0]; // identify closure aggregate assign (the invariant body) let closure_assigns = mir.basic_blocks[invariant_block] @@ -190,7 +204,8 @@ impl<'v, 'tcx: 'v> SpecificationEncoderInterface<'tcx> for crate::encoder::Encod .encode_place_high(mir, inv_cl_expr, Some(span)) .with_span(span)?; let closure_borrow_type = vir_high::Type::reference( - vir_high::ty::LifetimeConst::erased(), + // vir_high::ty::LifetimeConst::erased(), + vir_high::ty::LifetimeConst::new("fixme_closure_lifetime"), vir_high::ty::Uniqueness::Shared, inv_cl_expr_encoded.get_type().clone(), ); @@ -206,22 +221,23 @@ impl<'v, 'tcx: 'v> SpecificationEncoderInterface<'tcx> for crate::encoder::Encod vec![], parent_def_id, substs, - )? - .erase_lifetime(); + keep_lifetimes, + )?; // backward interpret the body to get rid of the upvars let interpreter = ExpressionBackwardInterpreter::new( self, mir, parent_def_id, - PureEncodingContext::Code, + PureEncodingContext::Assertion, parent_def_id, substs, ); let invariant = run_backward_interpretation_point_to_point( mir, &interpreter, - predecessor, + // predecessor, + invariant_block, invariant_block, inv_loc + 1, // include the closure assign itself crate::encoder::mir::pure::interpreter::state_high::ExprBackwardInterpreterState::new_defined(encoded_invariant), @@ -353,7 +369,7 @@ impl<'v, 'tcx: 'v> SpecificationEncoderInterface<'tcx> for crate::encoder::Encod self, mir, parent_def_id, - PureEncodingContext::Code, + PureEncodingContext::Assertion, parent_def_id, ); let invariant = run_backward_interpretation_point_to_point( diff --git a/prusti-viper/src/encoder/mir/specifications/interface.rs b/prusti-viper/src/encoder/mir/specifications/interface.rs index 87c88c29917..23470b63c43 100644 --- a/prusti-viper/src/encoder/mir/specifications/interface.rs +++ b/prusti-viper/src/encoder/mir/specifications/interface.rs @@ -104,6 +104,12 @@ pub(crate) trait SpecificationsInterface<'tcx> { /// Get the end marker of the ghost block fn get_ghost_end(&self, def_id: DefId) -> Option; + /// Get the prusti specification expression + fn get_prusti_specification_expression( + &self, + def_id: DefId, + ) -> Option; + /// Get the specifications attached to a function. fn get_procedure_specs( &self, @@ -140,6 +146,7 @@ impl<'v, 'tcx: 'v> SpecificationsInterface<'tcx> for super::super::super::Encode || func_name.starts_with("prusti_contracts::prusti_contracts::Seq") || func_name.starts_with("prusti_contracts::prusti_contracts::Ghost") || func_name.starts_with("prusti_contracts::prusti_contracts::Int") + // || func_name.starts_with("prusti_contracts::prusti_contracts::prusti_own") { pure = true; } @@ -257,6 +264,17 @@ impl<'v, 'tcx: 'v> SpecificationsInterface<'tcx> for super::super::super::Encode .cloned() } + fn get_prusti_specification_expression( + &self, + def_id: DefId, + ) -> Option { + self.specifications_state + .specs + .borrow() + .get_specification_expression(&def_id) + .cloned() + } + fn get_procedure_specs( &self, def_id: DefId, diff --git a/prusti-viper/src/encoder/mir/specifications/specs.rs b/prusti-viper/src/encoder/mir/specifications/specs.rs index 20704c31192..5c0ee8ddc7c 100644 --- a/prusti-viper/src/encoder/mir/specifications/specs.rs +++ b/prusti-viper/src/encoder/mir/specifications/specs.rs @@ -11,7 +11,7 @@ use prusti_interface::{ specs::typed::{ DefSpecificationMap, GhostBegin, GhostEnd, LoopSpecification, ProcedureSpecification, ProcedureSpecificationKind, ProcedureSpecificationKindError, PrustiAssertion, - PrustiAssumption, Refinable, SpecificationItem, TypeSpecification, + PrustiAssumption, Refinable, SpecificationExpression, SpecificationItem, TypeSpecification, }, PrustiError, }; @@ -101,6 +101,14 @@ impl<'tcx> Specifications<'tcx> { self.user_typed_specs.get_ghost_end(def_id) } + pub(super) fn get_specification_expression( + &self, + def_id: &DefId, + ) -> Option<&SpecificationExpression> { + trace!("Get specification expression specs of {:?}", def_id); + self.user_typed_specs.get_specification_expression(def_id) + } + pub(super) fn get_and_refine_proc_spec<'a, 'env: 'a>( &'a mut self, env: &'env Environment<'tcx>, diff --git a/prusti-viper/src/encoder/mir/types/encoder.rs b/prusti-viper/src/encoder/mir/types/encoder.rs index 9b9d0f3300b..f2dd34d2e71 100644 --- a/prusti-viper/src/encoder/mir/types/encoder.rs +++ b/prusti-viper/src/encoder/mir/types/encoder.rs @@ -6,9 +6,10 @@ use super::{helpers::compute_discriminant_values, interface::MirTypeEncoderInterface}; use crate::encoder::{ - errors::{EncodingResult, SpannedEncodingError, SpannedEncodingResult, WithSpan}, + errors::{EncodingResult, ErrorCtxt, SpannedEncodingError, SpannedEncodingResult, WithSpan}, mir::{ - constants::ConstantsEncoderInterface, generics::MirGenericsEncoderInterface, + constants::ConstantsEncoderInterface, errors::ErrorInterface, + generics::MirGenericsEncoderInterface, pure::SpecificationEncoderInterface, specifications::SpecificationsInterface, types::helpers::compute_discriminant_ranges, }, Encoder, @@ -47,6 +48,8 @@ impl<'p, 'v, 'r: 'v, 'tcx: 'v> TypeEncoder<'p, 'v, 'tcx> { | "prusti_contracts::Map" | "prusti_contracts::Int" | "prusti_contracts::Ghost" + | "prusti_contracts::Byte" + | "prusti_contracts::Bytes" ) } @@ -62,10 +65,7 @@ impl<'p, 'v, 'r: 'v, 'tcx: 'v> TypeEncoder<'p, 'v, 'tcx> { self.encoder.compute_array_len(size) } - pub fn encode_type( - self, - const_arguments: &[vir::Expression], - ) -> SpannedEncodingResult { + pub fn encode_type(self) -> SpannedEncodingResult { debug!("Encode type '{:?}'", self.ty); // self.encode_polymorphic_predicate_use() let lifetimes = self.encoder.get_lifetimes_from_type_high(self.ty)?; @@ -136,6 +136,10 @@ impl<'p, 'v, 'r: 'v, 'tcx: 'v> TypeEncoder<'p, 'v, 'tcx> { }) } else if type_name == "prusti_contracts::Int" { vir::Type::Int(vir::ty::Int::Unbounded) + } else if type_name == "prusti_contracts::Byte" { + vir::Type::MByte + } else if type_name == "prusti_contracts::Bytes" { + vir::Type::MBytes } else if type_name == "prusti_contracts::Ghost" { (*enc_substs[0]).clone() } else { @@ -190,22 +194,26 @@ impl<'p, 'v, 'r: 'v, 'tcx: 'v> TypeEncoder<'p, 'v, 'tcx> { ty::TyKind::Str => vir::Type::Str, ty::TyKind::Array(elem_ty, size) => { - let (array_len, tail): (_, &[vir::Expression]) = - if let Some((array_len, tail)) = const_arguments.split_first() { - (array_len.clone(), tail) - } else { - let array_len: usize = self - .compute_array_len(*size) - .with_span(self.get_definition_span())? - .try_into() - .unwrap(); - (array_len.into(), &[]) - }; + // let (array_len, tail): (_, &[vir::Expression]) = + // if let Some((array_len, tail)) = const_arguments.split_first() { + // (array_len.clone(), tail) + // } else { + // let array_len: usize = self + // .compute_array_len(*size) + // .with_span(self.get_definition_span())? + // .try_into() + // .unwrap(); + // (array_len.into(), &[]) + // }; + let array_len: usize = self + .compute_array_len(*size) + .with_span(self.get_definition_span())? + .try_into() + .unwrap(); let lifetimes = self.encoder.get_lifetimes_from_type_high(*elem_ty)?; vir::Type::array( - vir::ty::ConstGenericArgument::new(Some(Box::new(array_len))), - self.encoder - .encode_type_high_with_const_arguments(*elem_ty, tail)?, + vir::ty::ConstGenericArgument::new(Some(Box::new(array_len.into()))), + self.encoder.encode_type_high(*elem_ty)?, lifetimes, ) } @@ -339,7 +347,11 @@ impl<'p, 'v, 'r: 'v, 'tcx: 'v> TypeEncoder<'p, 'v, 'tcx> { } } - pub fn encode_type_def_high(self) -> SpannedEncodingResult { + pub fn encode_type_def_high( + self, + ty: &vir::Type, + with_invariant: bool, + ) -> SpannedEncodingResult { debug!("Encode type predicate '{:?}'", self.ty); let type_decl = match self.ty.kind() { ty::TyKind::Bool => vir::TypeDecl::bool(), @@ -443,13 +455,20 @@ impl<'p, 'v, 'r: 'v, 'tcx: 'v> TypeEncoder<'p, 'v, 'tcx> { }), "prusti_contracts::Ghost" => { if let ty::subst::GenericArgKind::Type(ty) = substs[0].unpack() { - Self::new(self.encoder, ty).encode_type_def_high()? + let encoded_type = Self::new(self.encoder, ty).encode_type()?; + Self::new(self.encoder, ty) + .encode_type_def_high(&encoded_type, with_invariant)? } else { unreachable!("no type parameter given for Ghost") } } + "prusti_contracts::Bytes" | "prusti_contracts::Byte" => vir::TypeDecl::trusted( + encode_trusted_name(self.encoder, adt_def.did()), + Vec::new(), + Vec::new(), + ), _ => { - unreachable!(); + unreachable!("unexpected mathematical type: {type_name}"); } } } @@ -463,7 +482,7 @@ impl<'p, 'v, 'r: 'v, 'tcx: 'v> TypeEncoder<'p, 'v, 'tcx> { ) } ty::TyKind::Adt(adt_def, substs) => { - encode_adt_def(self.encoder, *adt_def, substs, None)? + encode_adt_def(self.encoder, ty, *adt_def, substs, None, with_invariant)? } ty::TyKind::Never => vir::TypeDecl::never(), ty::TyKind::Param(param_ty) => { @@ -697,6 +716,8 @@ fn encode_variant<'v, 'tcx: 'v>( name: String, substs: ty::subst::SubstsRef<'tcx>, variant: &ty::VariantDef, + mut structural_invariant: Option>, + def_id: Option, ) -> SpannedEncodingResult { let tcx = encoder.env().tcx(); let mut fields = Vec::new(); @@ -709,18 +730,87 @@ fn encode_variant<'v, 'tcx: 'v>( } let lifetimes = encoder.get_lifetimes_from_substs(substs)?; let const_parameters = encoder.get_const_parameters_from_substs(substs)?; - let variant = vir::type_decl::Struct::new(name, lifetimes, const_parameters, fields); + let position = if let Some(def_id) = def_id { + let span = encoder.env().query.get_def_span(def_id); + let position = encoder + .error_manager() + .register_error(span, ErrorCtxt::TypeInvariantDefinition, def_id) + .into(); + if let Some(structural_invariant) = &mut structural_invariant { + for expression in std::mem::take(structural_invariant) { + structural_invariant.push(encoder.set_surrounding_error_context_for_expression( + expression, + position, + ErrorCtxt::TypeInvariantDefinition, + )); + } + } + position + } else { + Default::default() + }; + let variant = vir::type_decl::Struct::new_with_pos( + name, + lifetimes, + const_parameters, + structural_invariant, + fields, + position, + ); Ok(variant) } +fn encode_structural_invariant<'v, 'tcx: 'v>( + encoder: &Encoder<'v, 'tcx>, + ty: &vir::Type, + substs: ty::subst::SubstsRef<'tcx>, + did: DefId, +) -> SpannedEncodingResult>> { + let invariant = if let Some(specs) = encoder.get_type_specs(did) { + match &specs.structural_invariant { + prusti_interface::specs::typed::SpecificationItem::Empty => None, + prusti_interface::specs::typed::SpecificationItem::Inherent(invs) => { + Some( + invs.iter() + .map(|inherent_def_id| { + encoder.encode_assertion_high( + *inherent_def_id, + None, + &[vir::Expression::self_variable(ty.clone())], + None, + // true, + *inherent_def_id, + substs, + ) + }) + .collect::, _>>()?, + ) + } + _ => todo!(), + // TODO(inv): handle invariant inheritance + } + } else { + None + }; + Ok(invariant) +} + +/// `with_invariant` is used to break infinite recursion. pub(super) fn encode_adt_def<'v, 'tcx>( encoder: &Encoder<'v, 'tcx>, + ty: &vir::Type, adt_def: ty::AdtDef<'tcx>, substs: ty::subst::SubstsRef<'tcx>, variant_index: Option, + with_invariant: bool, ) -> SpannedEncodingResult { let lifetimes = encoder.get_lifetimes_from_substs(substs)?; let const_parameters = encoder.get_const_parameters_from_substs(substs)?; + let structural_invariant = if with_invariant { + encode_structural_invariant(encoder, ty, substs, adt_def.did())? + } else { + None + }; let tcx = encoder.env().tcx(); if adt_def.is_box() { debug!("ADT {:?} is a box", adt_def); @@ -731,7 +821,9 @@ pub(super) fn encode_adt_def<'v, 'tcx>( encode_box_name(), lifetimes, const_parameters, + structural_invariant, vec![field], + Default::default(), )) } else if adt_def.is_struct() { debug!("ADT {:?} is a struct", adt_def); @@ -739,10 +831,21 @@ pub(super) fn encode_adt_def<'v, 'tcx>( let name = encode_struct_name(encoder, adt_def.did()); let variant = adt_def.non_enum_variant(); Ok(vir::TypeDecl::Struct(encode_variant( - encoder, name, substs, variant, + encoder, + name, + substs, + variant, + structural_invariant, + Some(adt_def.did()), )?)) } else if adt_def.is_union() { debug!("ADT {:?} is a union", adt_def); + if structural_invariant.is_some() { + return Err(SpannedEncodingError::unsupported( + "Structural invariants are not supported on unions", + encoder.env().query.get_def_span(adt_def.did()), + )); + } if !config::unsafe_core_proof() { return Err(SpannedEncodingError::unsupported( "unions are not supported", @@ -766,6 +869,7 @@ pub(super) fn encode_adt_def<'v, 'tcx>( field_name, lifetimes.clone(), const_parameters.clone(), + None, vec![encoded_field], ); variants.push(variant); @@ -782,6 +886,12 @@ pub(super) fn encode_adt_def<'v, 'tcx>( )) } else if adt_def.is_enum() { debug!("ADT {:?} is an enum", adt_def); + if structural_invariant.is_some() { + return Err(SpannedEncodingError::unsupported( + "Structural invariants are not supported on enums", + encoder.env().query.get_def_span(adt_def.did()), + )); + } let name = encode_enum_name(encoder, adt_def.did()); let num_variants = adt_def.variants().len(); debug!("ADT {:?} is enum with {} variants", adt_def, num_variants); @@ -789,7 +899,14 @@ pub(super) fn encode_adt_def<'v, 'tcx>( // FIXME: Currently fold-unfold assumes that everything that // has only a single variant is a struct. let variant = &adt_def.variants()[0usize.into()]; - vir::TypeDecl::Struct(encode_variant(encoder, name, substs, variant)?) + vir::TypeDecl::Struct(encode_variant( + encoder, + name, + substs, + variant, + None, + Default::default(), + )?) } else if let Some(_variant_index) = variant_index { // let variant = &adt_def.variants()[variant_index]; // vir::TypeDecl::Struct(encode_variant(encoder, name, substs, variant)?) @@ -802,7 +919,8 @@ pub(super) fn encode_adt_def<'v, 'tcx>( let mut variants = Vec::new(); for variant in adt_def.variants() { let name = variant.ident(tcx).to_string(); - let encoded_variant = encode_variant(encoder, name, substs, variant)?; + let encoded_variant = + encode_variant(encoder, name, substs, variant, None, Default::default())?; variants.push(encoded_variant); } let mir_discriminant_type = match adt_def.repr().discr_type() { diff --git a/prusti-viper/src/encoder/mir/types/interface.rs b/prusti-viper/src/encoder/mir/types/interface.rs index 982ed6a6259..5ee499012c5 100644 --- a/prusti-viper/src/encoder/mir/types/interface.rs +++ b/prusti-viper/src/encoder/mir/types/interface.rs @@ -11,7 +11,7 @@ use prusti_rustc_interface::{ }; use rustc_hash::FxHashMap; use std::cell::RefCell; -use vir_crate::{common::expression::less_equals, high as vir_high, polymorphic as vir}; +use vir_crate::{common::{expression::less_equals, builtin_constants::DISCRIMINANT_FIELD_NAME}, high as vir_high, polymorphic as vir}; #[derive(Default)] pub(crate) struct MirTypeEncoderState<'tcx> { @@ -55,11 +55,11 @@ pub(crate) trait MirTypeEncoderInterface<'tcx> { ty: ty::Ty<'tcx>, ) -> SpannedEncodingResult>; fn encode_type_high(&self, ty: ty::Ty<'tcx>) -> SpannedEncodingResult; - fn encode_type_high_with_const_arguments( - &self, - ty: ty::Ty<'tcx>, - const_arguments: &[vir_high::Expression], - ) -> SpannedEncodingResult; + // fn encode_type_high_with_const_arguments( + // &self, + // ty: ty::Ty<'tcx>, + // const_arguments: &[vir_high::Expression], + // ) -> SpannedEncodingResult; fn encode_place_type_high(&self, ty: mir::tcx::PlaceTy<'tcx>) -> EncodingResult; fn encode_enum_variant_index_high( @@ -76,13 +76,14 @@ pub(crate) trait MirTypeEncoderInterface<'tcx> { fn encode_type_def_high( &self, ty: &vir_high::Type, + with_invariant: bool, ) -> SpannedEncodingResult; - fn encode_adt_def( - &self, - adt_def: ty::AdtDef<'tcx>, - substs: ty::subst::SubstsRef<'tcx>, - variant_index: Option, - ) -> SpannedEncodingResult; + // fn encode_adt_def( + // &self, + // adt_def: ty::AdtDef<'tcx>, + // substs: ty::subst::SubstsRef<'tcx>, + // variant_index: Option, + // ) -> SpannedEncodingResult; fn encode_type_bounds_high( &self, var: &vir_high::Expression, @@ -113,7 +114,7 @@ impl<'v, 'tcx: 'v> MirTypeEncoderInterface<'tcx> for super::super::super::Encode vir::Field::new(name, vir::Type::typed_ref("")) } fn encode_discriminant_field(&self) -> vir::Field { - let name = "discriminant"; + let name = DISCRIMINANT_FIELD_NAME; vir::Field::new(name, vir::Type::Int) } fn encode_field( @@ -123,7 +124,7 @@ impl<'v, 'tcx: 'v> MirTypeEncoderInterface<'tcx> for super::super::super::Encode use_span: Option, declaration_span: Span, ) -> SpannedEncodingResult { - let type_decl = self.encode_type_def_high(ty)?; + let type_decl = self.encode_type_def_high(ty, false)?; let primary_span = if let Some(use_span) = use_span { use_span } else { @@ -223,15 +224,6 @@ impl<'v, 'tcx: 'v> MirTypeEncoderInterface<'tcx> for super::super::super::Encode Ok(const_parameters) } fn encode_type_high(&self, ty: ty::Ty<'tcx>) -> SpannedEncodingResult { - // FIXME: Remove encode_type_high_with_const_arguments because it is a - // failed attempt. - self.encode_type_high_with_const_arguments(ty, &[]) - } - fn encode_type_high_with_const_arguments( - &self, - ty: ty::Ty<'tcx>, - const_arguments: &[vir_high::Expression], - ) -> SpannedEncodingResult { if !self .mir_type_encoder_state .encoded_types @@ -239,7 +231,7 @@ impl<'v, 'tcx: 'v> MirTypeEncoderInterface<'tcx> for super::super::super::Encode .contains_key(ty.kind()) { let type_encoder = TypeEncoder::new(self, ty); - let encoded_type = type_encoder.encode_type(const_arguments)?; + let encoded_type = type_encoder.encode_type()?; assert!(self .mir_type_encoder_state .encoded_types @@ -254,11 +246,11 @@ impl<'v, 'tcx: 'v> MirTypeEncoderInterface<'tcx> for super::super::super::Encode .encoded_types_inverse .borrow_mut() .insert(encoded_type.clone(), ty); - let encoded_type = encoded_type.erase_lifetimes().erase_const_generics(); - self.mir_type_encoder_state - .encoded_types_inverse - .borrow_mut() - .insert(encoded_type, ty); + // let encoded_type = encoded_type.erase_lifetimes().erase_const_generics(); + // self.mir_type_encoder_state + // .encoded_types_inverse + // .borrow_mut() + // .insert(encoded_type, ty); } let encoded_type = self.mir_type_encoder_state.encoded_types.borrow()[ty.kind()].clone(); Ok(encoded_type) @@ -342,6 +334,7 @@ impl<'v, 'tcx: 'v> MirTypeEncoderInterface<'tcx> for super::super::super::Encode fn encode_type_def_high( &self, ty: &vir_high::Type, + with_invariant: bool, ) -> SpannedEncodingResult { if !self .mir_type_encoder_state @@ -357,12 +350,15 @@ impl<'v, 'tcx: 'v> MirTypeEncoderInterface<'tcx> for super::super::super::Encode lifetimes, }) => { let encoded_enum = self - .encode_type_def_high(&vir_high::Type::enum_( - name.clone(), - arguments.clone(), - None, - lifetimes.clone(), - ))? + .encode_type_def_high( + &vir_high::Type::enum_( + name.clone(), + arguments.clone(), + None, + lifetimes.clone(), + ), + with_invariant, + )? .unwrap_enum(); vir_high::TypeDecl::Struct(encoded_enum.into_variant(&variant.index).unwrap()) } @@ -373,37 +369,45 @@ impl<'v, 'tcx: 'v> MirTypeEncoderInterface<'tcx> for super::super::super::Encode lifetimes, }) => { let encoded_union = self - .encode_type_def_high(&vir_high::Type::union_( - name.clone(), - arguments.clone(), - None, - lifetimes.clone(), - ))? + .encode_type_def_high( + &vir_high::Type::union_( + name.clone(), + arguments.clone(), + None, + lifetimes.clone(), + ), + with_invariant, + )? .unwrap_union(); vir_high::TypeDecl::Struct(encoded_union.into_variant(&variant.index).unwrap()) } _ => { let original_ty = self.decode_type_high(ty); let type_encoder = TypeEncoder::new(self, original_ty); - type_encoder.encode_type_def_high()? + type_encoder.encode_type_def_high(ty, with_invariant)? } }; - self.mir_type_encoder_state - .encoded_type_decls - .borrow_mut() - .insert(ty.clone(), encoded_type); + if with_invariant { + // Cache only the fully encoded version. + self.mir_type_encoder_state + .encoded_type_decls + .borrow_mut() + .insert(ty.clone(), encoded_type); + } else { + return Ok(encoded_type); + } } let encoded_type = self.mir_type_encoder_state.encoded_type_decls.borrow()[ty].clone(); Ok(encoded_type) } - fn encode_adt_def( - &self, - adt_def: ty::AdtDef<'tcx>, - substs: ty::subst::SubstsRef<'tcx>, - variant_index: Option, - ) -> SpannedEncodingResult { - super::encoder::encode_adt_def(self, adt_def, substs, variant_index) - } + // fn encode_adt_def( + // &self, + // adt_def: ty::AdtDef<'tcx>, + // substs: ty::subst::SubstsRef<'tcx>, + // variant_index: Option, + // ) -> SpannedEncodingResult { + // super::encoder::encode_adt_def(self, ty, adt_def, substs, variant_index) + // } fn encode_type_bounds_high( &self, var: &vir_high::Expression, diff --git a/prusti-viper/src/encoder/mirror_function_encoder.rs b/prusti-viper/src/encoder/mirror_function_encoder.rs index e04ccb15bfd..57ea6532ad9 100644 --- a/prusti-viper/src/encoder/mirror_function_encoder.rs +++ b/prusti-viper/src/encoder/mirror_function_encoder.rs @@ -78,7 +78,7 @@ impl MirrorEncoder { function.posts.push(vir::Expr::InhaleExhale( vir::InhaleExhale { inhale_expr: box vir::Expr::eq_cmp( vir::Expr::local( - vir::LocalVar::new("__result", function.return_type.clone()), + vir::LocalVar::new(vir_crate::common::builtin_constants::RESULT_VARIABLE_NAME, function.return_type.clone()), ), vir::Expr::domain_func_app( mirror_func.clone(), diff --git a/prusti-viper/src/encoder/procedure_encoder.rs b/prusti-viper/src/encoder/procedure_encoder.rs index b4c87b76540..8a6ca28ec53 100644 --- a/prusti-viper/src/encoder/procedure_encoder.rs +++ b/prusti-viper/src/encoder/procedure_encoder.rs @@ -149,7 +149,7 @@ impl<'p, 'v: 'p, 'tcx: 'v> ProcedureEncoder<'p, 'v, 'tcx> { let init_info = InitInfo::new(mir, tcx, proc_def_id, &mir_encoder) .with_default_span(procedure.get_span())?; - let specification_blocks = SpecificationBlocks::build(encoder.env().query, mir, procedure, false); + let specification_blocks = SpecificationBlocks::build(encoder.env().query, mir, None, false); let cfg_method = vir::CfgMethod::new( // method name diff --git a/prusti-viper/src/encoder/purifier.rs b/prusti-viper/src/encoder/purifier.rs index 58a9dc8769b..dd6357c6a77 100644 --- a/prusti-viper/src/encoder/purifier.rs +++ b/prusti-viper/src/encoder/purifier.rs @@ -4,7 +4,7 @@ // represent types like "snapshot of X". Resolve SnapOf in snapshot patcher. use rustc_hash::{FxHashMap, FxHashSet}; -use vir_crate::polymorphic::{self as vir, ExprFolder, ExprWalker, StmtFolder, StmtWalker}; +use vir_crate::{polymorphic::{self as vir, ExprFolder, ExprWalker, StmtFolder, StmtWalker}, common::builtin_constants::DISCRIMINANT_VARIABLE_NAME}; use crate::encoder::{high::types::HighTypeEncoderInterface, Encoder}; use log::{debug, trace}; @@ -495,7 +495,7 @@ impl ExprFolder for Purifier<'_, '_, '_> { position: *local_pos, }); let discriminant_func = vir::DomainFunc { - name: "discriminant$".to_string(), + name: DISCRIMINANT_VARIABLE_NAME.to_string(), type_arguments, formal_args: vec![local_var.clone()], return_type: vir::Type::Int, diff --git a/prusti-viper/src/encoder/snapshot/encoder.rs b/prusti-viper/src/encoder/snapshot/encoder.rs index 6cddc14b7ba..b08607accc1 100644 --- a/prusti-viper/src/encoder/snapshot/encoder.rs +++ b/prusti-viper/src/encoder/snapshot/encoder.rs @@ -27,7 +27,7 @@ use prusti_rustc_interface::{ use rustc_hash::FxHashMap; use std::rc::Rc; use vir_crate::{ - common::identifier::WithIdentifier, + common::{identifier::WithIdentifier, builtin_constants::{DISCRIMINANT_VARIABLE_NAME, DISCRIMINANT_FIELD_NAME}}, polymorphic as vir, polymorphic::{ ContainerOpKind, Expr, ExprIterator, FallibleExprFolder, FallibleStmtFolder, @@ -314,7 +314,7 @@ impl SnapshotEncoder { let snapshot = self.decode_snapshot(encoder, expr.get_type())?; match (field.name.as_str(), snapshot) { ( - "discriminant", + DISCRIMINANT_FIELD_NAME, Snapshot::Complex { discriminant_func, .. }, @@ -1643,7 +1643,7 @@ impl SnapshotEncoder { // encode discriminant function let discriminant_func = vir::DomainFunc { - name: "discriminant$".to_string(), + name: DISCRIMINANT_VARIABLE_NAME.to_string(), type_arguments: vec![snapshot_type.clone()], formal_args: vec![arg_dom_local.clone()], return_type: Type::Int, diff --git a/prusti-viper/src/encoder/typed/to_middle/expression.rs b/prusti-viper/src/encoder/typed/to_middle/expression.rs index d75408523f3..fa9f7051fc0 100644 --- a/prusti-viper/src/encoder/typed/to_middle/expression.rs +++ b/prusti-viper/src/encoder/typed/to_middle/expression.rs @@ -1,7 +1,9 @@ use crate::encoder::errors::SpannedEncodingError; use vir_crate::{ middle as vir_mid, - middle::operations::{TypedToMiddleExpressionLowerer, TypedToMiddleType}, + middle::operations::{ + TypedToMiddleExpressionLowerer, TypedToMiddlePredicate, TypedToMiddleType, + }, typed as vir_typed, }; @@ -65,4 +67,11 @@ impl<'v, 'tcx> TypedToMiddleExpressionLowerer for crate::encoder::Encoder<'v, 't index: variant_index.index, }) } + + fn typed_to_middle_expression_predicate( + &self, + predicate: vir_typed::Predicate, + ) -> Result { + predicate.typed_to_middle_predicate(self) + } } diff --git a/prusti-viper/src/encoder/typed/to_middle/statement.rs b/prusti-viper/src/encoder/typed/to_middle/statement.rs index 052d9ca9b73..b66cee18c04 100644 --- a/prusti-viper/src/encoder/typed/to_middle/statement.rs +++ b/prusti-viper/src/encoder/typed/to_middle/statement.rs @@ -110,4 +110,57 @@ impl<'v, 'tcx> TypedToMiddleStatementLowerer for crate::encoder::Encoder<'v, 'tc ) -> Result { unreachable!("ObtainMutRef statement cannot be lowered"); } + + fn typed_to_middle_statement_statement_unpack( + &self, + _statement: vir_typed::Unpack, + ) -> Result { + unreachable!("Unpack statement cannot be lowered"); + } + + fn typed_to_middle_statement_statement_pack( + &self, + _statement: vir_typed::Pack, + ) -> Result { + unreachable!("Pack statement cannot be lowered"); + } + + fn typed_to_middle_statement_statement_forget_initialization( + &self, + _statement: vir_typed::ForgetInitialization, + ) -> Result { + unreachable!("ForgetInitialization statement cannot be lowered"); + } + + fn typed_to_middle_statement_statement_split( + &self, + _statement: vir_typed::Split, + ) -> Result { + unreachable!("Split statement cannot be lowered"); + } + + fn typed_to_middle_statement_statement_join( + &self, + _statement: vir_typed::Join, + ) -> Result { + unreachable!("Join statement cannot be lowered"); + } + + fn typed_to_middle_statement_dead_reference( + &self, + statement: vir_typed::DeadReference, + ) -> Result { + Ok(vir_mid::statement::DeadReference { + target: statement.target.typed_to_middle_expression(self)?, + condition: None, + position: statement.position, + }) + } + + // fn typed_to_middle_statement_statement_restore( + // &self, + // _statement: vir_typed::Restore, + // ) -> Result { + // unreachable!("Restore statement cannot be lowered"); + // } } diff --git a/prusti-viper/src/encoder/typed/to_middle/type_decl.rs b/prusti-viper/src/encoder/typed/to_middle/type_decl.rs index b60bb88f41c..2895a20c736 100644 --- a/prusti-viper/src/encoder/typed/to_middle/type_decl.rs +++ b/prusti-viper/src/encoder/typed/to_middle/type_decl.rs @@ -75,4 +75,11 @@ impl<'v, 'tcx> TypedToMiddleTypeDeclLowerer for crate::encoder::Encoder<'v, 'tcx vir_typed::ty::EnumSafety::Union => vir_mid::ty::EnumSafety::Union, }) } + + fn typed_to_middle_type_decl_position( + &self, + position: vir_typed::Position, + ) -> Result { + Ok(position) + } } diff --git a/prusti-viper/src/verifier.rs b/prusti-viper/src/verifier.rs index be34575f012..84d354fcd50 100644 --- a/prusti-viper/src/verifier.rs +++ b/prusti-viper/src/verifier.rs @@ -231,7 +231,7 @@ fn verify_programs( let check_mode = program.get_check_mode(); // Prepend the Rust file name to the program. program.set_name(format!("{rust_program_name}_{program_name}")); - let backend = if check_mode == CheckMode::Specifications { + let backend = if check_mode == CheckMode::PurificationFunctional { config::verify_specifications_backend() } else { config::viper_backend() diff --git a/prusti/src/callbacks.rs b/prusti/src/callbacks.rs index 69a9c7d7e95..92b357ad4c7 100644 --- a/prusti/src/callbacks.rs +++ b/prusti/src/callbacks.rs @@ -25,7 +25,7 @@ pub struct PrustiCompilerCalls; #[allow(clippy::needless_lifetimes)] fn mir_borrowck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> mir_borrowck<'tcx> { // *Don't take MIR bodies with borrowck info if we won't need them* - if !is_spec_fn(tcx, def_id.to_def_id()) { + if !is_spec_fn(tcx, def_id.to_def_id()) || config::unsafe_core_proof() { let body_with_facts = prusti_rustc_interface::borrowck::consumers::get_body_with_borrowck_facts( tcx, diff --git a/smt-log-analyzer/src/lib.rs b/smt-log-analyzer/src/lib.rs index 2c8e8e6bcf2..adc96ee9de6 100644 --- a/smt-log-analyzer/src/lib.rs +++ b/smt-log-analyzer/src/lib.rs @@ -34,7 +34,9 @@ pub struct Settings { fn process_line(settings: &Settings, state: &mut State, line: &str) -> Result<(), Error> { let mut parser = Parser::from_line(line); - match parser.parse_event_kind()? { + let event_kind = parser.parse_event_kind()?; + state.register_event_kind(event_kind); + match event_kind { EventKind::Pop => { let scopes_to_pop = parser.parse_number()?; let active_scopes_count = parser.parse_number()?; @@ -129,7 +131,13 @@ fn process_line(settings: &Settings, state: &mut State, line: &str) -> Result<() EventKind::Instance => { state.register_instance()?; } - EventKind::Unrecognized => {} + EventKind::DecideAndOr => { + let term_id = parser.parse_id()?; + let undef_child_id = parser.parse_id()?; + // FIXME: This information seems to be useless. + state.register_decide_and_or_term(term_id, undef_child_id); + } + _ => {} } Ok(()) } diff --git a/smt-log-analyzer/src/parser.rs b/smt-log-analyzer/src/parser.rs index 0e25e207ee5..cd8e15b2b1a 100644 --- a/smt-log-analyzer/src/parser.rs +++ b/smt-log-analyzer/src/parser.rs @@ -4,6 +4,7 @@ use crate::{ }; use std::str::CharIndices; +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub(crate) enum EventKind { Pop, Push, @@ -15,6 +16,20 @@ pub(crate) enum EventKind { Unrecognized, AttachMeaning, MkVar, + ToolVersion, + AttachVarNames, + MkProof, + AttachEnode, + EndOfInstance, + MkLambda, + BeginCheck, + Assign, + EqExpl, + DecideAndOr, + ResolveLit, + ResolveProcess, + Conflict, + Eof, } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -137,11 +152,20 @@ impl<'a> Parser<'a> { "inst-discovered" => EventKind::InstDiscovered, "instance" => EventKind::Instance, "attach-meaning" => EventKind::AttachMeaning, - "tool-version" | "attach-var-names" | "mk-proof" | "attach-enode" - | "end-of-instance" | "mk-lambda" | "begin-check" | "assign" | "eq-expl" - | "decide-and-or" | "resolve-lit" | "resolve-process" | "conflict" | "eof" => { - EventKind::Unrecognized - } + "tool-version" => EventKind::ToolVersion, + "attach-var-names" => EventKind::AttachVarNames, + "mk-proof" => EventKind::MkProof, + "attach-enode" => EventKind::AttachEnode, + "end-of-instance" => EventKind::EndOfInstance, + "mk-lambda" => EventKind::MkLambda, + "begin-check" => EventKind::BeginCheck, + "assign" => EventKind::Assign, + "eq-expl" => EventKind::EqExpl, + "decide-and-or" => EventKind::DecideAndOr, + "resolve-lit" => EventKind::ResolveLit, + "resolve-process" => EventKind::ResolveProcess, + "conflict" => EventKind::Conflict, + "eof" => EventKind::Eof, x => unimplemented!("got: {:?}", x), }; self.consume(']')?; diff --git a/smt-log-analyzer/src/state.rs b/smt-log-analyzer/src/state.rs index c519d1ca99e..b7c13884900 100644 --- a/smt-log-analyzer/src/state.rs +++ b/smt-log-analyzer/src/state.rs @@ -2,7 +2,7 @@ use csv::Writer; use crate::{ error::Error, - parser::TheoryKind, + parser::{EventKind, TheoryKind}, types::{Level, QuantifierId, TermId, BUILTIN_QUANTIFIER_ID}, }; use rustc_hash::{FxHashMap, FxHashSet}; @@ -84,6 +84,8 @@ struct LargestPop { pub(crate) struct State { quantifiers: FxHashMap, terms: FxHashMap, + /// Frequencies of each event kind. + event_kind_counters: HashMap, /// The currently matched quantifiers (via [new-match]) at a given level. quantifiers_matched_events: FxHashMap>, /// The currently discovered quantifiers (via [inst-discovered]) at a given level. @@ -116,9 +118,15 @@ pub(crate) struct State { current_active_scopes_count: Level, traced_quantifier: Option, traced_quantifier_triggers: Option, + decide_and_or_terms: Vec<(TermId, String, TermId, String)>, } impl State { + pub(crate) fn register_event_kind(&mut self, event_kind: EventKind) { + let entry = self.event_kind_counters.entry(event_kind).or_insert(0); + *entry += 1; + } + pub(crate) fn register_label(&mut self, label: String) { self.trace.push(BasicBlockVisitedEvent { level: self.current_active_scopes_count, @@ -298,6 +306,20 @@ impl State { .insert(term_id, Term::AttachMeaning { ident, value }); } + pub(crate) fn register_decide_and_or_term(&mut self, term_id: TermId, undef_child_id: TermId) { + let mut rendered_term = String::new(); + self.render_term(term_id, &mut rendered_term, 30).unwrap(); + let mut rendered_undef_child = String::new(); + self.render_term(undef_child_id, &mut rendered_undef_child, 30) + .unwrap(); + self.decide_and_or_terms.push(( + term_id, + rendered_term, + undef_child_id, + rendered_undef_child, + )); + } + pub(crate) fn active_scopes_count(&self) -> Level { self.current_active_scopes_count } @@ -450,6 +472,16 @@ impl State { } pub(crate) fn write_statistics(&self, input_file: &str) { + { + let mut writer = Writer::from_path(format!("{input_file}.event-kinds.csv")).unwrap(); + writer.write_record(["Event Kind", "Count"]).unwrap(); + for (event_kind, counter) in &self.event_kind_counters { + writer + .write_record([format!("{event_kind:?}"), counter.to_string()]) + .unwrap(); + } + } + { // [instance] – the number of quantifier instantiations. let mut writer = Writer::from_path(format!("{input_file}.instances.csv")).unwrap(); @@ -612,6 +644,29 @@ impl State { } } + { + // [decide-and-or] – Case splits. + let mut writer = Writer::from_path(format!("{input_file}.decide-and-or.csv")).unwrap(); + writer + .write_record([ + "TermId", + "Rendered Term", + "Undef child ID", + "Rendered undef child", + ]) + .unwrap(); + for (term_id, rendered_term, child_id, rendered_child) in &self.decide_and_or_terms { + writer + .write_record([ + &term_id.to_string(), + rendered_term, + &child_id.to_string(), + rendered_child, + ]) + .unwrap(); + } + } + { println!( "The largest number of quantifier matches removed in a single “pop {}” operation: {}", diff --git a/vir-gen/src/deriver/lower.rs b/vir-gen/src/deriver/lower.rs index dcb97a15035..6437f06c59f 100644 --- a/vir-gen/src/deriver/lower.rs +++ b/vir-gen/src/deriver/lower.rs @@ -554,6 +554,20 @@ impl<'a> Deriver<'a> { ty.map(|element| self.#inner_method_name(*element).map(Box::new)).transpose() } } + } else if container_ident_first == "Option" && container_ident_second == "Vec" { + let inner_method_name = self.encode_name(inner_ident); + parse_quote! { + fn #method_name( + #self_parameter, + ty: #container_ident_first < #container_ident_second < #parameter_type > > + ) -> Result< #container_ident_first < #container_ident_second < #return_type > >, Self::Error> { + ty.map(|elements| + elements.into_iter().map(|element| { + self.#inner_method_name(element) + }).collect() + ).transpose() + } + } } else { unimplemented!( "first: {} second: {}", diff --git a/vir-gen/src/helpers.rs b/vir-gen/src/helpers.rs index 8e2196ca8cf..ea8ce74267b 100644 --- a/vir-gen/src/helpers.rs +++ b/vir-gen/src/helpers.rs @@ -32,7 +32,7 @@ pub fn prefixed_method_name_from_camel_raw(prefix: &str, ident: &syn::Ident) -> } } match new_ident.as_ref() { - "struct" | "enum" | "union" | "type" | "ref" | "move" => { + "struct" | "enum" | "union" | "type" | "ref" | "move" | "final" => { new_ident.push('_'); new_ident } diff --git a/vir/defs/high/ast/expression.rs b/vir/defs/high/ast/expression.rs index fb685c77844..5ceca647b46 100644 --- a/vir/defs/high/ast/expression.rs +++ b/vir/defs/high/ast/expression.rs @@ -1,6 +1,7 @@ pub(crate) use super::{ field::FieldDecl, position::Position, + predicate::Predicate, ty::{Type, VariantIndex}, variable::VariableDecl, }; @@ -21,6 +22,8 @@ pub enum Expression { Field(Field), /// A reference or pointer dereference. (Sometimes can fail.) Deref(Deref), + /// A reference or pointer dereference. (Sometimes can fail.) + Final(Final), /// The inverse of Deref. AddrOf(AddrOf), LabelledOld(LabelledOld), @@ -45,6 +48,12 @@ pub enum Expression { /// * field that encodes the variant // FIXME: Is downcast really needed? Isn't variant enough? Downcast(Downcast), + /// An accessibility predicate such as `own`. + AccPredicate(AccPredicate), + /// An unpacking of an accessibility predicate. + Unfolding(Unfolding), + /// `eval_in(predicate, argument)` expression. + EvalIn(EvalIn), } #[display(fmt = "{}", "variable.name")] @@ -85,6 +94,13 @@ pub struct Deref { pub position: Position, } +#[display(fmt = "{}.^", base)] +pub struct Final { + pub base: Box, + pub ty: Type, + pub position: Position, +} + #[display(fmt = "{}.&", base)] pub struct AddrOf { pub base: Box, @@ -233,6 +249,7 @@ pub enum BuiltinFunc { SnapshotEquality, Size, PaddingSize, + Align, Discriminant, LifetimeIncluded, LifetimeIntersect, @@ -249,6 +266,23 @@ pub enum BuiltinFunc { NewInt, Index, Len, + PtrWrappingOffset, + PtrIsNull, + IsValid, // TODO: Delete. + EnsureOwnedPredicate, + // GetSnapshot, + /// Take the inner-most lifetime of a place. + TakeLifetime, + /// Read a single Byte from a sequence of Bytes. + ReadByte, + /// Retrieve the bytes of a memory block. + MemoryBlockBytes, + /// Dereference a raw pointer at a given index. + DerefOwn, + /// Cast `*mut T` to `*const T`. + CastMutToConstPointer, + BeforeExpiry, + AfterExpiry, } #[display(fmt = "__builtin__{}({})", function, "display::cjoin(arguments)")] @@ -268,3 +302,23 @@ pub struct Downcast { pub field: FieldDecl, pub position: Position, } + +#[display(fmt = "acc({})", predicate)] +pub struct AccPredicate { + pub predicate: Box, + pub position: Position, +} + +#[display(fmt = "unfolding({}, {})", predicate, body)] +pub struct Unfolding { + pub predicate: Box, + pub body: Box, + pub position: Position, +} + +#[display(fmt = "eval_in({}, {})", predicate, body)] +pub struct EvalIn { + pub predicate: Box, + pub body: Box, + pub position: Position, +} diff --git a/vir/defs/high/ast/function.rs b/vir/defs/high/ast/function.rs index bd52dccf969..f2a4bbe9d5c 100644 --- a/vir/defs/high/ast/function.rs +++ b/vir/defs/high/ast/function.rs @@ -8,7 +8,7 @@ use crate::common::display; "display::cjoin(parameters)", return_type, "display::foreach!(\" requires {}\n\", pres)", - "display::foreach!(\" ensures {}\n\", pres)", + "display::foreach!(\" ensures {}\n\", posts)", "display::option!(body, \"{{ {} }}\n\", \"\")" )] pub struct FunctionDecl { diff --git a/vir/defs/high/ast/mod.rs b/vir/defs/high/ast/mod.rs index 7d93ccce553..b5e68c91a50 100644 --- a/vir/defs/high/ast/mod.rs +++ b/vir/defs/high/ast/mod.rs @@ -4,10 +4,10 @@ Clone, serde::Serialize, serde::Deserialize, - PartialEq(ignore=[position]), + PartialEq(ignore=[position, lifetimes, lifetime]), Eq, - Hash(ignore=[position]), - Ord(ignore=[position]), + Hash(ignore=[position, lifetimes, lifetime]), + Ord(ignore=[position, lifetimes, lifetime]), )] #![derive_for_all_structs(new, new_with_pos)] diff --git a/vir/defs/high/ast/predicate.rs b/vir/defs/high/ast/predicate.rs index a1c1f68fbe7..81b4f2db6eb 100644 --- a/vir/defs/high/ast/predicate.rs +++ b/vir/defs/high/ast/predicate.rs @@ -11,8 +11,11 @@ pub enum Predicate { MemoryBlockStack(MemoryBlockStack), MemoryBlockStackDrop(MemoryBlockStackDrop), MemoryBlockHeap(MemoryBlockHeap), + MemoryBlockHeapRange(MemoryBlockHeapRange), MemoryBlockHeapDrop(MemoryBlockHeapDrop), OwnedNonAliased(OwnedNonAliased), + OwnedRange(OwnedRange), + OwnedSet(OwnedSet), } #[display(fmt = "acc(LifetimeToken({}), {})", lifetime, permission)] @@ -60,6 +63,21 @@ pub struct MemoryBlockHeap { pub position: Position, } +#[display( + fmt = "MemoryBlockHeapRange({}, {}, {}, {})", + address, + size, + start_index, + end_index +)] +pub struct MemoryBlockHeapRange { + pub address: Expression, + pub size: Expression, + pub start_index: Expression, + pub end_index: Expression, + pub position: Position, +} + /// A permission to deallocate a (precisely) matching `MemoryBlockHeap`. #[display(fmt = "MemoryBlockHeapDrop({}, {})", address, size)] pub struct MemoryBlockHeapDrop { @@ -74,3 +92,20 @@ pub struct OwnedNonAliased { pub place: Expression, pub position: Position, } + +/// A range of owned predicates of a specific type. `start_index` is inclusive +/// and `end_index` is exclusive. +#[display(fmt = "OwnedRange({}, {}, {})", address, start_index, end_index)] +pub struct OwnedRange { + pub address: Expression, + pub start_index: Expression, + pub end_index: Expression, + pub position: Position, +} + +/// A set of owned predicates of a specific type. +#[display(fmt = "OwnedSet({})", set)] +pub struct OwnedSet { + pub set: Expression, + pub position: Position, +} diff --git a/vir/defs/high/ast/rvalue.rs b/vir/defs/high/ast/rvalue.rs index 290182bbd9b..0a32fe5b72f 100644 --- a/vir/defs/high/ast/rvalue.rs +++ b/vir/defs/high/ast/rvalue.rs @@ -16,7 +16,7 @@ pub enum Rvalue { // ThreadLocalRef(ThreadLocalRef), AddressOf(AddressOf), Len(Len), - // Cast(Cast), + Cast(Cast), BinaryOp(BinaryOp), CheckedBinaryOp(CheckedBinaryOp), // NullaryOp(NullaryOp), @@ -66,6 +66,13 @@ pub struct Len { pub place: Expression, } +#[display(fmt = "cast({} -> {})", operand, ty)] +pub struct Cast { + // TODO: kind: CastKind, + pub operand: Operand, + pub ty: Type, +} + #[display(fmt = "{}({}, {})", kind, left, right)] pub struct BinaryOp { pub kind: BinaryOpKind, diff --git a/vir/defs/high/ast/statement.rs b/vir/defs/high/ast/statement.rs index e3fc011c1e3..35d39a01726 100644 --- a/vir/defs/high/ast/statement.rs +++ b/vir/defs/high/ast/statement.rs @@ -17,13 +17,16 @@ use std::collections::BTreeSet; pub enum Statement { Comment(Comment), OldLabel(OldLabel), - Inhale(Inhale), - Exhale(Exhale), + InhalePredicate(InhalePredicate), + ExhalePredicate(ExhalePredicate), + InhaleExpression(InhaleExpression), + ExhaleExpression(ExhaleExpression), + Assume(Assume), + Assert(Assert), Consume(Consume), Havoc(Havoc), GhostHavoc(GhostHavoc), - Assume(Assume), - Assert(Assert), + HeapHavoc(HeapHavoc), LoopInvariant(LoopInvariant), MovePlace(MovePlace), CopyPlace(CopyPlace), @@ -33,8 +36,19 @@ pub enum Statement { GhostAssign(GhostAssign), LeakAll(LeakAll), SetUnionVariant(SetUnionVariant), + Pack(Pack), + Unpack(Unpack), + Join(Join), + JoinRange(JoinRange), + Split(Split), + SplitRange(SplitRange), + StashRange(StashRange), + StashRangeRestore(StashRangeRestore), + ForgetInitialization(ForgetInitialization), + RestoreRawBorrowed(RestoreRawBorrowed), NewLft(NewLft), EndLft(EndLft), + DeadReference(DeadReference), DeadLifetime(DeadLifetime), DeadInclusion(DeadInclusion), LifetimeTake(LifetimeTake), @@ -59,16 +73,18 @@ pub struct OldLabel { pub position: Position, } -/// Inhale the permission denoted by the place. -#[display(fmt = "inhale {}", predicate)] -pub struct Inhale { +/// Inhale the permission denoted by the place. This operation is automatically +/// managed by fold-unfold. +#[display(fmt = "inhale-pred {}", predicate)] +pub struct InhalePredicate { pub predicate: Predicate, pub position: Position, } -#[display(fmt = "exhale {}", predicate)] -/// Exhale the permission denoted by the place. -pub struct Exhale { +#[display(fmt = "exhale-pred {}", predicate)] +/// Exhale the permission denoted by the place. This operation is automatically +/// managed by fold-unfold. +pub struct ExhalePredicate { pub predicate: Predicate, pub position: Position, } @@ -88,20 +104,47 @@ pub struct Havoc { } #[display(fmt = "ghost-havoc {}", variable)] +/// Havoc the local variable. pub struct GhostHavoc { pub variable: VariableDecl, pub position: Position, } +#[display(fmt = "heap-havoc")] +/// Havoc the heap. +pub struct HeapHavoc { + pub position: Position, +} + +#[display(fmt = "inhale-expr {}", expression)] +/// Inhale the boolean expression. This operation is ignored by fold-unfold. +pub struct InhaleExpression { + pub expression: Expression, + /// The label statement that immediatelly follows this inhale statement. Used + /// by `SelfFramingAssertionToSnapshot`. + pub label: Option, + pub position: Position, +} + +#[display(fmt = "exhale-expr {}", expression)] +/// Exhale the boolean expression. This operation is ignored by fold-unfold. +pub struct ExhaleExpression { + pub expression: Expression, + /// The label statement that immediatelly preceeds this exhale statement. + /// Used by `SelfFramingAssertionToSnapshot`. + pub label: Option, + pub position: Position, +} + #[display(fmt = "assume {}", expression)] -/// Assume the boolean expression. +/// Assume the pure boolean expression. pub struct Assume { pub expression: Expression, pub position: Position, } #[display(fmt = "assert {}", expression)] -/// Assert the boolean expression. +/// Assert the pure boolean expression. pub struct Assert { pub expression: Expression, pub position: Position, @@ -252,6 +295,123 @@ pub struct SetUnionVariant { pub position: Position, } +#[derive_helpers] +#[derive(derive_more::From, derive_more::IsVariant, derive_more::Unwrap)] +pub enum PredicateKind { + Owned, + UniqueRef(UniqueRef), + FracRef(FracRef), +} + +pub struct UniqueRef { + pub lifetime: LifetimeConst, +} + +pub struct FracRef { + pub lifetime: LifetimeConst, +} + +#[display( + fmt = "pack-{}{} {}", + predicate_kind, + "display::option!(permission, \"({})\", \"\")", + place +)] +pub struct Pack { + pub place: Expression, + pub predicate_kind: PredicateKind, + pub permission: Option, + pub position: Position, +} + +#[display( + fmt = "unpack-{}{} {}", + predicate_kind, + "display::option!(permission, \"({})\", \"\")", + place +)] +pub struct Unpack { + pub place: Expression, + pub predicate_kind: PredicateKind, + pub permission: Option, + pub position: Position, +} + +#[display(fmt = "join {}", place)] +pub struct Join { + pub place: Expression, + pub position: Position, +} + +#[display(fmt = "join-range {} {} {}", address, start_index, end_index)] +pub struct JoinRange { + pub address: Expression, + pub start_index: Expression, + pub end_index: Expression, + pub position: Position, +} + +#[display(fmt = "split {}", place)] +pub struct Split { + pub place: Expression, + pub position: Position, +} + +#[display(fmt = "split-range {} {} {}", address, start_index, end_index)] +pub struct SplitRange { + pub address: Expression, + pub start_index: Expression, + pub end_index: Expression, + pub position: Position, +} + +#[display( + fmt = "stash-range {} {} {} {}", + address, + start_index, + end_index, + label +)] +pub struct StashRange { + pub address: Expression, + pub start_index: Expression, + pub end_index: Expression, + pub label: String, + pub position: Position, +} + +#[display( + fmt = "stash-range-restore {} {} {} {} → {} {}", + old_address, + old_start_index, + old_end_index, + old_label, + new_address, + new_start_index +)] +pub struct StashRangeRestore { + pub old_address: Expression, + pub old_start_index: Expression, + pub old_end_index: Expression, + pub old_label: String, + pub new_address: Expression, + pub new_start_index: Expression, + pub position: Position, +} + +#[display(fmt = "forget-initialization {}", place)] +pub struct ForgetInitialization { + pub place: Expression, + pub position: Position, +} + +#[display(fmt = "restore {} --* {}", borrowing_place, restored_place)] +pub struct RestoreRawBorrowed { + pub borrowing_place: Expression, + pub restored_place: Expression, + pub position: Position, +} + #[display(fmt = "{} = newlft()", target)] pub struct NewLft { pub target: VariableDecl, @@ -264,6 +424,12 @@ pub struct EndLft { pub position: Position, } +#[display(fmt = "dead-reference({})", target)] +pub struct DeadReference { + pub target: Expression, + pub position: Position, +} + #[display(fmt = "dead-lifetime({})", lifetime)] pub struct DeadLifetime { pub lifetime: LifetimeConst, diff --git a/vir/defs/high/ast/ty.rs b/vir/defs/high/ast/ty.rs index 52368a6eabb..4a1648aeb88 100644 --- a/vir/defs/high/ast/ty.rs +++ b/vir/defs/high/ast/ty.rs @@ -14,6 +14,10 @@ pub enum Type { MFloat64, /// Viper permission amount. MPerm, + /// Mathematical Byte. + MByte, + /// A sequence of mathematical Bytes. + MBytes, Lifetime, /// Rust's Bool allocated on the Viper heap. Bool, diff --git a/vir/defs/high/ast/type_decl.rs b/vir/defs/high/ast/type_decl.rs index f4768418715..49b801d6939 100644 --- a/vir/defs/high/ast/type_decl.rs +++ b/vir/defs/high/ast/type_decl.rs @@ -1,6 +1,7 @@ pub(crate) use super::{ expression::Expression, field::FieldDecl, + position::Position, ty::{GenericType, LifetimeConst, Type, Uniqueness}, variable::VariableDecl, }; @@ -70,7 +71,9 @@ pub struct Struct { pub name: String, pub lifetimes: Vec, pub const_parameters: Vec, + pub structural_invariant: Option>, pub fields: Vec, + pub position: Position, } pub type DiscriminantValue = i128; diff --git a/vir/defs/high/cfg/procedure.rs b/vir/defs/high/cfg/procedure.rs index 757598f4e19..4f3f46fc83b 100644 --- a/vir/defs/high/cfg/procedure.rs +++ b/vir/defs/high/cfg/procedure.rs @@ -1,5 +1,6 @@ use super::super::ast::{ expression::{Expression, Local}, + position::Position, statement::Statement, }; use crate::common::{check_mode::CheckMode, display}; @@ -14,9 +15,13 @@ use std::collections::BTreeMap; pub struct ProcedureDecl { pub name: String, pub check_mode: CheckMode, + /// Stack variables are by default non-aliased. This property is exploited + /// by optimizations. + pub non_aliased_places: Vec, pub entry: BasicBlockId, pub exit: BasicBlockId, pub basic_blocks: BTreeMap, + pub position: Position, } #[derive(PartialOrd, Ord, derive_more::Constructor, derive_more::AsRef)] diff --git a/vir/defs/high/mod.rs b/vir/defs/high/mod.rs index 8460b5e512f..276d0c3ba56 100644 --- a/vir/defs/high/mod.rs +++ b/vir/defs/high/mod.rs @@ -5,25 +5,28 @@ pub(crate) mod operations_internal; pub use self::{ ast::{ expression::{ - self, visitors, AddrOf, BinaryOp, BinaryOpKind, BuiltinFunc, BuiltinFuncApp, - Conditional, Constant, Constructor, ContainerOp, Deref, Downcast, Expression, Field, - FuncApp, LabelledOld, LetExpr, Local, Quantifier, Seq, Trigger, UnaryOp, UnaryOpKind, - Variant, + self, visitors, AccPredicate, AddrOf, BinaryOp, BinaryOpKind, BuiltinFunc, + BuiltinFuncApp, Conditional, Constant, Constructor, ContainerOp, Deref, Downcast, + EvalIn, Expression, Field, Final, FuncApp, LabelledOld, LetExpr, Local, Quantifier, + Seq, Trigger, UnaryOp, UnaryOpKind, Unfolding, Variant, }, field::FieldDecl, function::FunctionDecl, position::Position, predicate::{ LifetimeToken, MemoryBlockHeap, MemoryBlockHeapDrop, MemoryBlockStack, - MemoryBlockStackDrop, Predicate, + MemoryBlockStackDrop, OwnedNonAliased, Predicate, }, rvalue::{Operand, OperandKind, Rvalue}, statement::{ Assert, Assign, Assume, BorShorten, CloseFracRef, CloseMutRef, Comment, Consume, - CopyPlace, DeadInclusion, DeadLifetime, EndLft, Exhale, GhostAssign, GhostHavoc, Havoc, - Inhale, LeakAll, LifetimeReturn, LifetimeTake, LoopInvariant, MovePlace, NewLft, - ObtainMutRef, OldLabel, OpenFracRef, OpenMutRef, SetUnionVariant, Statement, - WriteAddress, WritePlace, + CopyPlace, DeadInclusion, DeadLifetime, DeadReference, EndLft, ExhaleExpression, + ExhalePredicate, ForgetInitialization, FracRef, GhostAssign, GhostHavoc, Havoc, + HeapHavoc, InhaleExpression, InhalePredicate, Join, JoinRange, LeakAll, LifetimeReturn, + LifetimeTake, LoopInvariant, MovePlace, NewLft, ObtainMutRef, OldLabel, OpenFracRef, + OpenMutRef, Pack, PredicateKind, RestoreRawBorrowed, SetUnionVariant, Split, + SplitRange, StashRange, StashRangeRestore, Statement, UniqueRef, Unpack, WriteAddress, + WritePlace, }, ty::{self, Type}, type_decl::{self, DiscriminantRange, DiscriminantValue, TypeDecl}, diff --git a/vir/defs/high/operations_internal/const_generics/common.rs b/vir/defs/high/operations_internal/const_generics/common.rs index 05d243d6a62..2441234105b 100644 --- a/vir/defs/high/operations_internal/const_generics/common.rs +++ b/vir/defs/high/operations_internal/const_generics/common.rs @@ -41,6 +41,7 @@ impl WithConstArguments for Rvalue { Self::Repeat(value) => value.get_const_arguments(), Self::AddressOf(value) => value.get_const_arguments(), Self::Len(value) => value.get_const_arguments(), + Self::Cast(value) => value.get_const_arguments(), Self::BinaryOp(value) => value.get_const_arguments(), Self::CheckedBinaryOp(value) => value.get_const_arguments(), Self::UnaryOp(value) => value.get_const_arguments(), @@ -82,6 +83,14 @@ impl WithConstArguments for Len { } } +impl WithConstArguments for Cast { + fn get_const_arguments(&self) -> Vec { + let mut arguments = self.operand.get_const_arguments(); + arguments.extend(self.ty.get_const_arguments()); + arguments + } +} + impl WithConstArguments for BinaryOp { fn get_const_arguments(&self) -> Vec { let mut arguments = self.left.get_const_arguments(); diff --git a/vir/defs/high/operations_internal/expression.rs b/vir/defs/high/operations_internal/expression.rs index 72cdd733cdc..3ee335922fd 100644 --- a/vir/defs/high/operations_internal/expression.rs +++ b/vir/defs/high/operations_internal/expression.rs @@ -2,17 +2,18 @@ use super::{ super::ast::{ expression::{ visitors::{ - default_fold_expression, default_fold_quantifier, default_walk_expression, - ExpressionFolder, ExpressionWalker, + default_fold_expression, default_fold_quantifier, default_walk_binary_op, + default_walk_expression, ExpressionFolder, ExpressionWalker, }, *, }, position::Position, + predicate::visitors::{PredicateFolder, PredicateWalker}, ty::{self, visitors::TypeFolder, LifetimeConst, Type}, }, ty::Typed, }; -use crate::common::expression::SyntacticEvaluation; +use crate::common::expression::{ExpressionIterator, SyntacticEvaluation, UnaryOperationHelpers}; use std::collections::BTreeMap; impl From for Expression { @@ -61,7 +62,10 @@ impl Expression { Expression::Variant(Variant { box ref base, .. }) | Expression::Field(Field { box ref base, .. }) | Expression::Deref(Deref { box ref base, .. }) - | Expression::AddrOf(AddrOf { box ref base, .. }) => Some(base), + | Expression::AddrOf(AddrOf { box ref base, .. }) + | Expression::EvalIn(EvalIn { + body: box ref base, .. + }) => Some(base), Expression::LabelledOld(_) => None, Expression::BuiltinFuncApp(BuiltinFuncApp { function: BuiltinFunc::Index, @@ -71,6 +75,28 @@ impl Expression { expr => unreachable!("{}", expr), } } + /// Create a new place with the provided parent. + pub fn with_new_parent(&self, new_parent: Self) -> Self { + match self { + Expression::Variant(expression) => Expression::Variant(Variant { + base: box new_parent, + ..expression.clone() + }), + Expression::Field(expression) => Expression::Field(Field { + base: box new_parent, + ..expression.clone() + }), + Expression::Deref(expression) => Expression::Deref(Deref { + base: box new_parent, + ..expression.clone() + }), + Expression::AddrOf(expression) => Expression::AddrOf(AddrOf { + base: box new_parent, + ..expression.clone() + }), + _ => unreachable!("Cannot change parent for {}", self), + } + } /// Only defined for places. pub fn try_into_parent(self) -> Option { debug_assert!(self.is_place()); @@ -113,7 +139,8 @@ impl Expression { | Expression::Field(Field { base, .. }) | Expression::Deref(Deref { base, .. }) | Expression::AddrOf(AddrOf { base, .. }) - | Expression::LabelledOld(LabelledOld { base, .. }) => base.is_place(), + | Expression::LabelledOld(LabelledOld { base, .. }) + | Expression::EvalIn(EvalIn { body: base, .. }) => base.is_place(), Expression::BuiltinFuncApp(BuiltinFuncApp { function: BuiltinFunc::Index, arguments, @@ -180,8 +207,8 @@ impl Expression { } } - /// Check whether the place is a dereference of a reference and if that is - /// the case, return its base. + /// Check whether the place is a dereference if that is the case, return its + /// base. pub fn get_dereference_base(&self) -> Option<&Expression> { assert!(self.is_place()); if let Expression::Deref(Deref { box base, .. }) = self { @@ -193,6 +220,80 @@ impl Expression { } } + /// Check whether the place is a dereference of a reference and if that is + /// the case, return its base. + pub fn get_last_dereferenced_reference(&self) -> Option<&Expression> { + assert!(self.is_place()); + if let Expression::Deref(Deref { box base, .. }) = self { + if let Type::Reference(_) = base.get_type() { + Some(base) + } else { + base.get_last_dereferenced_reference() + } + } else if let Some(parent) = self.get_parent_ref() { + parent.get_last_dereferenced_reference() + } else { + None + } + } + + /// Same as `get_last_dereferenced_reference`, just returns the first + /// reference. + pub fn get_first_dereferenced_reference(&self) -> Option<&Expression> { + assert!(self.is_place()); + if let Expression::Deref(Deref { box base, .. }) = self { + let parent_ref = base.get_first_dereferenced_reference(); + if parent_ref.is_some() { + parent_ref + } else if let Type::Reference(_) = base.get_type() { + Some(base) + } else { + None + } + } else if let Some(parent) = self.get_parent_ref() { + parent.get_first_dereferenced_reference() + } else { + None + } + } + + pub fn is_behind_pointer_dereference(&self) -> bool { + assert!(self.is_place()); + if let Some(parent) = self.get_parent_ref() { + if self.is_deref() && parent.get_type().is_pointer() { + return true; + } + parent.is_behind_pointer_dereference() + } else { + false + } + } + + pub fn get_last_dereferenced_pointer(&self) -> Option<&Expression> { + assert!(self.is_place()); + if let Some(parent) = self.get_parent_ref() { + if self.is_deref() && parent.get_type().is_pointer() { + return Some(parent); + } + parent.get_last_dereferenced_pointer() + } else { + None + } + } + + pub fn get_first_dereferenced_pointer(&self) -> Option<&Expression> { + assert!(self.is_place()); + if let Some(last_pointer) = self.get_last_dereferenced_pointer() { + if let Some(parent) = last_pointer.get_first_dereferenced_pointer() { + Some(parent) + } else { + Some(last_pointer) + } + } else { + None + } + } + #[must_use] pub fn erase_lifetime(self) -> Expression { struct DefaultLifetimeEraser {} @@ -218,6 +319,22 @@ impl Expression { DefaultLifetimeEraser {}.fold_expression(self) } + pub fn check_no_erased_lifetime(&self) { + struct LifetimeChecker {} + impl ExpressionWalker for LifetimeChecker { + fn walk_type(&mut self, ty: &Type) { + ty.check_no_erased_lifetime(); + } + fn walk_variable_decl(&mut self, variable_decl: &VariableDecl) { + variable_decl.ty.check_no_erased_lifetime(); + } + fn walk_field_decl(&mut self, field_decl: &FieldDecl) { + field_decl.ty.check_no_erased_lifetime(); + } + } + LifetimeChecker {}.walk_expression(self); + } + #[must_use] pub fn replace_lifetimes( self, @@ -322,12 +439,29 @@ impl Expression { default_fold_expression(self, expression) } } + fn fold_predicate(&mut self, predicate: Predicate) -> Predicate { + PredicateFolder::fold_predicate(self, predicate) + } + fn fold_trigger(&mut self, trigger: Trigger) -> Trigger { + Trigger::new( + trigger + .terms + .into_iter() + .map(|term| ExpressionFolder::fold_expression(self, term)) + .collect::>(), + ) + } + } + impl<'a> PredicateFolder for PlaceReplacer<'a> { + fn fold_expression(&mut self, expression: Expression) -> Expression { + ExpressionFolder::fold_expression(self, expression) + } } let mut replacer = PlaceReplacer { target, replacement, }; - replacer.fold_expression(self) + ExpressionFolder::fold_expression(&mut replacer, self) } #[must_use] pub fn replace_multiple_places(self, replacements: &[(Expression, Expression)]) -> Self { @@ -364,8 +498,76 @@ impl Expression { } Expression::Quantifier(default_fold_quantifier(self, quantifier)) } + + fn fold_predicate(&mut self, predicate: Predicate) -> Predicate { + PredicateFolder::fold_predicate(self, predicate) + } + + fn fold_trigger(&mut self, trigger: Trigger) -> Trigger { + Trigger::new( + trigger + .terms + .into_iter() + .map(|term| ExpressionFolder::fold_expression(self, term)) + .collect::>(), + ) + } + } + impl<'a> PredicateFolder for PlaceReplacer<'a> { + fn fold_expression(&mut self, expression: Expression) -> Expression { + ExpressionFolder::fold_expression(self, expression) + } + } + let mut replacer = PlaceReplacer { replacements }; + ExpressionFolder::fold_expression(&mut replacer, self) + } + #[must_use] + pub fn replace_self(self, replacement: &Expression) -> Self { + struct PlaceReplacer<'a> { + replacement: &'a Expression, + } + impl<'a> ExpressionFolder for PlaceReplacer<'a> { + fn fold_local_enum(&mut self, local: Local) -> Expression { + if local.variable.is_self_variable() { + assert_eq!( + &local.variable.ty, + self.replacement.get_type(), + "{} → {}", + local.variable.ty, + self.replacement + ); + self.replacement.clone() + } else { + Expression::Local(local) + } + } + fn fold_predicate(&mut self, predicate: Predicate) -> Predicate { + PredicateFolder::fold_predicate(self, predicate) + } + + fn fold_trigger(&mut self, trigger: Trigger) -> Trigger { + Trigger::new( + trigger + .terms + .into_iter() + .map(|term| ExpressionFolder::fold_expression(self, term)) + .collect::>(), + ) + } + } + impl<'a> PredicateFolder for PlaceReplacer<'a> { + fn fold_expression(&mut self, expression: Expression) -> Expression { + ExpressionFolder::fold_expression(self, expression) + } + } + let mut replacer = PlaceReplacer { replacement }; + ExpressionFolder::fold_expression(&mut replacer, self) + } + pub fn peel_unfoldings(&self) -> &Self { + match self { + Expression::Unfolding(unfolding) => unfolding.body.peel_unfoldings(), + _ => self, } - PlaceReplacer { replacements }.fold_expression(self) } #[must_use] pub fn map_old_expression_label(self, substitutor: F) -> Self @@ -421,6 +623,43 @@ impl Expression { } Simplifier.fold_expression(self) } + /// Simplify `construtor(arg1, arg2, ..., argn).field_k` to `argk`. + pub fn simplify_out_constructors(self) -> Self { + struct Simplifier; + impl ExpressionFolder for Simplifier { + fn fold_expression(&mut self, expression: Expression) -> Expression { + match expression { + Expression::Field(Field { + base: box Expression::Constructor(constructor), + field, + position: _, + }) => ExpressionFolder::fold_expression( + self, + constructor.arguments[field.index].clone(), + ), + _ => default_fold_expression(self, expression), + } + } + fn fold_predicate(&mut self, predicate: Predicate) -> Predicate { + PredicateFolder::fold_predicate(self, predicate) + } + fn fold_trigger(&mut self, trigger: Trigger) -> Trigger { + Trigger::new( + trigger + .terms + .into_iter() + .map(|term| ExpressionFolder::fold_expression(self, term)) + .collect::>(), + ) + } + } + impl PredicateFolder for Simplifier { + fn fold_expression(&mut self, expression: Expression) -> Expression { + ExpressionFolder::fold_expression(self, expression) + } + } + ExpressionFolder::fold_expression(&mut Simplifier, self) + } fn apply_simplification_rules(self) -> Self { let mut expression = self; loop { @@ -529,8 +768,25 @@ impl Expression { let expression = default_fold_expression(self, expression); expression.apply_simplification_rules() } + fn fold_predicate(&mut self, predicate: Predicate) -> Predicate { + PredicateFolder::fold_predicate(self, predicate) + } + fn fold_trigger(&mut self, trigger: Trigger) -> Trigger { + Trigger::new( + trigger + .terms + .into_iter() + .map(|term| ExpressionFolder::fold_expression(self, term)) + .collect::>(), + ) + } } - Simplifier.fold_expression(self) + impl PredicateFolder for Simplifier { + fn fold_expression(&mut self, expression: Expression) -> Expression { + ExpressionFolder::fold_expression(self, expression) + } + } + ExpressionFolder::fold_expression(&mut Simplifier, self) } pub fn find(&self, sub_target: &Expression) -> bool { pub struct ExprFinder<'a> { @@ -545,13 +801,26 @@ impl Expression { default_walk_expression(self, expr) } } + fn walk_predicate(&mut self, predicate: &Predicate) { + PredicateWalker::walk_predicate(self, predicate) + } + fn walk_trigger(&mut self, trigger: &Trigger) { + for term in &trigger.terms { + ExpressionWalker::walk_expression(self, term) + } + } + } + impl<'a> PredicateWalker for ExprFinder<'a> { + fn walk_expression(&mut self, expr: &Expression) { + ExpressionWalker::walk_expression(self, expr) + } } let mut finder = ExprFinder { sub_target, found: false, }; - finder.walk_expression(self); + ExpressionWalker::walk_expression(&mut finder, self); finder.found } pub fn function_call>( @@ -686,4 +955,192 @@ impl Expression { pub fn full_permission() -> Self { Self::constant_no_pos(ConstantValue::Int(1), Type::MPerm) } + + pub fn is_pure(&self) -> bool { + struct Checker { + is_pure: bool, + } + impl ExpressionWalker for Checker { + fn walk_acc_predicate(&mut self, _: &AccPredicate) { + self.is_pure = false; + } + fn walk_eval_in(&mut self, eval_in: &EvalIn) { + self.walk_expression(&eval_in.body); + } + } + let mut checker = Checker { is_pure: true }; + checker.walk_expression(self); + checker.is_pure + } +} + +/// Methods for collecting places. +impl Expression { + /// Returns place used in `own`. + pub fn collect_owned_places(&self) -> (Vec, Vec) { + struct Collector { + owned_places: Vec, + owned_range_addresses: Vec, + } + impl<'a> ExpressionWalker for Collector { + fn walk_acc_predicate(&mut self, acc_predicate: &AccPredicate) { + match &*acc_predicate.predicate { + Predicate::LifetimeToken(_) + | Predicate::MemoryBlockStack(_) + | Predicate::MemoryBlockStackDrop(_) + | Predicate::MemoryBlockHeap(_) + | Predicate::MemoryBlockHeapRange(_) + | Predicate::MemoryBlockHeapDrop(_) => {} + Predicate::OwnedNonAliased(predicate) => { + self.owned_places.push(predicate.place.clone()); + } + Predicate::OwnedRange(predicate) => { + self.owned_range_addresses.push(predicate.address.clone()); + } + Predicate::OwnedSet(predicate) => { + unimplemented!("predicate: {}", predicate); + } + } + } + } + let mut collector = Collector { + owned_places: Vec::new(), + owned_range_addresses: Vec::new(), + }; + collector.walk_expression(self); + (collector.owned_places, collector.owned_range_addresses) + } + + /// Returns places used in `own` with path conditions that guard them. + pub fn collect_guarded_owned_places(&self) -> Vec<(Expression, Expression)> { + struct Collector { + path_condition: Vec, + owned_places: Vec<(Expression, Expression)>, + } + impl<'a> ExpressionWalker for Collector { + fn walk_acc_predicate(&mut self, acc_predicate: &AccPredicate) { + match &*acc_predicate.predicate { + Predicate::LifetimeToken(_) + | Predicate::MemoryBlockStack(_) + | Predicate::MemoryBlockStackDrop(_) + | Predicate::MemoryBlockHeap(_) + | Predicate::MemoryBlockHeapRange(_) + | Predicate::MemoryBlockHeapDrop(_) => {} + Predicate::OwnedNonAliased(predicate) => { + self.owned_places.push(( + self.path_condition.iter().cloned().conjoin(), + predicate.place.clone(), + )); + } + Predicate::OwnedRange(predicate) => { + unimplemented!("predicate: {}", predicate); + } + Predicate::OwnedSet(predicate) => { + unimplemented!("predicate: {}", predicate); + } + } + } + fn walk_binary_op(&mut self, binary_op: &BinaryOp) { + if binary_op.op_kind == BinaryOpKind::Implies { + self.path_condition.push((*binary_op.left).clone()); + self.walk_expression(&binary_op.right); + self.path_condition.pop(); + } else { + default_walk_binary_op(self, binary_op); + } + } + fn walk_conditional(&mut self, conditional: &Conditional) { + self.path_condition.push((*conditional.guard).clone()); + self.walk_expression(&conditional.then_expr); + let guard = self.path_condition.pop().unwrap(); + self.path_condition.push(Expression::not(guard)); + self.walk_expression(&conditional.else_expr); + self.path_condition.pop(); + } + } + let mut collector = Collector { + path_condition: Vec::new(), + owned_places: Vec::new(), + }; + collector.walk_expression(self); + collector.owned_places + } + + /// Returns the expression with all pure parts removed and implications + /// converted into conditionals. + /// + /// This method is different from `collect_guarded_owned_places` in that it + /// still returns a single expression preserving most of the original + /// structure. + pub fn convert_into_permission_expression(self) -> Expression { + struct Remover {} + impl<'a> ExpressionFolder for Remover { + fn fold_expression(&mut self, expression: Expression) -> Expression { + if expression.is_pure() { + true.into() + } else { + default_fold_expression(self, expression) + } + } + fn fold_binary_op_enum(&mut self, binary_op: BinaryOp) -> Expression { + if binary_op.op_kind == BinaryOpKind::Implies { + let guard = *binary_op.left; + let then_expr = self.fold_expression(*binary_op.right); + let else_expr = false.into(); + Expression::conditional(guard, then_expr, else_expr, binary_op.position) + } else { + Expression::BinaryOp(self.fold_binary_op(binary_op)) + } + } + } + let mut remover = Remover {}; + remover.fold_expression(self) + } + + /// Returns places that contain dereferences with their path conditions. + pub fn collect_guarded_dereferenced_places(&self) -> Vec<(Expression, Expression)> { + struct Collector { + path_condition: Vec, + deref_places: Vec<(Expression, Expression)>, + } + impl<'a> ExpressionWalker for Collector { + fn walk_expression(&mut self, expression: &Expression) { + if expression.is_place() { + if expression.get_last_dereferenced_pointer().is_some() { + self.deref_places.push(( + self.path_condition.iter().cloned().conjoin(), + expression.clone(), + )); + } + } else { + default_walk_expression(self, expression) + } + } + fn walk_binary_op(&mut self, binary_op: &BinaryOp) { + if binary_op.op_kind == BinaryOpKind::Implies { + self.walk_expression(&binary_op.left); + self.path_condition.push((*binary_op.left).clone()); + self.walk_expression(&binary_op.right); + self.path_condition.pop(); + } else { + default_walk_binary_op(self, binary_op); + } + } + fn walk_conditional(&mut self, conditional: &Conditional) { + self.walk_expression(&conditional.guard); + self.path_condition.push((*conditional.guard).clone()); + self.walk_expression(&conditional.then_expr); + let guard = self.path_condition.pop().unwrap(); + self.path_condition.push(Expression::not(guard)); + self.walk_expression(&conditional.else_expr); + self.path_condition.pop(); + } + } + let mut collector = Collector { + path_condition: Vec::new(), + deref_places: Vec::new(), + }; + collector.walk_expression(self); + collector.deref_places + } } diff --git a/vir/defs/high/operations_internal/graphviz.rs b/vir/defs/high/operations_internal/graphviz.rs index dc94f6a7b55..7ca52872c4e 100644 --- a/vir/defs/high/operations_internal/graphviz.rs +++ b/vir/defs/high/operations_internal/graphviz.rs @@ -2,7 +2,7 @@ use super::super::{ ast::statement::Statement, cfg::procedure::{BasicBlock, ProcedureDecl, Successor}, }; -use crate::common::graphviz::{escape_html, Graph, NodeBuilder, ToGraphviz}; +use crate::common::graphviz::{escape_html, escape_html_wrap, Graph, NodeBuilder, ToGraphviz}; use std::io::Write; impl ToGraphviz for ProcedureDecl { @@ -48,9 +48,12 @@ fn block_to_graph_node(block: &BasicBlock, node_builder: &mut NodeBuilder) { for statement in &block.statements { let statement_string = match statement { Statement::Comment(statement) => { - format!("{}", escape_html(statement)) + format!( + "{}", + escape_html_wrap(statement) + ) } - _ => escape_html(statement.to_string()), + _ => escape_html_wrap(statement.to_string()), }; node_builder.add_row_sequence(vec![statement_string]); } diff --git a/vir/defs/high/operations_internal/helpers.rs b/vir/defs/high/operations_internal/helpers.rs index aeca7d5ae0f..03536c7bcf5 100644 --- a/vir/defs/high/operations_internal/helpers.rs +++ b/vir/defs/high/operations_internal/helpers.rs @@ -117,22 +117,44 @@ impl ConstantHelpers for Expression { impl SyntacticEvaluation for Expression { fn is_true(&self) -> bool { - matches!( - self, + match self { Self::Constant(Constant { value: ConstantValue::Bool(true), .. - }) - ) + }) => true, + Self::UnaryOp(UnaryOp { + op_kind: UnaryOpKind::Not, + argument, + .. + }) => argument.is_false(), + Self::BinaryOp(BinaryOp { + op_kind: BinaryOpKind::Or, + left, + right, + .. + }) => left.is_true() || right.is_true(), + _ => false, + } } fn is_false(&self) -> bool { - matches!( - self, + match self { Self::Constant(Constant { value: ConstantValue::Bool(false), .. - }) - ) + }) => true, + Self::UnaryOp(UnaryOp { + op_kind: UnaryOpKind::Not, + argument, + .. + }) => argument.is_true(), + Self::BinaryOp(BinaryOp { + op_kind: BinaryOpKind::And, + left, + right, + .. + }) => left.is_false() || right.is_false(), + _ => false, + } } fn is_zero(&self) -> bool { matches!( diff --git a/vir/defs/high/operations_internal/identifier/predicate.rs b/vir/defs/high/operations_internal/identifier/predicate.rs index 5f1a5b0427a..99a6be27c01 100644 --- a/vir/defs/high/operations_internal/identifier/predicate.rs +++ b/vir/defs/high/operations_internal/identifier/predicate.rs @@ -14,8 +14,11 @@ impl WithIdentifier for Predicate { Self::MemoryBlockStack(predicate) => predicate.get_identifier(), Self::MemoryBlockStackDrop(predicate) => predicate.get_identifier(), Self::MemoryBlockHeap(predicate) => predicate.get_identifier(), + Self::MemoryBlockHeapRange(predicate) => predicate.get_identifier(), Self::MemoryBlockHeapDrop(predicate) => predicate.get_identifier(), Self::OwnedNonAliased(predicate) => predicate.get_identifier(), + Self::OwnedRange(predicate) => predicate.get_identifier(), + Self::OwnedSet(predicate) => predicate.get_identifier(), } } } @@ -44,6 +47,12 @@ impl WithIdentifier for predicate::MemoryBlockHeap { } } +impl WithIdentifier for predicate::MemoryBlockHeapRange { + fn get_identifier(&self) -> String { + "MemoryBlockHeapRange".to_string() + } +} + impl WithIdentifier for predicate::MemoryBlockHeapDrop { fn get_identifier(&self) -> String { "MemoryBlockHeapDrop".to_string() @@ -55,3 +64,15 @@ impl WithIdentifier for predicate::OwnedNonAliased { format!("OwnedNonAliased${}", self.place.get_type().get_identifier()) } } + +impl WithIdentifier for predicate::OwnedRange { + fn get_identifier(&self) -> String { + format!("OwnedRange${}", self.address.get_type().get_identifier()) + } +} + +impl WithIdentifier for predicate::OwnedSet { + fn get_identifier(&self) -> String { + format!("OwnedSet${}", self.set.get_type().get_identifier()) + } +} diff --git a/vir/defs/high/operations_internal/identifier/rvalue.rs b/vir/defs/high/operations_internal/identifier/rvalue.rs index c9ecdc33215..542a8ad6421 100644 --- a/vir/defs/high/operations_internal/identifier/rvalue.rs +++ b/vir/defs/high/operations_internal/identifier/rvalue.rs @@ -7,6 +7,7 @@ impl WithIdentifier for Rvalue { Self::Repeat(value) => value.get_identifier(), Self::AddressOf(value) => value.get_identifier(), Self::Len(value) => value.get_identifier(), + Self::Cast(value) => value.get_identifier(), Self::BinaryOp(value) => value.get_identifier(), Self::CheckedBinaryOp(value) => value.get_identifier(), Self::UnaryOp(value) => value.get_identifier(), @@ -48,6 +49,16 @@ impl WithIdentifier for Len { } } +impl WithIdentifier for Cast { + fn get_identifier(&self) -> String { + format!( + "Cast${}${}", + self.operand.get_identifier(), + self.ty.get_identifier() + ) + } +} + impl WithIdentifier for UnaryOp { fn get_identifier(&self) -> String { format!("UnaryOp${}${}", self.kind, self.argument.get_identifier()) diff --git a/vir/defs/high/operations_internal/identifier/ty.rs b/vir/defs/high/operations_internal/identifier/ty.rs index c8ac0b8585f..a11dc7433d2 100644 --- a/vir/defs/high/operations_internal/identifier/ty.rs +++ b/vir/defs/high/operations_internal/identifier/ty.rs @@ -9,6 +9,8 @@ impl WithIdentifier for ty::Type { ty::Type::MFloat32 => "MFloat32".to_string(), ty::Type::MFloat64 => "MFloat64".to_string(), ty::Type::MPerm => "MPerm".to_string(), + ty::Type::MByte => "MByte".to_string(), + ty::Type::MBytes => "MBytes".to_string(), ty::Type::Bool => "Bool".to_string(), ty::Type::Int(ty) => ty.get_identifier(), ty::Type::Sequence(ty) => ty.get_identifier(), diff --git a/vir/defs/high/operations_internal/lifetimes/common.rs b/vir/defs/high/operations_internal/lifetimes/common.rs index 7e644d94d9e..b4835a0e102 100644 --- a/vir/defs/high/operations_internal/lifetimes/common.rs +++ b/vir/defs/high/operations_internal/lifetimes/common.rs @@ -51,6 +51,7 @@ impl WithLifetimes for Rvalue { Self::Repeat(value) => value.get_lifetimes(), Self::AddressOf(value) => value.get_lifetimes(), Self::Len(value) => value.get_lifetimes(), + Self::Cast(value) => value.get_lifetimes(), Self::BinaryOp(value) => value.get_lifetimes(), Self::CheckedBinaryOp(value) => value.get_lifetimes(), Self::UnaryOp(value) => value.get_lifetimes(), @@ -96,6 +97,14 @@ impl WithLifetimes for Len { } } +impl WithLifetimes for Cast { + fn get_lifetimes(&self) -> Vec { + let mut lifetimes = self.operand.get_lifetimes(); + lifetimes.extend(self.ty.get_lifetimes()); + lifetimes + } +} + impl WithLifetimes for BinaryOp { fn get_lifetimes(&self) -> Vec { let mut lifetimes = self.left.get_lifetimes(); diff --git a/vir/defs/high/operations_internal/position/expressions.rs b/vir/defs/high/operations_internal/position/expressions.rs index e9e5bd86136..ee83da64aad 100644 --- a/vir/defs/high/operations_internal/position/expressions.rs +++ b/vir/defs/high/operations_internal/position/expressions.rs @@ -9,6 +9,7 @@ impl Positioned for Expression { Self::Variant(expression) => expression.position(), Self::Field(expression) => expression.position(), Self::Deref(expression) => expression.position(), + Self::Final(expression) => expression.position(), Self::AddrOf(expression) => expression.position(), Self::LabelledOld(expression) => expression.position(), Self::Constant(expression) => expression.position(), @@ -22,6 +23,9 @@ impl Positioned for Expression { Self::FuncApp(expression) => expression.position(), Self::Downcast(expression) => expression.position(), Self::BuiltinFuncApp(expression) => expression.position(), + Self::AccPredicate(expression) => expression.position(), + Self::Unfolding(expression) => expression.position(), + Self::EvalIn(expression) => expression.position(), } } } @@ -56,6 +60,12 @@ impl Positioned for Deref { } } +impl Positioned for Final { + fn position(&self) -> Position { + self.position + } +} + impl Positioned for AddrOf { fn position(&self) -> Position { self.position @@ -133,3 +143,21 @@ impl Positioned for Downcast { self.position } } + +impl Positioned for AccPredicate { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for Unfolding { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for EvalIn { + fn position(&self) -> Position { + self.position + } +} diff --git a/vir/defs/high/operations_internal/position/mod.rs b/vir/defs/high/operations_internal/position/mod.rs index 3705384ac71..5ae756e1a4c 100644 --- a/vir/defs/high/operations_internal/position/mod.rs +++ b/vir/defs/high/operations_internal/position/mod.rs @@ -1,2 +1,3 @@ mod expressions; mod statement; +mod type_decl; diff --git a/vir/defs/high/operations_internal/position/statement.rs b/vir/defs/high/operations_internal/position/statement.rs index 0b9ae927f4c..35557c593ed 100644 --- a/vir/defs/high/operations_internal/position/statement.rs +++ b/vir/defs/high/operations_internal/position/statement.rs @@ -6,10 +6,13 @@ impl Positioned for Statement { match self { Self::Comment(statement) => statement.position(), Self::OldLabel(statement) => statement.position(), - Self::Inhale(statement) => statement.position(), - Self::Exhale(statement) => statement.position(), + Self::InhalePredicate(statement) => statement.position(), + Self::ExhalePredicate(statement) => statement.position(), + Self::InhaleExpression(statement) => statement.position(), + Self::ExhaleExpression(statement) => statement.position(), Self::Havoc(statement) => statement.position(), Self::GhostHavoc(statement) => statement.position(), + Self::HeapHavoc(statement) => statement.position(), Self::Assume(statement) => statement.position(), Self::Assert(statement) => statement.position(), Self::LoopInvariant(statement) => statement.position(), @@ -22,8 +25,19 @@ impl Positioned for Statement { Self::Consume(statement) => statement.position(), Self::LeakAll(statement) => statement.position(), Self::SetUnionVariant(statement) => statement.position(), + Self::Pack(statement) => statement.position(), + Self::Unpack(statement) => statement.position(), + Self::Join(statement) => statement.position(), + Self::JoinRange(statement) => statement.position(), + Self::Split(statement) => statement.position(), + Self::SplitRange(statement) => statement.position(), + Self::StashRange(statement) => statement.position(), + Self::StashRangeRestore(statement) => statement.position(), + Self::ForgetInitialization(statement) => statement.position(), + Self::RestoreRawBorrowed(statement) => statement.position(), Self::NewLft(statement) => statement.position(), Self::EndLft(statement) => statement.position(), + Self::DeadReference(statement) => statement.position(), Self::DeadLifetime(statement) => statement.position(), Self::DeadInclusion(statement) => statement.position(), Self::LifetimeTake(statement) => statement.position(), @@ -50,13 +64,25 @@ impl Positioned for OldLabel { } } -impl Positioned for Inhale { +impl Positioned for InhalePredicate { fn position(&self) -> Position { self.position } } -impl Positioned for Exhale { +impl Positioned for ExhalePredicate { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for InhaleExpression { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for ExhaleExpression { fn position(&self) -> Position { self.position } @@ -74,6 +100,12 @@ impl Positioned for GhostHavoc { } } +impl Positioned for HeapHavoc { + fn position(&self) -> Position { + self.position + } +} + impl Positioned for GhostAssign { fn position(&self) -> Position { self.position @@ -146,6 +178,66 @@ impl Positioned for SetUnionVariant { } } +impl Positioned for Pack { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for Unpack { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for Join { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for JoinRange { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for Split { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for SplitRange { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for StashRange { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for StashRangeRestore { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for ForgetInitialization { + fn position(&self) -> Position { + self.position + } +} + +impl Positioned for RestoreRawBorrowed { + fn position(&self) -> Position { + self.position + } +} + impl Positioned for NewLft { fn position(&self) -> Position { self.position @@ -158,6 +250,12 @@ impl Positioned for EndLft { } } +impl Positioned for DeadReference { + fn position(&self) -> Position { + self.position + } +} + impl Positioned for DeadLifetime { fn position(&self) -> Position { self.position diff --git a/vir/defs/high/operations_internal/position/type_decl.rs b/vir/defs/high/operations_internal/position/type_decl.rs new file mode 100644 index 00000000000..8482ae7b43e --- /dev/null +++ b/vir/defs/high/operations_internal/position/type_decl.rs @@ -0,0 +1,33 @@ +use super::super::super::ast::type_decl::*; +use crate::common::position::Positioned; + +impl Positioned for TypeDecl { + fn position(&self) -> Position { + match self { + Self::Bool => Default::default(), + Self::Int(_) => Default::default(), + Self::Float(_) => Default::default(), + Self::TypeVar(_) => Default::default(), + Self::Tuple(_) => Default::default(), + Self::Struct(decl) => decl.position(), + Self::Sequence(_) => Default::default(), + Self::Map(_) => Default::default(), + Self::Enum(_) => Default::default(), + Self::Union(_) => Default::default(), + Self::Array(_) => Default::default(), + Self::Slice(_) => Default::default(), + Self::Reference(_) => Default::default(), + Self::Pointer(_) => Default::default(), + Self::Never => Default::default(), + Self::Closure(_) => Default::default(), + Self::Unsupported(_) => Default::default(), + Self::Trusted(_) => Default::default(), + } + } +} + +impl Positioned for Struct { + fn position(&self) -> Position { + self.position + } +} diff --git a/vir/defs/high/operations_internal/predicate.rs b/vir/defs/high/operations_internal/predicate.rs index 88f3f66c3ef..3707679039d 100644 --- a/vir/defs/high/operations_internal/predicate.rs +++ b/vir/defs/high/operations_internal/predicate.rs @@ -32,6 +32,14 @@ impl Predicate { predicate.size.get_type().clone(), ] } + Self::MemoryBlockHeapRange(predicate) => { + // FIXME: This is probably wrong: we need to use the type of the + // target. + vec![ + predicate.address.get_type().clone(), + predicate.size.get_type().clone(), + ] + } Self::MemoryBlockHeapDrop(predicate) => { vec![ predicate.address.get_type().clone(), @@ -41,6 +49,16 @@ impl Predicate { Self::OwnedNonAliased(predicate) => { vec![predicate.place.get_type().clone()] } + Self::OwnedRange(predicate) => { + // FIXME: This is probably wrong: we need to use the type of the + // target. + vec![predicate.address.get_type().clone()] + } + Self::OwnedSet(predicate) => { + // FIXME: This is probably wrong: we need to use the type of the + // target of the pointer stored in the set. + vec![predicate.set.get_type().clone()] + } } } pub fn check_no_default_position(&self) { diff --git a/vir/defs/high/operations_internal/special_variables.rs b/vir/defs/high/operations_internal/special_variables.rs index 2a0ceb78d23..55be0d68bf1 100644 --- a/vir/defs/high/operations_internal/special_variables.rs +++ b/vir/defs/high/operations_internal/special_variables.rs @@ -6,18 +6,54 @@ use super::super::ast::{ variable::VariableDecl, }; +impl VariableDecl { + pub fn self_variable(ty: Type) -> Self { + VariableDecl::new(crate::common::builtin_constants::SELF_VARIABLE_NAME, ty) + } + + pub fn is_self_variable(&self) -> bool { + self.name == crate::common::builtin_constants::SELF_VARIABLE_NAME + } + + pub fn result_variable(ty: Type) -> Self { + VariableDecl::new(crate::common::builtin_constants::RESULT_VARIABLE_NAME, ty) + } + + pub fn discriminant_variable() -> Self { + VariableDecl::new( + crate::common::builtin_constants::DISCRIMINANT_VARIABLE_NAME, + Type::MInt, + ) + } +} + impl Expression { + pub fn self_variable(ty: Type) -> Self { + let variable = VariableDecl::self_variable(ty); + Expression::local_no_pos(variable) + } + + pub fn is_self_variable(&self) -> bool { + if let Expression::Local(Local { variable, .. }) = self { + variable.is_self_variable() + } else { + false + } + } + pub fn discriminant() -> Self { - let variable = VariableDecl::new("discriminant$", Type::MInt); + let variable = VariableDecl::discriminant_variable(); Expression::local_no_pos(variable) } + pub fn is_discriminant(&self) -> bool { if let Expression::Local(Local { variable, .. }) = self { - variable.name == "discriminant$" + variable.name == crate::common::builtin_constants::DISCRIMINANT_VARIABLE_NAME } else { false } } + pub fn is_discriminant_field(&self) -> bool { if let Expression::Field(Field { field, .. }) = self { field.is_discriminant() @@ -31,10 +67,15 @@ const DISCRIMINANT_INDEX: usize = 100000; impl FieldDecl { pub fn discriminant(ty: Type) -> Self { - FieldDecl::new("discriminant", DISCRIMINANT_INDEX, ty) + FieldDecl::new( + crate::common::builtin_constants::DISCRIMINANT_FIELD_NAME, + DISCRIMINANT_INDEX, + ty, + ) } pub fn is_discriminant(&self) -> bool { - self.name == "discriminant" && self.index == DISCRIMINANT_INDEX + self.name == crate::common::builtin_constants::DISCRIMINANT_FIELD_NAME + && self.index == DISCRIMINANT_INDEX } } diff --git a/vir/defs/high/operations_internal/ty.rs b/vir/defs/high/operations_internal/ty.rs index 6ca408ac373..f99dc803ea2 100644 --- a/vir/defs/high/operations_internal/ty.rs +++ b/vir/defs/high/operations_internal/ty.rs @@ -96,6 +96,15 @@ impl Type { } DefaultLifetimeEraser {}.fold_type(self.clone()) } + pub fn check_no_erased_lifetime(&self) { + struct LifetimeChecker {} + impl TypeWalker for LifetimeChecker { + fn walk_lifetime_const(&mut self, lifetime: &LifetimeConst) { + assert_ne!(lifetime.name, LifetimeConst::erased().name); + } + } + LifetimeChecker {}.walk_type(self); + } #[must_use] pub fn replace_lifetimes( self, @@ -200,6 +209,13 @@ impl Type { _ => false, } } + pub fn is_unique_reference(&self) -> bool { + if let Type::Reference(Reference { uniqueness, .. }) = self { + uniqueness.is_unique() + } else { + false + } + } } impl AsRef for VariantIndex { @@ -254,7 +270,7 @@ impl super::super::ast::type_decl::Union { impl LifetimeConst { pub fn erased() -> Self { LifetimeConst { - name: String::from("lft_erased"), + name: String::from(crate::common::builtin_constants::ERASED_LIFETIME_NAME), } } } @@ -357,6 +373,7 @@ impl Typed for Expression { Expression::Variant(expression) => expression.get_type(), Expression::Field(expression) => expression.get_type(), Expression::Deref(expression) => expression.get_type(), + Expression::Final(expression) => expression.get_type(), Expression::AddrOf(expression) => expression.get_type(), Expression::LabelledOld(expression) => expression.get_type(), Expression::Constant(expression) => expression.get_type(), @@ -370,6 +387,9 @@ impl Typed for Expression { Expression::FuncApp(expression) => expression.get_type(), Expression::BuiltinFuncApp(expression) => expression.get_type(), Expression::Downcast(expression) => expression.get_type(), + Expression::AccPredicate(expression) => expression.get_type(), + Expression::Unfolding(expression) => expression.get_type(), + Expression::EvalIn(expression) => expression.get_type(), } } fn set_type(&mut self, new_type: Type) { @@ -379,6 +399,7 @@ impl Typed for Expression { Expression::Variant(expression) => expression.set_type(new_type), Expression::Field(expression) => expression.set_type(new_type), Expression::Deref(expression) => expression.set_type(new_type), + Expression::Final(expression) => expression.set_type(new_type), Expression::AddrOf(expression) => expression.set_type(new_type), Expression::LabelledOld(expression) => expression.set_type(new_type), Expression::Constant(expression) => expression.set_type(new_type), @@ -392,6 +413,9 @@ impl Typed for Expression { Expression::FuncApp(expression) => expression.set_type(new_type), Expression::BuiltinFuncApp(expression) => expression.set_type(new_type), Expression::Downcast(expression) => expression.set_type(new_type), + Expression::AccPredicate(expression) => expression.set_type(new_type), + Expression::Unfolding(expression) => expression.set_type(new_type), + Expression::EvalIn(expression) => expression.set_type(new_type), } } } @@ -440,6 +464,15 @@ impl Typed for Deref { } } +impl Typed for Final { + fn get_type(&self) -> &Type { + &self.ty + } + fn set_type(&mut self, new_type: Type) { + self.ty = new_type; + } +} + impl Typed for AddrOf { fn get_type(&self) -> &Type { &self.ty @@ -502,6 +535,22 @@ impl Typed for BinaryOp { } } fn set_type(&mut self, new_type: Type) { + assert!( + !matches!( + self.op_kind, + BinaryOpKind::EqCmp + | BinaryOpKind::NeCmp + | BinaryOpKind::GtCmp + | BinaryOpKind::GeCmp + | BinaryOpKind::LtCmp + | BinaryOpKind::LeCmp + | BinaryOpKind::And + | BinaryOpKind::Or + | BinaryOpKind::Implies + ), + "cannot change the type of {:?}", + self.op_kind + ); self.left.set_type(new_type.clone()); self.right.set_type(new_type); } @@ -582,3 +631,30 @@ impl Typed for Downcast { self.base.set_type(new_type); } } + +impl Typed for AccPredicate { + fn get_type(&self) -> &Type { + &Type::Bool + } + fn set_type(&mut self, _new_type: Type) { + unreachable!(); + } +} + +impl Typed for Unfolding { + fn get_type(&self) -> &Type { + self.body.get_type() + } + fn set_type(&mut self, new_type: Type) { + self.body.set_type(new_type) + } +} + +impl Typed for EvalIn { + fn get_type(&self) -> &Type { + self.body.get_type() + } + fn set_type(&mut self, new_type: Type) { + self.body.set_type(new_type) + } +} diff --git a/vir/defs/high/operations_internal/type_decl.rs b/vir/defs/high/operations_internal/type_decl.rs index 687e2fc60b9..973ac34862a 100644 --- a/vir/defs/high/operations_internal/type_decl.rs +++ b/vir/defs/high/operations_internal/type_decl.rs @@ -4,6 +4,12 @@ use super::super::ast::{ type_decl::{Enum, Struct, Trusted, Tuple, TypeDecl, Union}, }; +impl Struct { + pub fn is_manually_managed_type(&self) -> bool { + self.structural_invariant.is_some() + } +} + impl Enum { pub fn variant(&self, variant_name: &str) -> Option<&Struct> { self.variants diff --git a/vir/defs/low/ast/expression.rs b/vir/defs/low/ast/expression.rs index 90cefcd028c..66025df3dc7 100644 --- a/vir/defs/low/ast/expression.rs +++ b/vir/defs/low/ast/expression.rs @@ -3,7 +3,7 @@ use crate::common::display; #[derive_helpers] #[derive_visitors] -#[derive(derive_more::From, derive_more::IsVariant)] +#[derive(derive_more::From, derive_more::IsVariant, derive_more::Unwrap)] pub enum Expression { /// A Viper variable. /// @@ -90,21 +90,14 @@ pub struct FieldAccessPredicate { pub position: Position, } -#[display( - fmt = "(unfolding acc({}({}), {}) in {})", - predicate, - "display::cjoin(arguments)", - permission, - base -)] +#[display(fmt = "(unfolding {} in {})", predicate, base)] pub struct Unfolding { - pub predicate: String, - pub arguments: Vec, - pub permission: Box, + pub predicate: PredicateAccessPredicate, pub base: Box, pub position: Position, } +#[derive(Copy)] pub enum UnaryOpKind { Not, Minus, diff --git a/vir/defs/low/ast/function.rs b/vir/defs/low/ast/function.rs index c4784dee55c..b2f37d13be9 100644 --- a/vir/defs/low/ast/function.rs +++ b/vir/defs/low/ast/function.rs @@ -4,6 +4,8 @@ use crate::common::display; pub enum FunctionKind { MemoryBlockBytes, CallerFor, + Snap, + SnapRange, } #[display( @@ -13,7 +15,7 @@ pub enum FunctionKind { "display::cjoin(parameters)", return_type, "display::foreach!(\" requires {}\n\", pres)", - "display::foreach!(\" ensures {}\n\", pres)", + "display::foreach!(\" ensures {}\n\", posts)", "display::option!(body, \"{{ {} }}\n\", \"\")" )] pub struct FunctionDecl { diff --git a/vir/defs/low/ast/predicate.rs b/vir/defs/low/ast/predicate.rs index dbecbc3ad5d..137c86e4dbc 100644 --- a/vir/defs/low/ast/predicate.rs +++ b/vir/defs/low/ast/predicate.rs @@ -1,14 +1,26 @@ use super::{expression::Expression, variable::VariableDecl}; use crate::common::display; +#[derive(Copy)] +pub enum PredicateKind { + MemoryBlock, + Owned, + LifetimeToken, + WithoutSnapshotFrac, + WithoutSnapshotWhole, + WithoutSnapshotWholeNonAliased, +} + #[display( - fmt = "predicate {}({}){}\n", + fmt = "predicate<{}> {}({}){}\n", + kind, "name", "display::cjoin(parameters)", "display::option!(body, \" {{\n {}\n}}\", \";\")" )] pub struct PredicateDecl { pub name: String, + pub kind: PredicateKind, pub parameters: Vec, pub body: Option, } diff --git a/vir/defs/low/ast/statement.rs b/vir/defs/low/ast/statement.rs index 1c2f9756af7..3973ac1305a 100644 --- a/vir/defs/low/ast/statement.rs +++ b/vir/defs/low/ast/statement.rs @@ -6,6 +6,7 @@ use crate::common::display; #[derive(derive_more::From, derive_more::IsVariant)] pub enum Statement { Comment(Comment), + Label(Label), LogEvent(LogEvent), Assume(Assume), Assert(Assert), @@ -24,10 +25,17 @@ pub struct Comment { pub comment: String, } +#[display(fmt = "label {}", label)] +pub struct Label { + pub label: String, + pub position: Position, +} + #[display(fmt = "log-event {}", expression)] /// Log an event by assuming a (fresh) domain function. pub struct LogEvent { pub expression: Expression, + pub position: Position, } #[display(fmt = "assume {}", expression)] diff --git a/vir/defs/low/cfg/procedure.rs b/vir/defs/low/cfg/procedure.rs index c1c48658d07..dcc5923ecb8 100644 --- a/vir/defs/low/cfg/procedure.rs +++ b/vir/defs/low/cfg/procedure.rs @@ -1,33 +1,39 @@ use crate::{ common::display, - low::ast::{expression::Expression, statement::Statement, variable::VariableDecl}, + low::ast::{ + expression::Expression, position::Position, statement::Statement, variable::VariableDecl, + }, }; +use std::collections::BTreeMap; #[display( fmt = "procedure {} {{\n{}\n{}}}\n", name, "display::foreach!(\" var {};\n\", locals)", - "display::foreach!(\"{}\n\", basic_blocks)" + "display::foreach2!(\" label {}\n{}\", basic_blocks.keys(), basic_blocks.values())" )] pub struct ProcedureDecl { pub name: String, pub locals: Vec, - pub basic_blocks: Vec, + pub custom_labels: Vec