Repository: microsoft/Armada Branch: main Commit: da7497dcf506 Files: 921 Total size: 9.1 MB Directory structure: gitextract_4_a407x8/ ├── .gitattributes ├── .gitignore ├── Armada/ │ ├── .gitignore │ ├── ArmadaCommonDefinitions.dfy │ ├── spec/ │ │ └── refinement.s.dfy │ ├── strategies/ │ │ ├── chl/ │ │ │ ├── AtomicConcurrentHoareLogic.i.dfy │ │ │ ├── AtomicConcurrentHoareLogicLemmas.i.dfy │ │ │ ├── AtomicConcurrentHoareLogicSpec.i.dfy │ │ │ ├── ConcurrentHoareLogic.i.dfy │ │ │ ├── ConcurrentHoareLogicLemmas.i.dfy │ │ │ └── ConcurrentHoareLogicSpec.i.dfy │ │ ├── combining/ │ │ │ ├── ArmadaCombining.i.dfy │ │ │ ├── ArmadaCombiningLemmas.i.dfy │ │ │ └── ArmadaCombiningSpec.i.dfy │ │ ├── generic/ │ │ │ ├── GenericArmadaAtomic.i.dfy │ │ │ ├── GenericArmadaLemmas.i.dfy │ │ │ ├── GenericArmadaPlus.i.dfy │ │ │ ├── GenericArmadaSpec.i.dfy │ │ │ ├── LiftAtomicToAtomic.i.dfy │ │ │ ├── LiftFromAtomic.i.dfy │ │ │ └── LiftToAtomic.i.dfy │ │ ├── invariants.i.dfy │ │ ├── reduction/ │ │ │ ├── AtomicReduction.i.dfy │ │ │ ├── AtomicReductionLemmas.i.dfy │ │ │ ├── AtomicReductionSpec.i.dfy │ │ │ ├── CohenLamportReduction.i.dfy │ │ │ ├── CohenLamportReductionLemmas.i.dfy │ │ │ ├── CohenLamportReductionSpec.i.dfy │ │ │ ├── RefinementViaReduction.i.dfy │ │ │ ├── RefinementViaReductionLemmas.i.dfy │ │ │ └── RefinementViaReductionSpec.i.dfy │ │ ├── refinement/ │ │ │ ├── AnnotatedBehavior.i.dfy │ │ │ ├── GeneralRefinementLemmas.i.dfy │ │ │ └── RefinementConvolution.i.dfy │ │ ├── starweakening/ │ │ │ ├── StarWeakening.i.dfy │ │ │ └── StarWeakeningSpec.i.dfy │ │ ├── tsoelimination/ │ │ │ ├── TSOElimination.i.dfy │ │ │ ├── TSOEliminationLemmas.i.dfy │ │ │ └── TSOEliminationSpec.i.dfy │ │ ├── varhiding/ │ │ │ ├── VarHiding.i.dfy │ │ │ └── VarHidingSpec.i.dfy │ │ ├── varintro/ │ │ │ ├── VarIntro.i.dfy │ │ │ └── VarIntroSpec.i.dfy │ │ └── weakening/ │ │ ├── Weakening.i.dfy │ │ └── WeakeningSpec.i.dfy │ └── util/ │ ├── collections/ │ │ ├── MapSum.i.dfy │ │ ├── SeqSum.i.dfy │ │ ├── maps.i.dfy │ │ ├── maps.s.dfy │ │ ├── seqs.i.dfy │ │ ├── seqs.s.dfy │ │ └── sets.i.dfy │ ├── functions.i.dfy │ ├── math/ │ │ ├── .gitignore │ │ ├── div.i.dfy │ │ ├── div_auto.i.dfy │ │ ├── div_auto_proofs.i.dfy │ │ ├── div_boogie.i.dfy │ │ ├── div_def.i.dfy │ │ ├── div_nonlinear.i.dfy │ │ ├── mod_auto.i.dfy │ │ ├── mod_auto_proofs.i.dfy │ │ ├── mul.i.dfy │ │ ├── mul_auto.i.dfy │ │ ├── mul_auto_proofs.i.dfy │ │ ├── mul_nonlinear.i.dfy │ │ ├── power.i.dfy │ │ └── powers.i.dfy │ ├── option.s.dfy │ └── types.s.dfy ├── BUILD.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── NOTICE.txt ├── README.md ├── SConstruct1 ├── SConstruct2 ├── SECURITY.md ├── Source/ │ ├── Armada/ │ │ ├── AbstractProofGenerator.cs │ │ ├── Armada.atg │ │ ├── ArmadaAst.cs │ │ ├── ArmadaExpr.cs │ │ ├── ArmadaHelper.cs │ │ ├── ArmadaMain.cs │ │ ├── ArmadaOptions.cs │ │ ├── ArmadaParser.cs │ │ ├── ArmadaPipeline.csproj │ │ ├── ArmadaRValue.cs │ │ ├── ArmadaStructs.cs │ │ ├── AssumeIntro.cs │ │ ├── AtomicSpec.cs │ │ ├── BigIntegerParser.cs │ │ ├── Cloner.cs │ │ ├── Combining.cs │ │ ├── Compiler-clight.cs │ │ ├── Compiler.cs │ │ ├── ConstraintCollector.cs │ │ ├── DeclCollector.cs │ │ ├── ExpressionBuilder.cs │ │ ├── GlobalVarHiding.cs │ │ ├── GlobalVarIntro.cs │ │ ├── NextRoutine.cs │ │ ├── PathPrinter.cs │ │ ├── PredicateBuilder.cs │ │ ├── Printer.cs │ │ ├── Prioritization.cs │ │ ├── ProofFiles.cs │ │ ├── ProofGenerationParams.cs │ │ ├── ProofGenerator.cs │ │ ├── Reduction.cs │ │ ├── RefinementTransformer.cs │ │ ├── Reporting.cs │ │ ├── ResolutionContext.cs │ │ ├── Resolver.cs │ │ ├── Rewriter.cs │ │ ├── SccGraph.cs │ │ ├── StackVarHiding.cs │ │ ├── StackVarIntro.cs │ │ ├── StarWeakening.cs │ │ ├── StateMachineTranslator.cs │ │ ├── StepPrinter.cs │ │ ├── StraightlineBehavior.cs │ │ ├── SymbolTable.cs │ │ ├── TSOElimination.cs │ │ ├── Translator.cs │ │ ├── Triggers/ │ │ │ ├── QuantifierSplitter.cs │ │ │ ├── QuantifiersCollection.cs │ │ │ ├── QuantifiersCollector.cs │ │ │ ├── TriggerExtensions.cs │ │ │ ├── TriggerUtils.cs │ │ │ └── TriggersCollector.cs │ │ ├── Util.cs │ │ ├── VarHiding.cs │ │ ├── VarIntro.cs │ │ └── Weakening.cs │ ├── Armada.sln │ ├── ArmadaDriver/ │ │ ├── ArmadaDriver.cs │ │ └── ArmadaDriver.csproj │ ├── Directory.Build.props │ └── dotnet-tools.json ├── Test/ │ ├── armada-parser/ │ │ ├── .gitignore │ │ ├── OGcounter.arm │ │ ├── scheduler_atomic.arm │ │ ├── scheduler_impl.arm │ │ ├── scheduler_simplify.arm │ │ ├── scheduler_spec.arm │ │ ├── test-var-hiding-fail.arm │ │ ├── test.arm │ │ ├── test2.arm │ │ ├── test3.arm │ │ ├── test4.arm │ │ ├── test5.arm │ │ ├── test6.arm │ │ └── test7.arm │ ├── assume-intro/ │ │ ├── .gitignore │ │ ├── ABHelpers.dfy │ │ ├── test.arm │ │ ├── test2.arm │ │ └── test3.arm │ ├── barrier/ │ │ ├── .gitignore │ │ ├── barrier.arm │ │ └── extra.dfy │ ├── bitvector/ │ │ ├── .gitignore │ │ ├── bv.dfy │ │ └── test.arm │ ├── clight/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── barrier.arm │ │ ├── bench/ │ │ │ ├── bench_armada.c │ │ │ ├── bench_armada_gcc.c │ │ │ ├── bench_lfds.c │ │ │ └── bench_sound_queue.c │ │ ├── extern.h │ │ ├── failed/ │ │ │ ├── failed_0.c │ │ │ └── failed_1.c │ │ ├── lock.arm │ │ ├── queue.arm │ │ ├── sqrt.arm │ │ └── upload.sh │ ├── combining/ │ │ ├── .gitignore │ │ └── test.arm │ ├── counter/ │ │ ├── .gitignore │ │ ├── A.arm │ │ ├── AB.arm │ │ ├── B.arm │ │ ├── BC.arm │ │ ├── C.arm │ │ ├── CD.arm │ │ ├── D.arm │ │ ├── DE.arm │ │ ├── E.arm │ │ ├── EF.arm │ │ ├── F.arm │ │ ├── FG.arm │ │ ├── G.arm │ │ ├── GI.arm │ │ ├── I.arm │ │ ├── IJ.arm │ │ ├── J.arm │ │ ├── SharedStructs.arm │ │ ├── Z.arm │ │ └── extra.dfy │ ├── estimate-sqrt/ │ │ ├── .gitignore │ │ └── sqrt.arm │ ├── mcslock/ │ │ ├── .gitignore │ │ ├── lock-array.arm │ │ └── lock.c │ ├── qbss/ │ │ ├── .gitignore │ │ ├── assumeintroproof_invariant.dfy │ │ ├── auxiliary_helper.dfy │ │ ├── bv.dfy │ │ ├── queue.arm │ │ ├── queue_abstractloginvariant.dfy │ │ ├── queue_tsobypassing_abstractloginvariant.dfy │ │ └── tau_invariant_helper.dfy │ ├── qbss_benchmark/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── benchmark.c │ │ ├── benchmark_lfds.c │ │ ├── liblfds711/ │ │ │ ├── build/ │ │ │ │ ├── gcc_gnumake/ │ │ │ │ │ └── Makefile │ │ │ │ ├── gcc_gnumake_kbuild/ │ │ │ │ │ ├── Kbuild │ │ │ │ │ └── Makefile │ │ │ │ ├── msvc_gnumake/ │ │ │ │ │ ├── liblfds711.def │ │ │ │ │ └── makefile │ │ │ │ └── wdk_7.1/ │ │ │ │ ├── dirs │ │ │ │ ├── driver_entry_renamed_to_avoid_compiler_warning.c │ │ │ │ ├── liblfds711.def │ │ │ │ ├── readme_before_win_kernel_build.txt │ │ │ │ ├── runme_before_win_kernel_dynamic_lib_build.bat │ │ │ │ ├── runme_before_win_kernel_static_lib_build.bat │ │ │ │ ├── sources.dynamic │ │ │ │ └── sources.static │ │ │ ├── inc/ │ │ │ │ ├── liblfds711/ │ │ │ │ │ ├── lfds711_btree_addonly_unbalanced.h │ │ │ │ │ ├── lfds711_freelist.h │ │ │ │ │ ├── lfds711_hash_addonly.h │ │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered.h │ │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered.h │ │ │ │ │ ├── lfds711_misc.h │ │ │ │ │ ├── lfds711_porting_abstraction_layer_compiler.h │ │ │ │ │ ├── lfds711_porting_abstraction_layer_operating_system.h │ │ │ │ │ ├── lfds711_porting_abstraction_layer_processor.h │ │ │ │ │ ├── lfds711_prng.h │ │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer.h │ │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer.h │ │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer.h │ │ │ │ │ ├── lfds711_ringbuffer.h │ │ │ │ │ └── lfds711_stack.h │ │ │ │ └── liblfds711.h │ │ │ ├── obj/ │ │ │ │ └── .gitkeep │ │ │ └── src/ │ │ │ ├── lfds711_btree_addonly_unbalanced/ │ │ │ │ ├── lfds711_btree_addonly_unbalanced_cleanup.c │ │ │ │ ├── lfds711_btree_addonly_unbalanced_get.c │ │ │ │ ├── lfds711_btree_addonly_unbalanced_init.c │ │ │ │ ├── lfds711_btree_addonly_unbalanced_insert.c │ │ │ │ ├── lfds711_btree_addonly_unbalanced_internal.h │ │ │ │ └── lfds711_btree_addonly_unbalanced_query.c │ │ │ ├── lfds711_freelist/ │ │ │ │ ├── lfds711_freelist_cleanup.c │ │ │ │ ├── lfds711_freelist_init.c │ │ │ │ ├── lfds711_freelist_internal.h │ │ │ │ ├── lfds711_freelist_pop.c │ │ │ │ ├── lfds711_freelist_push.c │ │ │ │ └── lfds711_freelist_query.c │ │ │ ├── lfds711_hash_addonly/ │ │ │ │ ├── lfds711_hash_addonly_cleanup.c │ │ │ │ ├── lfds711_hash_addonly_get.c │ │ │ │ ├── lfds711_hash_addonly_init.c │ │ │ │ ├── lfds711_hash_addonly_insert.c │ │ │ │ ├── lfds711_hash_addonly_internal.h │ │ │ │ ├── lfds711_hash_addonly_iterate.c │ │ │ │ └── lfds711_hash_addonly_query.c │ │ │ ├── lfds711_list_addonly_singlylinked_ordered/ │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_cleanup.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_get.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_init.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_insert.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_internal.h │ │ │ │ └── lfds711_list_addonly_singlylinked_ordered_query.c │ │ │ ├── lfds711_list_addonly_singlylinked_unordered/ │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_cleanup.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_get.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_init.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_insert.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_internal.h │ │ │ │ └── lfds711_list_addonly_singlylinked_unordered_query.c │ │ │ ├── lfds711_misc/ │ │ │ │ ├── lfds711_misc_globals.c │ │ │ │ ├── lfds711_misc_internal.h │ │ │ │ ├── lfds711_misc_internal_backoff_init.c │ │ │ │ └── lfds711_misc_query.c │ │ │ ├── lfds711_prng/ │ │ │ │ ├── lfds711_prng_init.c │ │ │ │ └── lfds711_prng_internal.h │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer/ │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_cleanup.c │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_dequeue.c │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_enqueue.c │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_init.c │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_internal.h │ │ │ │ └── lfds711_queue_bounded_manyproducer_manyconsumer_query.c │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer/ │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_cleanup.c │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_dequeue.c │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_enqueue.c │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_enqueue.ll │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_init.c │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_internal.h │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_query.c │ │ │ │ └── test.ll │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer/ │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_cleanup.c │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_dequeue.c │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_enqueue.c │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_init.c │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_internal.h │ │ │ │ └── lfds711_queue_unbounded_manyproducer_manyconsumer_query.c │ │ │ ├── lfds711_ringbuffer/ │ │ │ │ ├── lfds711_ringbuffer_cleanup.c │ │ │ │ ├── lfds711_ringbuffer_init.c │ │ │ │ ├── lfds711_ringbuffer_internal.h │ │ │ │ ├── lfds711_ringbuffer_query.c │ │ │ │ ├── lfds711_ringbuffer_read.c │ │ │ │ └── lfds711_ringbuffer_write.c │ │ │ ├── lfds711_stack/ │ │ │ │ ├── lfds711_stack_cleanup.c │ │ │ │ ├── lfds711_stack_init.c │ │ │ │ ├── lfds711_stack_internal.h │ │ │ │ ├── lfds711_stack_pop.c │ │ │ │ ├── lfds711_stack_push.c │ │ │ │ └── lfds711_stack_query.c │ │ │ └── liblfds711_internal.h │ │ ├── liblfds711_modulo/ │ │ │ ├── build/ │ │ │ │ ├── gcc_gnumake/ │ │ │ │ │ └── Makefile │ │ │ │ ├── gcc_gnumake_kbuild/ │ │ │ │ │ ├── Kbuild │ │ │ │ │ └── Makefile │ │ │ │ ├── msvc_gnumake/ │ │ │ │ │ ├── liblfds711.def │ │ │ │ │ └── makefile │ │ │ │ └── wdk_7.1/ │ │ │ │ ├── dirs │ │ │ │ ├── driver_entry_renamed_to_avoid_compiler_warning.c │ │ │ │ ├── liblfds711.def │ │ │ │ ├── readme_before_win_kernel_build.txt │ │ │ │ ├── runme_before_win_kernel_dynamic_lib_build.bat │ │ │ │ ├── runme_before_win_kernel_static_lib_build.bat │ │ │ │ ├── sources.dynamic │ │ │ │ └── sources.static │ │ │ ├── inc/ │ │ │ │ ├── liblfds711/ │ │ │ │ │ ├── lfds711_btree_addonly_unbalanced.h │ │ │ │ │ ├── lfds711_freelist.h │ │ │ │ │ ├── lfds711_hash_addonly.h │ │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered.h │ │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered.h │ │ │ │ │ ├── lfds711_misc.h │ │ │ │ │ ├── lfds711_porting_abstraction_layer_compiler.h │ │ │ │ │ ├── lfds711_porting_abstraction_layer_operating_system.h │ │ │ │ │ ├── lfds711_porting_abstraction_layer_processor.h │ │ │ │ │ ├── lfds711_prng.h │ │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer.h │ │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer.h │ │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer.h │ │ │ │ │ ├── lfds711_ringbuffer.h │ │ │ │ │ └── lfds711_stack.h │ │ │ │ └── liblfds711.h │ │ │ ├── obj/ │ │ │ │ └── .gitkeep │ │ │ └── src/ │ │ │ ├── lfds711_btree_addonly_unbalanced/ │ │ │ │ ├── lfds711_btree_addonly_unbalanced_cleanup.c │ │ │ │ ├── lfds711_btree_addonly_unbalanced_get.c │ │ │ │ ├── lfds711_btree_addonly_unbalanced_init.c │ │ │ │ ├── lfds711_btree_addonly_unbalanced_insert.c │ │ │ │ ├── lfds711_btree_addonly_unbalanced_internal.h │ │ │ │ └── lfds711_btree_addonly_unbalanced_query.c │ │ │ ├── lfds711_freelist/ │ │ │ │ ├── lfds711_freelist_cleanup.c │ │ │ │ ├── lfds711_freelist_init.c │ │ │ │ ├── lfds711_freelist_internal.h │ │ │ │ ├── lfds711_freelist_pop.c │ │ │ │ ├── lfds711_freelist_push.c │ │ │ │ └── lfds711_freelist_query.c │ │ │ ├── lfds711_hash_addonly/ │ │ │ │ ├── lfds711_hash_addonly_cleanup.c │ │ │ │ ├── lfds711_hash_addonly_get.c │ │ │ │ ├── lfds711_hash_addonly_init.c │ │ │ │ ├── lfds711_hash_addonly_insert.c │ │ │ │ ├── lfds711_hash_addonly_internal.h │ │ │ │ ├── lfds711_hash_addonly_iterate.c │ │ │ │ └── lfds711_hash_addonly_query.c │ │ │ ├── lfds711_list_addonly_singlylinked_ordered/ │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_cleanup.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_get.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_init.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_insert.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_ordered_internal.h │ │ │ │ └── lfds711_list_addonly_singlylinked_ordered_query.c │ │ │ ├── lfds711_list_addonly_singlylinked_unordered/ │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_cleanup.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_get.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_init.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_insert.c │ │ │ │ ├── lfds711_list_addonly_singlylinked_unordered_internal.h │ │ │ │ └── lfds711_list_addonly_singlylinked_unordered_query.c │ │ │ ├── lfds711_misc/ │ │ │ │ ├── lfds711_misc_globals.c │ │ │ │ ├── lfds711_misc_internal.h │ │ │ │ ├── lfds711_misc_internal_backoff_init.c │ │ │ │ └── lfds711_misc_query.c │ │ │ ├── lfds711_prng/ │ │ │ │ ├── lfds711_prng_init.c │ │ │ │ └── lfds711_prng_internal.h │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer/ │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_cleanup.c │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_dequeue.c │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_enqueue.c │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_init.c │ │ │ │ ├── lfds711_queue_bounded_manyproducer_manyconsumer_internal.h │ │ │ │ └── lfds711_queue_bounded_manyproducer_manyconsumer_query.c │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer/ │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_cleanup.c │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_dequeue.c │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_enqueue.c │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_enqueue.ll │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_init.c │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_internal.h │ │ │ │ ├── lfds711_queue_bounded_singleproducer_singleconsumer_query.c │ │ │ │ └── test.ll │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer/ │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_cleanup.c │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_dequeue.c │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_enqueue.c │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_init.c │ │ │ │ ├── lfds711_queue_unbounded_manyproducer_manyconsumer_internal.h │ │ │ │ └── lfds711_queue_unbounded_manyproducer_manyconsumer_query.c │ │ │ ├── lfds711_ringbuffer/ │ │ │ │ ├── lfds711_ringbuffer_cleanup.c │ │ │ │ ├── lfds711_ringbuffer_init.c │ │ │ │ ├── lfds711_ringbuffer_internal.h │ │ │ │ ├── lfds711_ringbuffer_query.c │ │ │ │ ├── lfds711_ringbuffer_read.c │ │ │ │ └── lfds711_ringbuffer_write.c │ │ │ ├── lfds711_stack/ │ │ │ │ ├── lfds711_stack_cleanup.c │ │ │ │ ├── lfds711_stack_init.c │ │ │ │ ├── lfds711_stack_internal.h │ │ │ │ ├── lfds711_stack_pop.c │ │ │ │ ├── lfds711_stack_push.c │ │ │ │ └── lfds711_stack_query.c │ │ │ └── liblfds711_internal.h │ │ ├── queue.arm │ │ ├── queue.patch │ │ └── run_benchmarks.py │ ├── reduction/ │ │ ├── .gitignore │ │ ├── test.arm │ │ └── test2.arm │ ├── regions/ │ │ ├── .gitignore │ │ ├── pointers.arm │ │ ├── test.arm │ │ ├── test2.arm │ │ ├── test3.arm │ │ └── test4.arm │ ├── starweakening/ │ │ ├── .gitignore │ │ ├── test.arm │ │ ├── test2.arm │ │ ├── test3.arm │ │ └── test4.arm │ ├── tsoelim/ │ │ ├── .gitignore │ │ ├── test.arm │ │ ├── test2.arm │ │ ├── test3.arm │ │ └── test4.arm │ ├── varhiding/ │ │ ├── .gitignore │ │ ├── VarHidingManualProof.i.dfy │ │ ├── test.arm │ │ ├── test2.arm │ │ ├── test3.arm │ │ ├── test4.arm │ │ └── test5.arm │ ├── varintro/ │ │ ├── .gitignore │ │ ├── test.arm │ │ ├── test2.arm │ │ ├── test3.arm │ │ └── test4.arm │ └── weakening/ │ ├── .gitignore │ ├── ArithmeticFacts.dfy │ ├── TestWeakeningProof.dfy │ ├── test.arm │ ├── test2.arm │ └── test3.arm ├── experimental/ │ ├── .gitignore │ ├── EditorPlugins/ │ │ └── mle-vscode/ │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .vscodeignore │ │ ├── changelog.md │ │ ├── notes.md │ │ ├── package.json │ │ ├── readme.md │ │ ├── src/ │ │ │ ├── extension.ts │ │ │ ├── refactor-view.ts │ │ │ ├── test/ │ │ │ │ ├── runTest.ts │ │ │ │ └── suite/ │ │ │ │ ├── extension.test.ts │ │ │ │ └── index.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── view/ │ │ │ ├── main.css │ │ │ ├── main.js │ │ │ ├── reset.css │ │ │ └── vscode.css │ │ └── vsc-extension-quickstart.md │ ├── README.md │ ├── SConstruct │ ├── Source/ │ │ ├── .gitignore │ │ ├── Armada/ │ │ │ ├── .gitignore │ │ │ ├── AST/ │ │ │ │ ├── Base.cs │ │ │ │ ├── Expr.cs │ │ │ │ ├── Printer.cs │ │ │ │ ├── ProofStrategy.cs │ │ │ │ ├── Scope.cs │ │ │ │ ├── Statement.cs │ │ │ │ └── Type.cs │ │ │ ├── Armada.csproj │ │ │ ├── Atomic/ │ │ │ │ ├── AtomicPrinter.cs │ │ │ │ └── AtomicSpec.cs │ │ │ ├── IncludeProcessor.cs │ │ │ ├── Program.cs │ │ │ ├── Proof/ │ │ │ │ ├── InvariantPrinter.cs │ │ │ │ ├── Myers.cs │ │ │ │ ├── ProofPrinter.cs │ │ │ │ ├── ProofSpec.cs │ │ │ │ ├── VarHidingPrinter.cs │ │ │ │ ├── VarIntroPrinter.cs │ │ │ │ ├── WeakeningPrinter.cs │ │ │ │ └── WeakeningSpec.cs │ │ │ ├── Reporting.cs │ │ │ ├── Resolver.cs │ │ │ ├── Starmada.atg │ │ │ ├── Util.cs │ │ │ └── dotnet-tools.json │ │ ├── Armada.sln │ │ └── Editor/ │ │ ├── BasicEquality.cs │ │ ├── Driver.cs │ │ ├── Editor.cs │ │ ├── Editor.csproj │ │ ├── IO.cs │ │ ├── Mapper.cs │ │ ├── Paper.md │ │ ├── Position.cs │ │ ├── Readme.md │ │ ├── RefactorBuffer.cs │ │ ├── StatementEquality.cs │ │ ├── StatementSeq.cs │ │ ├── Summary.cs │ │ └── Utils.cs │ ├── Tests/ │ │ ├── .gitignore │ │ ├── editor/ │ │ │ ├── .gitignore │ │ │ ├── basic/ │ │ │ │ ├── ab/ │ │ │ │ │ ├── arg │ │ │ │ │ ├── expect/ │ │ │ │ │ │ └── AB.arm │ │ │ │ │ ├── in │ │ │ │ │ └── proj/ │ │ │ │ │ └── AB.arm │ │ │ │ ├── atomic/ │ │ │ │ │ ├── arg │ │ │ │ │ ├── expect/ │ │ │ │ │ │ └── t.arm │ │ │ │ │ ├── in │ │ │ │ │ └── proj/ │ │ │ │ │ └── t.arm │ │ │ │ ├── if/ │ │ │ │ │ ├── arg │ │ │ │ │ ├── expect/ │ │ │ │ │ │ └── t.arm │ │ │ │ │ ├── in │ │ │ │ │ └── proj/ │ │ │ │ │ └── t.arm │ │ │ │ ├── ifcond/ │ │ │ │ │ ├── arg │ │ │ │ │ ├── expect/ │ │ │ │ │ │ └── t.arm │ │ │ │ │ ├── in │ │ │ │ │ └── proj/ │ │ │ │ │ └── t.arm │ │ │ │ └── while/ │ │ │ │ ├── arg │ │ │ │ ├── expect/ │ │ │ │ │ └── t.arm │ │ │ │ ├── in │ │ │ │ └── proj/ │ │ │ │ └── t.arm │ │ │ └── range/ │ │ │ ├── AST/ │ │ │ │ ├── arg │ │ │ │ ├── expect/ │ │ │ │ │ ├── A.arm │ │ │ │ │ ├── AB.arm │ │ │ │ │ ├── B.arm │ │ │ │ │ ├── BC.arm │ │ │ │ │ ├── C.arm │ │ │ │ │ ├── CD.arm │ │ │ │ │ └── D.arm │ │ │ │ ├── in │ │ │ │ └── proj/ │ │ │ │ ├── A.arm │ │ │ │ ├── AB.arm │ │ │ │ ├── B.arm │ │ │ │ ├── BC.arm │ │ │ │ ├── C.arm │ │ │ │ ├── CD.arm │ │ │ │ └── D.arm │ │ │ ├── ASTC/ │ │ │ │ ├── arg │ │ │ │ ├── expect/ │ │ │ │ │ ├── A.arm │ │ │ │ │ ├── AB.arm │ │ │ │ │ ├── B.arm │ │ │ │ │ ├── BC.arm │ │ │ │ │ ├── C.arm │ │ │ │ │ ├── CD.arm │ │ │ │ │ └── D.arm │ │ │ │ ├── in │ │ │ │ └── proj/ │ │ │ │ ├── A.arm │ │ │ │ ├── AB.arm │ │ │ │ ├── B.arm │ │ │ │ ├── BC.arm │ │ │ │ ├── C.arm │ │ │ │ ├── CD.arm │ │ │ │ └── D.arm │ │ │ ├── ASTS/ │ │ │ │ ├── arg │ │ │ │ ├── expect/ │ │ │ │ │ ├── A.arm │ │ │ │ │ ├── AB.arm │ │ │ │ │ ├── B.arm │ │ │ │ │ ├── BC.arm │ │ │ │ │ ├── C.arm │ │ │ │ │ ├── CD.arm │ │ │ │ │ └── D.arm │ │ │ │ ├── in │ │ │ │ └── proj/ │ │ │ │ ├── A.arm │ │ │ │ ├── AB.arm │ │ │ │ ├── B.arm │ │ │ │ ├── BC.arm │ │ │ │ ├── C.arm │ │ │ │ ├── CD.arm │ │ │ │ └── D.arm │ │ │ └── counter/ │ │ │ ├── arg │ │ │ ├── in │ │ │ └── proj/ │ │ │ ├── A.arm │ │ │ ├── AB.arm │ │ │ ├── B.arm │ │ │ ├── BC.arm │ │ │ ├── C.arm │ │ │ ├── CD.arm │ │ │ ├── D.arm │ │ │ ├── DE.arm │ │ │ ├── E.arm │ │ │ ├── EF.arm │ │ │ ├── F.arm │ │ │ ├── FG.arm │ │ │ ├── G.arm │ │ │ ├── GI.arm │ │ │ ├── I.arm │ │ │ ├── IJ.arm │ │ │ ├── J.arm │ │ │ ├── SharedStructs.arm │ │ │ ├── Z.arm │ │ │ └── extra.dfy │ │ ├── fail/ │ │ │ ├── arr.arm │ │ │ └── fail.arm │ │ ├── legacy-armada/ │ │ │ ├── .gitignore │ │ │ └── MyConcreteAProg.arm │ │ ├── libcuckoo/ │ │ │ ├── Makefile │ │ │ ├── armada.sh │ │ │ ├── carmada.cpp │ │ │ ├── cpp/ │ │ │ │ ├── cuckoohash_config.hh │ │ │ │ ├── cuckoohash_map.hh │ │ │ │ ├── cuckoohash_util.hh │ │ │ │ └── libcuckoo_bucket_container.hh │ │ │ ├── cuckoo.arm │ │ │ └── cuckoo2.arm │ │ ├── parser/ │ │ │ ├── AST/ │ │ │ │ ├── A.arm │ │ │ │ ├── A.arm.expect │ │ │ │ ├── AB.arm │ │ │ │ ├── AB.arm.expect │ │ │ │ ├── B.arm │ │ │ │ ├── B.arm.expect │ │ │ │ ├── BC.arm │ │ │ │ ├── BC.arm.expect │ │ │ │ ├── C.arm │ │ │ │ ├── C.arm.expect │ │ │ │ ├── CD.arm │ │ │ │ ├── CD.arm.expect │ │ │ │ ├── D.arm │ │ │ │ ├── D.arm.expect │ │ │ │ ├── DE.arm │ │ │ │ ├── E.arm │ │ │ │ ├── EndlessExpression.arm │ │ │ │ ├── EndlessExpression.arm.expect │ │ │ │ ├── Statement.arm │ │ │ │ ├── Statement.arm.expect │ │ │ │ ├── array.arm │ │ │ │ ├── array.c │ │ │ │ ├── state.arm │ │ │ │ ├── state.arm.expect │ │ │ │ ├── struct.arm │ │ │ │ ├── struct.arm.expect │ │ │ │ ├── suffix.arm │ │ │ │ └── suffix.arm.expect │ │ │ ├── TypeResolver/ │ │ │ │ ├── boundedInt.arm │ │ │ │ ├── boundedInt.arm.expect │ │ │ │ ├── collectionType.arm │ │ │ │ ├── collectionType.arm.expect │ │ │ │ ├── ifExpr.arm │ │ │ │ ├── literalExpr.arm │ │ │ │ ├── malloc.arm │ │ │ │ ├── method.arm │ │ │ │ ├── pointer.arm │ │ │ │ └── struct.arm │ │ │ ├── compilerTest/ │ │ │ │ ├── arrayDecl.arm │ │ │ │ ├── createThread.arm │ │ │ │ ├── joinThread.arm │ │ │ │ ├── memoryAlloc.arm │ │ │ │ ├── methodDecl.arm │ │ │ │ ├── pointerUsage.arm │ │ │ │ ├── primitiveTypes.arm │ │ │ │ └── structDecl.arm │ │ │ ├── counter/ │ │ │ │ ├── A.arm │ │ │ │ ├── AB.arm │ │ │ │ ├── B.arm │ │ │ │ ├── BC.arm │ │ │ │ ├── C.arm │ │ │ │ ├── CD.arm │ │ │ │ ├── D.arm │ │ │ │ ├── DE.arm │ │ │ │ ├── E.arm │ │ │ │ ├── EF.arm │ │ │ │ ├── F.arm │ │ │ │ ├── FG.arm │ │ │ │ ├── G.arm │ │ │ │ ├── GI.arm │ │ │ │ ├── I.arm │ │ │ │ ├── IJ.arm │ │ │ │ ├── J.arm │ │ │ │ ├── SharedStructs.arm │ │ │ │ ├── Z.arm │ │ │ │ └── extra.dfy │ │ │ ├── fstar/ │ │ │ │ ├── alloc/ │ │ │ │ │ └── alloc.arm │ │ │ │ ├── arraytest/ │ │ │ │ │ └── arraytest.arm │ │ │ │ ├── assert/ │ │ │ │ │ └── assert.arm │ │ │ │ ├── assume/ │ │ │ │ │ └── assume.arm │ │ │ │ ├── atomic/ │ │ │ │ │ └── atomic.arm │ │ │ │ ├── atomicExchange/ │ │ │ │ │ └── atomicExchange.arm │ │ │ │ ├── atomicMethodCall/ │ │ │ │ │ └── atomicMethodCall.arm │ │ │ │ ├── atomicRecursive/ │ │ │ │ │ └── atomicRecursive.arm │ │ │ │ ├── atomicWhile/ │ │ │ │ │ └── atomicWhile.arm │ │ │ │ ├── binaryOperator/ │ │ │ │ │ └── binaryOperator.arm │ │ │ │ ├── boundedInt/ │ │ │ │ │ └── boundedInt.arm │ │ │ │ ├── break/ │ │ │ │ │ └── break.arm │ │ │ │ ├── code/ │ │ │ │ │ └── code.arm │ │ │ │ ├── compareAndSwap/ │ │ │ │ │ └── compareAndSwap.arm │ │ │ │ ├── conversionExpr/ │ │ │ │ │ └── conversionExpr.arm │ │ │ │ ├── createThread/ │ │ │ │ │ └── createThread.arm │ │ │ │ ├── datatype/ │ │ │ │ │ └── datatype.arm │ │ │ │ ├── fence/ │ │ │ │ │ └── fence.arm │ │ │ │ ├── generatedAssign/ │ │ │ │ │ └── generatedAssign.arm │ │ │ │ ├── goto/ │ │ │ │ │ └── goto.arm │ │ │ │ ├── ifExpr/ │ │ │ │ │ └── ifExpr.arm │ │ │ │ ├── ifStmt/ │ │ │ │ │ └── ifStmt.arm │ │ │ │ ├── ifundefined/ │ │ │ │ │ └── ifundefined.arm │ │ │ │ ├── invariantDecl_MaintainedIfStmtSatisfies/ │ │ │ │ │ └── invariantDecl.arm │ │ │ │ ├── invariantDecl_MaintainedIfVarsUnchanged/ │ │ │ │ │ └── invariantDecl.arm │ │ │ │ ├── literalExpr/ │ │ │ │ │ └── literalExpr.arm │ │ │ │ ├── mapComprehension/ │ │ │ │ │ └── mapComprehension.arm │ │ │ │ ├── maptest/ │ │ │ │ │ └── maptest.arm │ │ │ │ ├── matchCase/ │ │ │ │ │ └── matchCase.arm │ │ │ │ ├── methodCall/ │ │ │ │ │ └── methodCall.arm │ │ │ │ ├── quantifierexpr/ │ │ │ │ │ └── quantifierexpr.arm │ │ │ │ ├── recursiveMatch/ │ │ │ │ │ └── recursiveMatch.arm │ │ │ │ ├── relation/ │ │ │ │ │ └── relation.arm │ │ │ │ ├── seqtest/ │ │ │ │ │ └── seqtest.arm │ │ │ │ ├── setComprehension/ │ │ │ │ │ └── setComprehension.arm │ │ │ │ ├── settest/ │ │ │ │ │ └── settest.arm │ │ │ │ ├── somehow/ │ │ │ │ │ └── somehow.arm │ │ │ │ ├── somehowLock/ │ │ │ │ │ └── somehow.arm │ │ │ │ ├── struct/ │ │ │ │ │ └── struct.arm │ │ │ │ ├── subroutine/ │ │ │ │ │ └── subroutine.arm │ │ │ │ ├── unaryOperator/ │ │ │ │ │ └── unaryOperator.arm │ │ │ │ ├── while/ │ │ │ │ │ └── while.arm │ │ │ │ └── wildcard/ │ │ │ │ └── wildcard.arm │ │ │ ├── includeTest/ │ │ │ │ ├── A.arm │ │ │ │ ├── A.arm.expect │ │ │ │ ├── B.arm │ │ │ │ ├── B.arm.expect │ │ │ │ ├── C.arm │ │ │ │ └── C.arm.expect │ │ │ ├── ogCounterExample.arm │ │ │ └── test.arm │ │ └── proof/ │ │ ├── globalVarsUnmodifiable/ │ │ │ └── globalVarsUnmodifiable.arm │ │ ├── varHiding/ │ │ │ └── varHiding.arm │ │ ├── varIntro/ │ │ │ └── varIntro.arm │ │ └── varIntroWithAtomic/ │ │ └── varIntro.arm │ ├── docker/ │ │ └── Dockerfile │ ├── fstar.opam │ ├── grammar.txt │ ├── lib/ │ │ ├── .gitignore │ │ ├── Armada.Action.fst │ │ ├── Armada.Base.fst │ │ ├── Armada.BinaryOp.fst │ │ ├── Armada.BoundedInt.fst │ │ ├── Armada.Computation.fst │ │ ├── Armada.Expression.fst │ │ ├── Armada.Globals.fst │ │ ├── Armada.Init.fst │ │ ├── Armada.Memory.fst │ │ ├── Armada.Pointer.fst │ │ ├── Armada.Program.fst │ │ ├── Armada.State.fst │ │ ├── Armada.Statement.fst │ │ ├── Armada.Step.fst │ │ ├── Armada.Thread.fst │ │ ├── Armada.Threads.fst │ │ ├── Armada.Transition.fst │ │ ├── Armada.Type.fst │ │ ├── Armada.UnaryOp.fst │ │ ├── GlobalVarExampleInvariant.fst │ │ ├── Makefile │ │ ├── MyAProg.fst │ │ ├── MyAtomicAProg.fst │ │ ├── MyAtomicBInvariant.fst │ │ ├── MyAtomicBProg.fst │ │ ├── MyAtomicHProg.fst │ │ ├── MyAtomicLProg.fst │ │ ├── MyAtomicToRegularRefinement.fst │ │ ├── MyBProg.fst │ │ ├── MyHProg.arm │ │ ├── MyHProg.fst │ │ ├── MyLProg.arm │ │ ├── MyLProg.fst │ │ ├── MyList.fst │ │ ├── MyProgramInvariant.fst │ │ ├── MyProgramProof.fst │ │ ├── MyRegularToAtomicRefinement.fst │ │ ├── MyVarHidingProof.fst │ │ ├── MyVarIntroProof.fst │ │ ├── Spec.Behavior.fst │ │ ├── Spec.List.fst │ │ ├── Spec.Logic.fst │ │ ├── Spec.Map.fst │ │ ├── Spec.Ubool.fst │ │ ├── Strategies.ArmadaInvariant.PositionsValid.fst │ │ ├── Strategies.ArmadaInvariant.PositionsValid.fsti │ │ ├── Strategies.ArmadaInvariant.RootsMatch.fst │ │ ├── Strategies.ArmadaInvariant.UnstartedThreads.fst │ │ ├── Strategies.ArmadaInvariant.UnstartedThreads.fsti │ │ ├── Strategies.ArmadaStatement.Breaking.fst │ │ ├── Strategies.ArmadaStatement.Nonyielding.fst │ │ ├── Strategies.ArmadaStatement.Opaque.fst │ │ ├── Strategies.ArmadaStatement.Propagate.fst │ │ ├── Strategies.ArmadaStatement.Status.fst │ │ ├── Strategies.ArmadaStatement.Status.fsti │ │ ├── Strategies.ArmadaStatement.ThreadState.fst │ │ ├── Strategies.ArmadaStatement.fst │ │ ├── Strategies.Atomic.fst │ │ ├── Strategies.AtomicToRegular.Armada.fst │ │ ├── Strategies.AtomicToRegular.Armada.fsti │ │ ├── Strategies.AtomicToRegular.fst │ │ ├── Strategies.AtomicToRegular.fsti │ │ ├── Strategies.Breaking.fst │ │ ├── Strategies.Common.fst │ │ ├── Strategies.GlobalVars.Init.fst │ │ ├── Strategies.GlobalVars.Permanent.fst │ │ ├── Strategies.GlobalVars.Pointer.fst │ │ ├── Strategies.GlobalVars.Statement.fst │ │ ├── Strategies.GlobalVars.Statement.fsti │ │ ├── Strategies.GlobalVars.Types.fst │ │ ├── Strategies.GlobalVars.Unaddressed.fst │ │ ├── Strategies.GlobalVars.UnaddressedStatement.fst │ │ ├── Strategies.GlobalVars.Util.fst │ │ ├── Strategies.GlobalVars.Value.fst │ │ ├── Strategies.GlobalVars.VarHiding.fst │ │ ├── Strategies.GlobalVars.VarHiding.fsti │ │ ├── Strategies.GlobalVars.VarIntro.fst │ │ ├── Strategies.GlobalVars.VarIntro.fsti │ │ ├── Strategies.GlobalVars.fst │ │ ├── Strategies.GlobalVarsProof.fst │ │ ├── Strategies.Invariant.Armada.Atomic.fst │ │ ├── Strategies.Invariant.Armada.Atomic.fsti │ │ ├── Strategies.Invariant.Armada.AtomicSubstep.fst │ │ ├── Strategies.Invariant.Armada.AtomicSubstep.fsti │ │ ├── Strategies.Invariant.Armada.fst │ │ ├── Strategies.Invariant.Armada.fsti │ │ ├── Strategies.Invariant.Atomic.fst │ │ ├── Strategies.Invariant.Atomic.fsti │ │ ├── Strategies.Invariant.fst │ │ ├── Strategies.Invariant.fsti │ │ ├── Strategies.Lift.Generic.fst │ │ ├── Strategies.Lift.Generic.fsti │ │ ├── Strategies.Nonyielding.fst │ │ ├── Strategies.PCIndices.fst │ │ ├── Strategies.PCRelation.fst │ │ ├── Strategies.RegularToAtomic.Armada.Helper.fst │ │ ├── Strategies.RegularToAtomic.Armada.fst │ │ ├── Strategies.RegularToAtomic.Armada.fsti │ │ ├── Strategies.RegularToAtomic.fst │ │ ├── Strategies.RegularToAtomic.fsti │ │ ├── Strategies.Semantics.Armada.fst │ │ ├── Strategies.Semantics.Armada.fsti │ │ ├── Strategies.Semantics.fst │ │ ├── Strategies.VarHiding.Defs.fst │ │ ├── Strategies.VarHiding.Efficient.fst │ │ ├── Strategies.VarHiding.Efficient.fsti │ │ ├── Strategies.VarHiding.Helpers.fst │ │ ├── Strategies.VarHiding.Inefficient.fst │ │ ├── Strategies.VarHiding.Inefficient.fsti │ │ ├── Strategies.VarHiding.Initialization.fst │ │ ├── Strategies.VarHiding.Initialization.fsti │ │ ├── Strategies.VarHiding.Invariant.fst │ │ ├── Strategies.VarHiding.Propagate.fst │ │ ├── Strategies.VarHiding.Relation.fst │ │ ├── Strategies.VarHiding.Relation.fsti │ │ ├── Strategies.VarIntro.Defs.fst │ │ ├── Strategies.VarIntro.Efficient.fst │ │ ├── Strategies.VarIntro.Efficient.fsti │ │ ├── Strategies.VarIntro.Helpers.fst │ │ ├── Strategies.VarIntro.Inefficient.fst │ │ ├── Strategies.VarIntro.Inefficient.fsti │ │ ├── Strategies.VarIntro.Initialization.fst │ │ ├── Strategies.VarIntro.Initialization.fsti │ │ ├── Strategies.VarIntro.Invariant.fst │ │ ├── Strategies.VarIntro.Propagate.fst │ │ ├── Strategies.VarIntro.Relation.fst │ │ ├── Strategies.VarIntro.Relation.fsti │ │ ├── Strategies.Weakening.Armada.fst │ │ ├── Strategies.Weakening.Armada.fsti │ │ ├── Strategies.Weakening.fst │ │ ├── Strategies.Weakening.fsti │ │ ├── Util.Behavior.fst │ │ ├── Util.ImmutableArray.fst │ │ ├── Util.ImmutableArray.fsti │ │ ├── Util.List.fst │ │ ├── Util.Logic.fst │ │ ├── Util.Nth.fst │ │ ├── Util.Range.fst │ │ ├── Util.Range.fsti │ │ ├── Util.Relation.fst │ │ ├── Util.Seq.fst │ │ ├── Util.Seq.fsti │ │ ├── Util.Tactics.fst │ │ └── Util.Trigger.fst │ ├── output.c │ └── third_party/ │ └── Coco/ │ ├── LICENSE.txt │ ├── README.txt │ └── src/ │ ├── Parser.frame │ └── Scanner.frame ├── third_party/ │ └── Coco/ │ ├── LICENSE.txt │ ├── README.txt │ └── src/ │ ├── Coco.atg │ ├── Coco.build │ ├── Coco.cs │ ├── Coco.csproj │ ├── DFA.cs │ ├── Parser.cs │ ├── Parser.frame │ ├── ParserGen.cs │ ├── Scanner.cs │ ├── Scanner.frame │ ├── Tab.cs │ ├── build.bat │ └── coc.bat └── tools/ └── scripts/ ├── .gitignore ├── __init__.py ├── dafny-oneproc.py └── dafny_profiler.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ * text=auto #Explicitly set here just for sanity. *.cs text *.dfy text *.expect text *.bpl text *.atg text *.mdk text *.json text *.sty text *.md text *.tmp text #Needs to be crlf *.sln text eol=crlf *.csproj text eol=crlf *.vsixmanifest eol=crlf *.config eol=crlf *.bat eol=crlf *.transcript eol=crlf #Needs to be lr Binaries/dafny text eol=lf Binaries/dafny-server text eol=lf #Never convert these *.dll binary *.exe binary *.png binary *.jpg binary *.xcf binary *.snk binary ================================================ FILE: .gitignore ================================================ *.config *.manifest *.pkgdef *.suo *.userprefs *.vsix *.vsixmanifest *~ *.vdfy .idea/ Binaries/* Package/ Source/*/bin/ Source/*/obj/ Source/Armada/Parser.cs Source/Armada/Scanner.cs Source/Armada/Parser.cs.old Source/Armada/Scanner.cs.old Source/packages Source/DafnyExtension/DafnyPrelude.bpl Source/DafnyExtension/DafnyRuntime.cs Source/DafnyExtension/Z3-LICENSE.txt Source/DafnyExtension/z3.exe *.csproj.user Test/**/.sconsign.dblite Test/**/*.exe Test/**/*.dll Test/**/*.mdb Test/**/*.pdb Test/**/Output/ Test/server/model.bvd Test/dafny0/Extern.cs Test/dafny0/ExternCopyFromTrait.cs Test/git-issues/github-issue-305-*.cs Test/desktop/* Test/node_modules/ Test/package-lock.json Docs/OnlineTutorial/DocumentationTransducer.exe Docs/OnlineTutorial/DocumentationTransducer.pdb Docs/OnlineTutorial/DocumentationTransducer/obj Docs/OnlineTutorial/manuscripts/*.htm Docs/OnlineTutorial/manuscripts/*.*.dfy Test/comp/*.cs Test/comp/*.js Test/comp/*-go # Generated by Rider IDE *.sln.DotSettings.user *.sln.DotSettings # Generated by Visual Studio 2019 *.csproj.user Source/.vs Binaries/CodeContracts/ bin/ .sconsign.dblite *.tmp Misc/ ================================================ FILE: Armada/.gitignore ================================================ *.tmp *.vdfy bin/ cache/ .sconsign.dblite ================================================ FILE: Armada/ArmadaCommonDefinitions.dfy ================================================ include "util/collections/seqs.i.dfy" include "util/option.s.dfy" include "spec/refinement.s.dfy" module ArmadaCommonDefinitions { import opened util_collections_seqs_i import opened util_option_s import opened GeneralRefinementModule //////////////////////////////// // Primitive types //////////////////////////////// newtype uint8 = n: int | 0 <= n < 256 newtype uint16 = n: int | 0 <= n < 65536 newtype uint32 = n: int | 0 <= n < 4294967296 newtype uint64 = n: int | 0 <= n < 18446744073709551616 newtype int8 = n: int | -128 <= n < 128 newtype int16 = n: int | -32768 <= n < 32768 newtype int32 = n: int | -2147483648 <= n < 2147483648 newtype int64 = n: int | -9223372036854775808 <= n < 9223372036854775808 function Armada_CastTo_uint8(n: int): (n': uint8) ensures 0 <= n < 256 ==> n' as int == n { (n % 256) as uint8 } function Armada_CastTo_uint16(n: int): (n': uint16) ensures 0 <= n < 65536 ==> n' as int == n { (n % 65536) as uint16 } function Armada_CastTo_uint32(n: int): (n': uint32) ensures 0 <= n < 4294967296 ==> n' as int == n { (n % 4294967296) as uint32 } function Armada_CastTo_uint64(n: int): (n': uint64) ensures 0 <= n < 18446744073709551616 ==> n' as int == n { (n % 18446744073709551616) as uint64 } function {:axiom} Armada_CastTo_int8(n: int): (n': int8) ensures -128 <= n < 128 ==> n' as int == n function {:axiom} Armada_CastTo_int16(n: int): (n': int16) ensures -32768 <= n < 32768 ==> n' as int == n function {:axiom} Armada_CastTo_int32(n: int): (n': int32) ensures -2147483648 <= n < 2147483648 ==> n' as int == n function {:axiom} Armada_CastTo_int64(n: int): (n': int64) ensures -9223372036854775808 <= n < 9223372036854775808 ==> n' as int == n datatype Armada_PrimitiveValue = Armada_PrimitiveValueNone | Armada_PrimitiveValue_uint8(n_uint8: uint8) | Armada_PrimitiveValue_uint16(n_uint16: uint16) | Armada_PrimitiveValue_uint32(n_uint32: uint32) | Armada_PrimitiveValue_uint64(n_uint64: uint64) | Armada_PrimitiveValue_int8(n_int8: int8) | Armada_PrimitiveValue_int16(n_int16: int16) | Armada_PrimitiveValue_int32(n_int32: int32) | Armada_PrimitiveValue_int64(n_int64: int64) datatype Armada_PrimitiveType = Armada_PrimitiveType_uint8 | Armada_PrimitiveType_uint16 | Armada_PrimitiveType_uint32 | Armada_PrimitiveType_uint64 | Armada_PrimitiveType_int8 | Armada_PrimitiveType_int16 | Armada_PrimitiveType_int32 | Armada_PrimitiveType_int64 predicate Armada_PrimitiveValueMatchesType(v: Armada_PrimitiveValue, ty: Armada_PrimitiveType) { match ty case Armada_PrimitiveType_uint8 => v.Armada_PrimitiveValue_uint8? case Armada_PrimitiveType_uint16 => v.Armada_PrimitiveValue_uint16? case Armada_PrimitiveType_uint32 => v.Armada_PrimitiveValue_uint32? case Armada_PrimitiveType_uint64 => v.Armada_PrimitiveValue_uint64? case Armada_PrimitiveType_int8 => v.Armada_PrimitiveValue_int8? case Armada_PrimitiveType_int16 => v.Armada_PrimitiveValue_int16? case Armada_PrimitiveType_int32 => v.Armada_PrimitiveValue_int32? case Armada_PrimitiveType_int64 => v.Armada_PrimitiveValue_int64? } predicate Armada_PrimitiveValuesOfSameType(v: Armada_PrimitiveValue, v': Armada_PrimitiveValue) { match v case Armada_PrimitiveValueNone => v'.Armada_PrimitiveValueNone? case Armada_PrimitiveValue_uint8(_) => v'.Armada_PrimitiveValue_uint8? case Armada_PrimitiveValue_uint16(_) => v'.Armada_PrimitiveValue_uint16? case Armada_PrimitiveValue_uint32(_) => v'.Armada_PrimitiveValue_uint32? case Armada_PrimitiveValue_uint64(_) => v'.Armada_PrimitiveValue_uint64? case Armada_PrimitiveValue_int8(_) => v'.Armada_PrimitiveValue_int8? case Armada_PrimitiveValue_int16(_) => v'.Armada_PrimitiveValue_int16? case Armada_PrimitiveValue_int32(_) => v'.Armada_PrimitiveValue_int32? case Armada_PrimitiveValue_int64(_) => v'.Armada_PrimitiveValue_int64? } //////////////////////////////// // Bit vectors //////////////////////////////// function {:opaque} U32(b:bv32) : uint32 { b as uint32 } function {:opaque} B32(u:uint32) : bv32 { u as bv32 } function {:opaque} U64(b:bv64) : uint64 { b as uint64 } function {:opaque} B64(u:uint64) : bv64 { u as bv64 } // 32 bits function {:opaque} bit_and32(b0:bv32, b1:bv32) : bv32 { b0 & b1 } function {:opaque} bit_or32(b0:bv32, b1:bv32) : bv32 { b0 | b1 } function {:opaque} bit_mod32(b0:bv32, b1:bv32) : bv32 requires b1 != 0; { b0 % b1 } function {:opaque} bit_xor32(b0:bv32, b1:bv32) : bv32 { b0 ^ b1 } function {:opaque} bit_lshift32(b0:bv32, b1:bv32) : bv32 requires b1 <= 32; { b0 << b1 } function {:opaque} bit_rshift32(b0:bv32, b1:bv32) : bv32 requires b1 <= 32; { b0 >> b1 } function {:opaque} bit_and_uint32(u0:uint32, u1:uint32) : uint32 { U32(bit_and32(B32(u0), B32(u1))) } function {:opaque} bit_or_uint32(u0:uint32, u1:uint32) : uint32 { U32(bit_or32(B32(u0), B32(u1))) } function {:opaque} bit_mod_uint32(u0:uint32, u1:uint32) : uint32 requires u1 != 0; { reveal B32(); U32(bit_mod32(B32(u0), B32(u1))) } function {:opaque} bit_xor_uint32(u0:uint32, u1:uint32) : uint32 { U32(bit_xor32(B32(u0), B32(u1))) } function {:opaque} bit_lshift_uint32(u0:uint32, u1:uint32) : uint32 requires u1 <= 32; { bv32_inequality(u1); U32(bit_lshift32(B32(u0), B32(u1))) } function {:opaque} bit_rshift_uint32(u0:uint32, u1:uint32) : uint32 requires u1 <= 32; { bv32_inequality(u1); U32(bit_rshift32(B32(u0), B32(u1))) } // 64 bits function {:opaque} bit_and64(b0:bv64, b1:bv64) : bv64 { b0 & b1 } function {:opaque} bit_or64(b0:bv64, b1:bv64) : bv64 { b0 | b1 } function {:opaque} bit_mod64(b0:bv64, b1:bv64) : bv64 requires b1 != 0; { b0 % b1 } function {:opaque} bit_xor64(b0:bv64, b1:bv64) : bv64 { b0 ^ b1 } function {:opaque} bit_lshift64(b0:bv64, b1:bv64) : bv64 requires b1 <= 64; { b0 << b1 } function {:opaque} bit_rshift64(b0:bv64, b1:bv64) : bv64 requires b1 <= 64; { b0 >> b1 } function {:opaque} bit_and_uint64(u0:uint64, u1:uint64) : uint64 { U64(bit_and64(B64(u0), B64(u1))) } function {:opaque} bit_or_uint64(u0:uint64, u1:uint64) : uint64 { U64(bit_or64(B64(u0), B64(u1))) } function {:opaque} bit_mod_uint64(u0:uint64, u1:uint64) : uint64 requires u1 != 0; { reveal B64(); U64(bit_mod64(B64(u0), B64(u1))) } function {:opaque} bit_xor_uint64(u0:uint64, u1:uint64) : uint64 { U64(bit_xor64(B64(u0), B64(u1))) } function {:opaque} bit_lshift_uint64(u0:uint64, u1:uint64) : uint64 requires u1 <= 64; { bv64_inequality(u1); U64(bit_lshift64(B64(u0), B64(u1))) } function {:opaque} bit_rshift_uint64(u0:uint64, u1:uint64) : uint64 requires u1 <= 64; { bv64_inequality(u1); U64(bit_rshift64(B64(u0), B64(u1))) } lemma {:axiom} bv_core_properties() ensures forall u:uint32 :: U32(B32(u)) == u; ensures forall b:bv32 :: B32(U32(b)) == b; ensures forall x:uint32, m:uint32 :: m != 0 && B32(m) != 0 ==> (x % m) == U32(bit_mod32(B32(x), B32(m))); ensures forall u:uint64 :: U64(B64(u)) == u; ensures forall b:bv64 :: B64(U64(b)) == b; ensures forall x:uint64, m:uint64 :: m != 0 && B64(m) != 0 ==> (x % m) == U64(bit_mod64(B64(x), B64(m))); // 32 bits lemma B32_injective(u0:uint32, u1:uint32) ensures u0 == u1 <==> B32(u0) == B32(u1); { bv_core_properties(); assert u0 == u1 ==> B32(u0) == B32(u1); var b0 := B32(u0); var b1 := B32(u1); assert b0 == b1 ==> U32(b0) == U32(b1); } lemma U32_injective(b0:bv32, b1:bv32) ensures b0 == b1 <==> U32(b0) == U32(b1); { bv_core_properties(); assert b0 == b1 ==> U32(b0) == U32(b1); var u0 := U32(b0); var u1 := U32(b1); assert u0 == u1 ==> B32(u0) == B32(u1); } lemma bv32_injectivity() ensures forall u0:uint32, u1:uint32 :: u0 == u1 <==> B32(u0) == B32(u1) ensures forall b0, b1 :: b0 == b1 <==> U32(b0) == U32(b1) { reveal B32(); // Without this, Dafny can't seem to translate the forall statement to the forall expression reveal U32(); // Without this, Dafny can't seem to translate the forall statement to the forall expression forall u0:uint32, u1:uint32 ensures u0 == u1 <==> B32(u0) == B32(u1) { B32_injective(u0, u1); } forall b0, b1 ensures b0 == b1 <==> U32(b0) == U32(b1) { U32_injective(b0, b1); } } lemma bv32_inequality(u:uint32) requires u <= 32; ensures B32(u) <= 32; { reveal B32(); reveal U32(); bv_core_properties(); bv32_injectivity(); } // 64 bits lemma B64_injective(u0:uint64, u1:uint64) ensures u0 == u1 <==> B64(u0) == B64(u1); { bv_core_properties(); assert u0 == u1 ==> B64(u0) == B64(u1); var b0 := B64(u0); var b1 := B64(u1); assert b0 == b1 ==> U64(b0) == U64(b1); } lemma U64_injective(b0:bv64, b1:bv64) ensures b0 == b1 <==> U64(b0) == U64(b1); { bv_core_properties(); assert b0 == b1 ==> U64(b0) == U64(b1); var u0 := U64(b0); var u1 := U64(b1); assert u0 == u1 ==> B64(u0) == B64(u1); } lemma bv64_injectivity() ensures forall u0:uint64, u1:uint64 :: u0 == u1 <==> B64(u0) == B64(u1) ensures forall b0, b1 :: b0 == b1 <==> U64(b0) == U64(b1) { reveal B64(); // Without this, Dafny can't seem to translate the forall statement to the forall expression reveal U64(); // Without this, Dafny can't seem to translate the forall statement to the forall expression forall u0:uint64, u1:uint64 ensures u0 == u1 <==> B64(u0) == B64(u1) { B64_injective(u0, u1); } forall b0, b1 ensures b0 == b1 <==> U64(b0) == U64(b1) { U64_injective(b0, b1); } } lemma bv64_inequality(u:uint64) requires u <= 64; ensures B64(u) <= 64; { reveal B64(); reveal U64(); bv_core_properties(); bv64_injectivity(); } // Example uses lemma bv_test(b:bv64) //ensures bit_and64(b, 0) == 0; //ensures bit_and64(b, 0xFFFFFFFFFFFFFFFF) == b ensures bit_xor64(b, 0) == b; { //reveal bit_and64(); var all_ones:bv64 := 0xFFFFFFFFFFFFFFFF; //assert bit_and64(b, all_ones) == b; reveal_bit_xor64(); } lemma bv64_properties() ensures forall u0:uint64 :: bit_and_uint64(u0, 0) == 0 { reveal bit_and_uint64(); // Without this, Dafny can't seem to translate the forall statement to the forall expression forall u0 ensures bit_and_uint64(u0, 0) == 0 { bv64_properties_specific(u0, 0); } } lemma bv64_properties_specific(u0:uint64, u1:uint64) ensures bit_and_uint64(u0, 0) == 0 ensures bit_and_uint64(u0, u1) == bit_and_uint64(u1, u0) ensures bit_xor_uint64(u0, u0) == 0 ensures bit_xor_uint64(u0, u1) == bit_xor_uint64(u1, u0) { bv_core_properties(); reveal U64(); reveal B64(); var all_ones:uint64 := 0xFFFFFFFFFFFFFFFF; assert B64(0) == 0; // Help Z3 with constant conversion assert B64(all_ones) == 0xFFFFFFFFFFFFFFFF; // Help Z3 with constant conversion // AND assert bit_and_uint64(u0, 0) == 0 by { reveal bit_and_uint64(); reveal bit_and64(); } assert bit_and_uint64(u0, all_ones) == u0 by { reveal bit_and_uint64(); reveal bit_and64(); } assert bit_and_uint64(u0, u1) == bit_and_uint64(u1, u0) by { reveal bit_and_uint64(); reveal bit_and64(); } // OR assert bit_or_uint64(u0, 0) == u0 by { reveal bit_or_uint64(); reveal bit_or64(); } assert bit_or_uint64(u0, all_ones) == all_ones by { reveal bit_or_uint64(); reveal bit_or64(); } assert bit_or_uint64(u0, u1) == bit_or_uint64(u1, u0) by { reveal bit_or_uint64(); reveal bit_or64(); } // XOR assert bit_xor_uint64(u0, u0) == 0 by { reveal bit_xor_uint64(); reveal bit_xor64(); } assert bit_xor_uint64(u0, u1) == bit_xor_uint64(u1, u0) by { reveal bit_xor_uint64(); reveal bit_xor64(); } assert bit_xor_uint64(u0, 0) == u0 by { reveal bit_xor_uint64(); reveal bit_xor64(); } } // method bitwise_and(x:uint64, y:uint64) returns (z:uint64) // ensures z == bit_and_uint64(x, y) //////////////////////////////// // Thread handle //////////////////////////////// type Armada_ThreadHandle = uint64 //////////////////////////////// // Termination //////////////////////////////// datatype Armada_StopReason = Armada_NotStopped | Armada_StopReasonTerminated | Armada_StopReasonAssertionFailure | Armada_StopReasonUndefinedBehavior //////////////////////////////// // Multisteps //////////////////////////////// datatype Armada_SpecFunctions = Armada_SpecFunctions( init:State->bool, step_valid:(State, OneStep, Armada_ThreadHandle)->bool, step_next:(State, OneStep, Armada_ThreadHandle)->State, is_step_tau:OneStep->bool, state_ok:State->bool, get_thread_pc:(State, Armada_ThreadHandle)->Option, is_pc_nonyielding:PC->bool ) predicate Armada_ThreadYielding( asf:Armada_SpecFunctions, s:State, tid:Armada_ThreadHandle ) { // Either the process has crashed, the thread isn't running, or the thread is at a yield point var pc := asf.get_thread_pc(s, tid); !asf.state_ok(s) || pc.None? || !asf.is_pc_nonyielding(pc.v) } predicate Armada_StepsStartNonyielding( asf:Armada_SpecFunctions, s:State, s':State, steps:seq, tid:Armada_ThreadHandle ) { |steps| > 0 ==> && !Armada_ThreadYielding(asf, s, tid) && !asf.is_step_tau(steps[0]) && Armada_StepsStartNonyielding(asf, asf.step_next(s, steps[0], tid), s', steps[1..], tid) } predicate Armada_NextMultipleSteps( asf:Armada_SpecFunctions, s:State, s':State, steps:seq, tid:Armada_ThreadHandle ) { if |steps| == 0 then s' == s else && asf.step_valid(s, steps[0], tid) && Armada_NextMultipleSteps(asf, asf.step_next(s, steps[0], tid), s', steps[1..], tid) } predicate Armada_NextMultistep( asf:Armada_SpecFunctions, s:State, s':State, steps:seq, tid:Armada_ThreadHandle, tau:bool ) { && Armada_NextMultipleSteps(asf, s, s', steps, tid) && (|steps| > 0 ==> if tau then && |steps| == 1 && asf.is_step_tau(steps[0]) else && Armada_ThreadYielding(asf, s, tid) && Armada_ThreadYielding(asf, s', tid) && !asf.is_step_tau(steps[0]) && Armada_StepsStartNonyielding(asf, asf.step_next(s, steps[0], tid), s', steps[1..], tid) ) } function Armada_SpecFunctionsToSpec( asf:Armada_SpecFunctions ) : Spec { Spec(iset s | asf.init(s), iset s, s', steps, tid, tau | Armada_NextMultistep(asf, s, s', steps, tid, tau) :: StatePair(s, s')) } //////////////////////////////// // Heap types //////////////////////////////// datatype Armada_ObjectType = Armada_ObjectTypePrimitive(pty: Armada_PrimitiveType) | Armada_ObjectTypeStruct(fieldTypes: seq) | Armada_ObjectTypeArray(subtype: Armada_ObjectType, sz: int) //////////////////////////////// // Pointers and heaps //////////////////////////////// type Armada_Pointer = uint64 function Armada_TriggerPointer(p:Armada_Pointer) : Armada_Pointer { p } datatype Armada_RootType = Armada_RootTypeStaticHeap | Armada_RootTypeDynamicHeap | Armada_RootTypeStack datatype Armada_ChildType = Armada_ChildTypeRoot(rt: Armada_RootType) | Armada_ChildTypeIndex(i: int) datatype Armada_Node = Armada_Node(parent: Armada_Pointer, child_type: Armada_ChildType, children: seq, ty: Armada_ObjectType) type Armada_HeapValues = map datatype Armada_Heap = Armada_Heap(tree: map, valid: set, freed: set, values: Armada_HeapValues) predicate Armada_TreeForestProperties(tree: map) { && (forall p {:trigger Armada_TriggerPointer(p)} :: Armada_TriggerPointer(p) in tree ==> tree[p].parent in tree) && (forall p {:trigger Armada_TriggerPointer(p)} :: Armada_TriggerPointer(p) in tree ==> (tree[p].child_type.Armada_ChildTypeRoot? <==> tree[p].parent == p)) && (forall p, i {:trigger Armada_TriggerPointer(p), tree[p].children[i]} :: Armada_TriggerPointer(p) in tree && 0 <= i < |tree[p].children| ==> var q := tree[p].children[i]; q in tree && tree[q].parent == p && tree[q].child_type == Armada_ChildTypeIndex(i)) && (forall q {:trigger Armada_TriggerPointer(q)} :: Armada_TriggerPointer(q) in tree && !tree[q].child_type.Armada_ChildTypeRoot? ==> var p, f := tree[q].parent, tree[q].child_type; p in tree && 0 <= f.i < |tree[p].children| && tree[p].children[f.i] == q) } predicate Armada_TreeStructProperties(tree: map) { && (forall p {:trigger Armada_TriggerPointer(p)} :: Armada_TriggerPointer(p) in tree && tree[p].ty.Armada_ObjectTypeStruct? ==> |tree[p].children| == |tree[p].ty.fieldTypes|) && (forall p, i {:trigger Armada_TriggerPointer(p), tree[p].children[i]} :: Armada_TriggerPointer(p) in tree && tree[p].ty.Armada_ObjectTypeStruct? && 0 <= i < |tree[p].children| ==> var q := tree[p].children[i]; q in tree && tree[q].ty == tree[p].ty.fieldTypes[i]) } predicate Armada_TreeArrayProperties(tree: map) { && (forall p {:trigger Armada_TriggerPointer(p)} :: Armada_TriggerPointer(p) in tree && tree[p].ty.Armada_ObjectTypeArray? ==> tree[p].ty.sz >= 0) && (forall p {:trigger Armada_TriggerPointer(p)} :: Armada_TriggerPointer(p) in tree && tree[p].ty.Armada_ObjectTypeArray? ==> |tree[p].children| == tree[p].ty.sz) && (forall p, q {:trigger Armada_TriggerPointer(p), Armada_TriggerPointer(q), tree[p].ty.Armada_ObjectTypeArray?} :: && Armada_TriggerPointer(p) in tree && Armada_TriggerPointer(q) in tree && tree[p].ty.Armada_ObjectTypeArray? && tree[q].parent == p && q != p ==> tree[q].ty == tree[p].ty.subtype) } predicate Armada_TreePrimitiveProperties(tree: map) { forall p {:trigger Armada_TriggerPointer(p)} :: Armada_TriggerPointer(p) in tree && tree[p].ty.Armada_ObjectTypePrimitive? ==> |tree[p].children| == 0 } predicate Armada_TreeProperties(tree: map) { && Armada_TreeForestProperties(tree) && Armada_TreeStructProperties(tree) && Armada_TreeArrayProperties(tree) && Armada_TreePrimitiveProperties(tree) } predicate Armada_HeapInvariant(h: Armada_Heap) { && (forall p :: p in h.valid ==> p !in h.freed) && 0 in h.freed && !(0 in h.valid) && Armada_TreeProperties(h.tree) && (forall p {:trigger Armada_TriggerPointer(p)} {:trigger Armada_TriggerPointer(h.tree[p].parent)} :: Armada_TriggerPointer(p) in h.tree ==> (p in h.valid <==> Armada_TriggerPointer(h.tree[p].parent) in h.valid)) && forall p {:trigger Armada_TriggerPointer(p)} :: Armada_TriggerPointer(p) in h.tree && h.tree[p].ty.Armada_ObjectTypePrimitive? ==> p in h.values && Armada_PrimitiveValueMatchesType(h.values[p], h.tree[p].ty.pty) } predicate Armada_HeapMetadataUnchanged(h: Armada_Heap, h': Armada_Heap) { && h'.tree == h.tree && h'.valid == h.valid && h'.freed == h.freed } predicate Armada_ComparablePointer(p: Armada_Pointer, h: Armada_Heap) { p !in h.freed || p == 0 } ///////////////////////////////////////////// // Pointer subtree structural correctness ///////////////////////////////////////////// predicate Armada_PointerSubtreeHasObjectType(tree: map, p: Armada_Pointer, ty: Armada_ObjectType) decreases ty { && p in tree && tree[p].ty == ty && match tree[p].ty case Armada_ObjectTypePrimitive(pty) => |tree[p].children| == 0 case Armada_ObjectTypeStruct(fieldTypes) => && |tree[p].children| == |fieldTypes| && (forall i {:trigger tree[p].children[i]} :: 0 <= i < |fieldTypes| ==> Armada_PointerSubtreeHasObjectType(tree, tree[p].children[i], fieldTypes[i])) case Armada_ObjectTypeArray(subtype, sz) => && |tree[p].children| == sz && (forall i {:trigger tree[p].children[i]} :: 0 <= i < sz ==> Armada_PointerSubtreeHasObjectType(tree, tree[p].children[i], subtype)) } predicate Armada_PointerSubtreeCorrect(tree: map, p: Armada_Pointer) { p in tree && Armada_PointerSubtreeHasObjectType(tree, p, tree[p].ty) } //////////////////////////////////// // Descendants in heap tree //////////////////////////////////// function Armada_DescendantsOfPointerToObjectType( tree: map, p: Armada_Pointer, ty: Armada_ObjectType ) : set decreases ty { match ty case Armada_ObjectTypePrimitive(_) => {Armada_TriggerPointer(p)} case Armada_ObjectTypeStruct(fieldTypes) => {Armada_TriggerPointer(p)} + (set q, i, ty {:trigger q in Armada_DescendantsOfPointerToObjectType(tree, tree[p].children[i], ty)} | && p in tree && 0 <= i < |tree[p].children| && i < |fieldTypes| && ty == fieldTypes[i] && q in Armada_DescendantsOfPointerToObjectType(tree, tree[p].children[i], ty) :: Armada_TriggerPointer(q)) case Armada_ObjectTypeArray(subtype, _) => {Armada_TriggerPointer(p)} + (set q, i {:trigger q in Armada_DescendantsOfPointerToObjectType(tree, tree[p].children[i], subtype)} | && p in tree && 0 <= i < |tree[p].children| && q in Armada_DescendantsOfPointerToObjectType(tree, tree[p].children[i], subtype) :: Armada_TriggerPointer(q)) } function Armada_DescendantsOfPointer(tree: map, p: Armada_Pointer) : set requires p in tree { Armada_DescendantsOfPointerToObjectType(tree, p, tree[p].ty) } //////////////////////////////////// // Pointer validity //////////////////////////////////// function Armada_ValidPointerToObjectType(h: Armada_Heap, p: Armada_Pointer, ty: Armada_ObjectType) : (b: bool) ensures b ==> Armada_PointerSubtreeHasObjectType(h.tree, p, ty) decreases ty { && Armada_TriggerPointer(p) in h.tree && p in h.valid && h.tree[p].ty == ty && match h.tree[p].ty case Armada_ObjectTypePrimitive(pty) => && |h.tree[p].children| == 0 && p in h.values && Armada_PrimitiveValueMatchesType(h.values[p], pty) case Armada_ObjectTypeStruct(fieldTypes) => && |h.tree[p].children| == |fieldTypes| && (forall i {:trigger h.tree[p].children[i]} :: 0 <= i < |fieldTypes| ==> Armada_ValidPointerToObjectType(h, h.tree[p].children[i], fieldTypes[i])) case Armada_ObjectTypeArray(subtype, sz) => && |h.tree[p].children| == sz && (forall i {:trigger h.tree[p].children[i]} :: 0 <= i < sz ==> Armada_ValidPointerToObjectType(h, h.tree[p].children[i], subtype)) } function Armada_ValidPointer(h: Armada_Heap, p: Armada_Pointer) : (b: bool) ensures b ==> Armada_PointerSubtreeCorrect(h.tree, p) { p in h.tree && Armada_ValidPointerToObjectType(h, p, h.tree[p].ty) } //////////////////////////////////// // Updating heap via pointers //////////////////////////////////// function Armada_UpdateHeapValuesWithPrimitiveValue( values: map, p: Armada_Pointer, new_value: Armada_PrimitiveValue ) : ( new_values: map ) { map q | q in values :: if q == p then new_value else values[q] } predicate Armada_ArbitrarilyResolveConflictingUpdatesTrigger(p: Armada_Pointer) { true } function {:opaque} Armada_ArbitrarilyResolveConflictingUpdates(update_set: set<(Armada_Pointer, Armada_PrimitiveValue)>) : (update_map : map) ensures forall p :: p in update_map ==> (p, update_map[p]) in update_set ensures forall p, v :: (p, v) in update_set ==> p in update_map { map p {:trigger Armada_ArbitrarilyResolveConflictingUpdatesTrigger(p)} | Armada_ArbitrarilyResolveConflictingUpdatesTrigger(p) && (exists v :: (p, v) in update_set) :: (var v :| (p, v) in update_set; v) } function Armada_UpdateHeapValuesSimultaneously( values: Armada_HeapValues, updates: set<(Armada_Pointer, Armada_PrimitiveValue)> ) : ( values': Armada_HeapValues ) { var update_map := Armada_ArbitrarilyResolveConflictingUpdates(updates); map p | p in values :: if p in update_map then update_map[p] else values[p] } //////////////////////////////// // Configuration //////////////////////////////// datatype Armada_Config = Armada_Config(tid_init: Armada_ThreadHandle, new_ptrs: set) //////////////////////////////// // Placeholder types //////////////////////////////// type ArmadaPlaceholder_ExtendedFrame datatype ArmadaPlaceholder_StoreBufferEntry = ArmadaPlaceholder_StoreBufferEntry(value: Armada_PrimitiveValue) datatype ArmadaPlaceholder_Thread = ArmadaPlaceholder_Thread(stack: seq, storeBuffer: seq) datatype ArmadaPlaceholder_TotalState = ArmadaPlaceholder_TotalState(stop_reason: Armada_StopReason, threads: map, joinable_tids: set) } ================================================ FILE: Armada/spec/refinement.s.dfy ================================================ include "../util/collections/seqs.s.dfy" module GeneralRefinementModule { import opened util_collections_seqs_s datatype StatePair = StatePair(s:State, s':State) datatype Spec = Spec(init:iset, next:iset>) predicate BehaviorSatisfiesSpec(b:seq, sm:Spec) { && |b| > 0 && b[0] in sm.init && (forall i {:trigger StatePair(b[i], b[i+1]) in sm.next} :: 0 <= i < |b|-1 ==> StatePair(b[i], b[i+1]) in sm.next) } datatype RefinementRange = RefinementRange(first:int, last:int) type RefinementMap = seq datatype RefinementPair = RefinementPair(low:L, high:H) type RefinementRelation = iset> predicate IsValidRefinementMap(low_level_behavior_size:int, high_level_behavior_size:int, lh_map:RefinementMap) { && |lh_map| == low_level_behavior_size && low_level_behavior_size > 0 && (forall pair :: pair in lh_map ==> 0 <= pair.first <= pair.last < high_level_behavior_size) && lh_map[0].first == 0 && last(lh_map).last == high_level_behavior_size - 1 && (forall i :: 0 <= i < |lh_map| - 1 ==> lh_map[i+1].first == lh_map[i].last || lh_map[i+1].first == lh_map[i].last + 1) } predicate BehaviorRefinesBehaviorUsingRefinementMap( lb:seq, hb:seq, relation:RefinementRelation, lh_map:RefinementMap ) { && IsValidRefinementMap(|lb|, |hb|, lh_map) && (forall i, j {:trigger RefinementPair(lb[i], hb[j]) in relation} :: 0 <= i < |lb| && lh_map[i].first <= j <= lh_map[i].last ==> RefinementPair(lb[i], hb[j]) in relation) } predicate BehaviorRefinesBehavior( lb:seq, hb:seq, relation:RefinementRelation ) { exists lh_map :: BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, relation, lh_map) } predicate BehaviorRefinesSpec( lb:seq, spec:Spec, relation:RefinementRelation ) { exists hb :: BehaviorRefinesBehavior(lb, hb, relation) && BehaviorSatisfiesSpec(hb, spec) } predicate SpecRefinesSpec( l_spec:Spec, h_spec:Spec, relation:RefinementRelation ) { forall lb :: BehaviorSatisfiesSpec(lb, l_spec) ==> BehaviorRefinesSpec(lb, h_spec, relation) } } ================================================ FILE: Armada/strategies/chl/AtomicConcurrentHoareLogic.i.dfy ================================================ include "../../util/option.s.dfy" include "../../util/collections/seqs.s.dfy" include "../../util/collections/seqs.i.dfy" include "../refinement/GeneralRefinementLemmas.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../generic/GenericArmadaLemmas.i.dfy" include "../generic/LiftAtomicToAtomic.i.dfy" include "ConcurrentHoareLogicSpec.i.dfy" include "AtomicConcurrentHoareLogicLemmas.i.dfy" module AtomicConcurrentHoareLogicModule { import opened util_option_s import opened util_collections_seqs_s import opened util_collections_seqs_i import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened ArmadaCommonDefinitions import opened GenericArmadaSpecModule import opened GenericArmadaLemmasModule import opened GenericArmadaAtomicModule import opened LiftAtomicToAtomicModule import opened ConcurrentHoareLogicSpecModule import opened AtomicConcurrentHoareLogicSpecModule import opened AtomicConcurrentHoareLogicLemmasModule lemma lemma_LiftAtomicToAtomicUsingCHLRequest( ar:AtomicConcurrentHoareLogicRequest ) requires IsValidAtomicConcurrentHoareLogicRequest(ar) ensures SpecRefinesSpec(AtomicSpec(ar.l), AtomicSpec(ar.h), ar.relation) { var inv := ExtractInv(ar); var relation := ExtractLiftingRelation(ar); lemma_EstablishInitRequirements(ar, inv, relation); lemma_EstablishAtomicPathsLiftable(ar, inv, relation); lemma_LiftAtomicToAtomicGivenAtomicPathsLiftable(ar.l, ar.h, inv, relation, ar.relation); } } ================================================ FILE: Armada/strategies/chl/AtomicConcurrentHoareLogicLemmas.i.dfy ================================================ include "../../util/option.s.dfy" include "../../util/collections/seqs.s.dfy" include "../../util/collections/seqs.i.dfy" include "../refinement/GeneralRefinementLemmas.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../generic/GenericArmadaLemmas.i.dfy" include "../generic/LiftAtomicToAtomic.i.dfy" include "ConcurrentHoareLogic.i.dfy" include "AtomicConcurrentHoareLogicSpec.i.dfy" module AtomicConcurrentHoareLogicLemmasModule { import opened util_option_s import opened util_collections_seqs_s import opened util_collections_seqs_i import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened ArmadaCommonDefinitions import opened GenericArmadaSpecModule import opened GenericArmadaLemmasModule import opened GenericArmadaAtomicModule import opened LiftAtomicToAtomicModule import opened ConcurrentHoareLogicSpecModule import opened ConcurrentHoareLogicLemmasModule import opened ConcurrentHoareLogicModule import opened AtomicConcurrentHoareLogicSpecModule function ExtractInv( ar:AtomicConcurrentHoareLogicRequest ) : LState->bool { s => s in AnnotatedReachables(ar.cr.spec) } function ExtractLiftingRelation( ar:AtomicConcurrentHoareLogicRequest ) : (LState, HState)->bool { (ls, hs) => hs == ar.lstate_to_hstate(ls) } lemma lemma_EstablishInitRequirements( ar:AtomicConcurrentHoareLogicRequest, inv:LState->bool, relation:(LState, HState)->bool ) requires IsValidAtomicConcurrentHoareLogicRequest(ar) requires inv == ExtractInv(ar) requires relation == ExtractLiftingRelation(ar) ensures AtomicInitImpliesInv(ar.l, inv) ensures forall ls :: ar.l.init(ls) ==> exists hs :: ar.h.init(hs) && relation(ls, hs) { forall ls | ar.l.init(ls) ensures inv(ls) ensures exists hs :: ar.h.init(hs) && relation(ls, hs) { lemma_InitStateInAnnotatedReachables(ls, ar.cr.spec); var hs := ar.lstate_to_hstate(ls); assert ar.h.init(hs) && relation(ls, hs); } } lemma lemma_EstablishAtomicPathsLiftable( ar:AtomicConcurrentHoareLogicRequest, inv:LState->bool, relation:(LState, HState)->bool ) requires IsValidAtomicConcurrentHoareLogicRequest(ar) requires inv == ExtractInv(ar) requires relation == ExtractLiftingRelation(ar) ensures forall ls, lpath, tid, hs :: inv(ls) && relation(ls, hs) && ar.l.path_valid(ls, lpath, tid) ==> exists hpath :: LiftAtomicPathSuccessful(ar.l, ar.h, inv, relation, ls, lpath, tid, hs, hpath) { forall ls, lpath, tid, hs | inv(ls) && relation(ls, hs) && ar.l.path_valid(ls, lpath, tid) ensures exists hpath :: LiftAtomicPathSuccessful(ar.l, ar.h, inv, relation, ls, lpath, tid, hs, hpath) { assert ls in AnnotatedReachables(ar.cr.spec); var lb :| AnnotatedBehaviorSatisfiesSpec(lb, ar.cr.spec) && last(lb.states) == ls; var ls' := ar.l.path_next(ls, lpath, tid); var lpath_and_tid := PathAndTid(lpath, tid); lemma_ExtendStateNextSeqRight(lb.states, lb.trace, ar.cr.spec.next, ls', lpath_and_tid); var lb' := AnnotatedBehavior(lb.states + [ls'], lb.trace + [lpath_and_tid]); var pos := |lb.trace|; assert AnnotatedBehaviorSatisfiesSpec(lb', ar.cr.spec); assert lb'.states[pos] == ls; assert lb'.states[pos + 1] == ls'; assert lb'.trace[pos] == lpath_and_tid; lemma_InvariantPredicateHoldsAtStep(lb', pos, ar.cr.spec, ar.cr.established_inv); var hpath := ar.lpath_to_hpath(lpath); assert CorrectCHLStepEffect(ar.cr, ls, ls', lpath_and_tid); assert ar.cr.state_ok(ls); lemma_CHLGlobalInvariantHoldsWhenActorPresent(ar.cr, lb', pos, tid); if !ar.l.path_type(lpath).AtomicPathType_Tau? { lemma_CHLLocalInvariantHoldsWhenActorAboutToStep(ar.cr, lb', pos, tid); } assert LPathImpliesHPathConditions(ar, ls, lpath, tid); assert LiftAtomicPathSuccessful(ar.l, ar.h, inv, relation, ls, lpath, tid, hs, hpath); } } } ================================================ FILE: Armada/strategies/chl/AtomicConcurrentHoareLogicSpec.i.dfy ================================================ include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../generic/GenericArmadaAtomic.i.dfy" include "ConcurrentHoareLogicSpec.i.dfy" module AtomicConcurrentHoareLogicSpecModule { import opened util_option_s import opened GeneralRefinementModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened ArmadaCommonDefinitions import opened GenericArmadaSpecModule import opened GenericArmadaAtomicModule import opened ConcurrentHoareLogicSpecModule datatype PathAndTid = PathAndTid(path: Path, tid: Armada_ThreadHandle) datatype AtomicConcurrentHoareLogicRequest = AtomicConcurrentHoareLogicRequest( cr:ConcurrentHoareLogicRequest, LPC, LProc>, l:AtomicSpecFunctions, h:AtomicSpecFunctions, relation:RefinementRelation, lstate_to_hstate:LState->HState, lpath_to_hpath:LPath->HPath, lpc_to_hpc:LPC->HPC ) predicate LInitImpliesHInit( ar:AtomicConcurrentHoareLogicRequest ) { forall ls :: ar.l.init(ls) ==> ar.h.init(ar.lstate_to_hstate(ls)) } predicate LStateToHStateMapsPCsCorrectly( ar:AtomicConcurrentHoareLogicRequest ) { forall ls, tid :: var hs := ar.lstate_to_hstate(ls); var lpc := ar.l.get_thread_pc(ls, tid); var hpc := ar.h.get_thread_pc(hs, tid); hpc == if lpc.Some? then Some(ar.lpc_to_hpc(lpc.v)) else None() } predicate LHPathPropertiesMatch( ar:AtomicConcurrentHoareLogicRequest ) { forall lpath :: var hpath := ar.lpath_to_hpath(lpath); ar.l.path_type(lpath) == ar.h.path_type(hpath) } predicate LPathImpliesHPathConditions( ar: AtomicConcurrentHoareLogicRequest, ls: LState, lpath: LPath, tid: Armada_ThreadHandle ) { && ar.l.path_valid(ls, lpath, tid) && ar.cr.established_inv(ls) && (!ar.l.path_type(lpath).AtomicPathType_Tau? ==> ar.cr.local_inv(ls, tid)) && ar.cr.global_inv(ls) } predicate LPathImpliesHPath( ar:AtomicConcurrentHoareLogicRequest ) { forall ls, lpath, tid {:trigger LPathImpliesHPathConditions(ar, ls, lpath, tid)} :: LPathImpliesHPathConditions(ar, ls, lpath, tid) ==> var ls' := ar.l.path_next(ls, lpath, tid); var hs := ar.lstate_to_hstate(ls); var hpath := ar.lpath_to_hpath(lpath); var hs' := ar.lstate_to_hstate(ls'); && ar.h.path_valid(hs, hpath, tid) && hs' == ar.h.path_next(hs, hpath, tid) } predicate StateConversionPreservesOK( ar:AtomicConcurrentHoareLogicRequest ) { forall ls :: ar.h.state_ok(ar.lstate_to_hstate(ls)) == ar.l.state_ok(ls) } predicate StateConversionSatisfiesRelation( ar:AtomicConcurrentHoareLogicRequest ) { forall ls :: RefinementPair(ls, ar.lstate_to_hstate(ls)) in ar.relation } predicate EmbeddedRequestCorresponds( ar:AtomicConcurrentHoareLogicRequest ) { && (forall s :: ar.l.init(s) <==> s in ar.cr.spec.init) && (forall s :: ar.l.state_ok(s) <==> ar.cr.state_ok(s)) && (forall s, s', path, tid :: ActionTuple(s, s', PathAndTid(path, tid)) in ar.cr.spec.next <==> ar.l.path_valid(s, path, tid) && s' == ar.l.path_next(s, path, tid)) && (forall path, tid :: ar.cr.step_to_actor(PathAndTid(path, tid)) == if ar.l.path_type(path).AtomicPathType_Tau? then None else Some(tid)) && (forall s, tid :: ar.cr.get_actor_pc_stack(s, tid).Some? <==> ar.l.get_thread_pc(s, tid).Some?) } predicate IsValidAtomicConcurrentHoareLogicRequest( ar:AtomicConcurrentHoareLogicRequest ) { && IsValidConcurrentHoareLogicRequest(ar.cr) && LPathImpliesHPath(ar) && EmbeddedRequestCorresponds(ar) && AtomicInitImpliesOK(ar.l) && AtomicPathRequiresOK(ar.l) && AtomicSteppingThreadHasPC(ar.l) && AtomicTauLeavesPCUnchanged(ar.l) && AtomicThreadCantAffectOtherThreadPCExceptViaFork(ar.l) && LInitImpliesHInit(ar) && LStateToHStateMapsPCsCorrectly(ar) && LHPathPropertiesMatch(ar) && StateConversionPreservesOK(ar) && StateConversionSatisfiesRelation(ar) } } ================================================ FILE: Armada/strategies/chl/ConcurrentHoareLogic.i.dfy ================================================ include "ConcurrentHoareLogicLemmas.i.dfy" module ConcurrentHoareLogicModule { import opened AnnotatedBehaviorModule import opened InvariantsModule import opened util_collections_seqs_s import opened util_option_s import opened ConcurrentHoareLogicSpecModule import opened ConcurrentHoareLogicLemmasModule lemma lemma_CHLGlobalInvariantHoldsWhenActorPresent( cr: ConcurrentHoareLogicRequest, b: AnnotatedBehavior, pos: int, actor: Actor ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 <= pos < |b.states| requires cr.get_actor_pc_stack(b.states[pos], actor).Some? requires cr.state_ok(b.states[pos]) ensures cr.global_inv(b.states[pos]) { var pc := cr.get_actor_pc_stack(b.states[pos], actor).v.pc; var proc := cr.pc_to_proc(pc); var overlay := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos, actor, proc); lemma_StraightlineBehaviorSatisfiesGlobalInvariant(cr, overlay.sb, actor, proc, |overlay.sb.states| - 1); } lemma lemma_CHLLocalInvariantHoldsWhenActorAboutToStep( cr: ConcurrentHoareLogicRequest, b: AnnotatedBehavior, pos: int, actor: Actor ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 <= pos < |b.trace| requires cr.step_to_actor(b.trace[pos]).Some? ensures cr.local_inv(b.states[pos], cr.step_to_actor(b.trace[pos]).v) { var s := b.states[pos]; var s' := b.states[pos + 1]; var step := b.trace[pos]; assert ActionTuple(s, s', step) in cr.spec.next; var actor := cr.step_to_actor(step).v; var effect := cr.step_to_effect(step); var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; var proc := cr.pc_to_proc(pc); var overlay := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos, actor, proc); assert AnnotatedBehaviorSatisfiesSpec(overlay.sb, GetStraightlineSpec(cr, actor, proc)); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, overlay.sb, step, s'); } } ================================================ FILE: Armada/strategies/chl/ConcurrentHoareLogicLemmas.i.dfy ================================================ include "ConcurrentHoareLogicSpec.i.dfy" module ConcurrentHoareLogicLemmasModule { import opened util_collections_maps_s import opened util_collections_seqs_s import opened util_option_s import opened AnnotatedBehaviorModule import opened InvariantsModule import opened ConcurrentHoareLogicSpecModule /////////////////////////////////////////// // Sequence monotonicity /////////////////////////////////////////// predicate {:opaque} SequenceMonotonic(s:seq) { forall i, j :: 0 <= i <= j < |s| ==> s[i] <= s[j] } lemma lemma_TakeSequenceMonotonic(s:seq, n:int) requires SequenceMonotonic(s) requires 0 <= n < |s| ensures SequenceMonotonic(s[..n]) { reveal SequenceMonotonic(); } lemma lemma_ExtendSequenceMonotonic(s:seq, n:int) requires SequenceMonotonic(s) requires |s| > 0 ==> n >= last(s) ensures SequenceMonotonic(s + [n]) { reveal SequenceMonotonic(); } lemma lemma_UseSequenceMonotonic(s:seq, i:int, j:int) requires SequenceMonotonic(s) requires 0 <= i <= j < |s| ensures s[i] <= s[j] { reveal SequenceMonotonic(); } lemma lemma_EstablishSequenceMonotonic(s:seq) requires forall i, j :: 0 <= i <= j < |s| ==> s[i] <= s[j] ensures SequenceMonotonic(s) { reveal SequenceMonotonic(); } lemma lemma_ReplaceLastSequenceMonotonic(s:seq, n:int) requires SequenceMonotonic(s) requires |s| > 0 requires n >= last(s) ensures SequenceMonotonic(all_but_last(s) + [n]) { reveal SequenceMonotonic(); } /////////////////////////////////////////// // Overlay validity /////////////////////////////////////////// datatype HowStarted = HowStartedInit | HowStartedCall | HowStartedFork datatype StraightlineOverlay = StraightlineOverlay( sb:AnnotatedBehavior, StraightlineStep>, how_started:HowStarted, positions:seq ) predicate IsCallStep( cr:ConcurrentHoareLogicRequest, actor:Actor, step:Step ) { var step_actor := cr.step_to_actor(step); var effect := cr.step_to_effect(step); && step_actor.Some? && step_actor.v == actor && (effect.CHLStepEffectCall? || effect.CHLStepEffectReturnThenCall?) } predicate IsForkStep( cr:ConcurrentHoareLogicRequest, actor:Actor, s:State ) { cr.get_actor_pc_stack(s, actor).None? } predicate StraightlineOverlayValid( cr:ConcurrentHoareLogicRequest, overlay:StraightlineOverlay, b:AnnotatedBehavior, actor:Actor, proc:Proc ) { && AnnotatedBehaviorSatisfiesSpec(overlay.sb, GetStraightlineSpec(cr, actor, proc)) && |overlay.positions| == |overlay.sb.states| && |overlay.positions| > 0 && SequenceMonotonic(overlay.positions) && (forall i :: 0 <= i < |overlay.positions| ==> && 0 <= overlay.positions[i] < |b.states| && b.states[overlay.positions[i]] == overlay.sb.states[i].state) && (var pos := overlay.positions[0]; match overlay.how_started case HowStartedInit => pos == 0 case HowStartedCall => 0 < pos <= |b.trace| && IsCallStep(cr, actor, b.trace[pos-1]) case HowStartedFork => 0 < pos <= |b.trace| && IsForkStep(cr, actor, b.states[pos-1])) } predicate StraightlineOverlayLeadsToPos( cr:ConcurrentHoareLogicRequest, overlay:StraightlineOverlay, b:AnnotatedBehavior, actor:Actor, proc:Proc, pos:int ) { && StraightlineOverlayValid(cr, overlay, b, actor, proc) && last(overlay.positions) == pos } /////////////////////////////////////////// // Lemmas about straightline behaviors /////////////////////////////////////////// predicate ActorInProc( cr:ConcurrentHoareLogicRequest, s:State, actor:Actor, proc:Proc ) { && cr.get_actor_pc_stack(s, actor).Some? && var pc := cr.get_actor_pc_stack(s, actor).v.pc; cr.pc_to_proc(pc) == proc } lemma lemma_StraightlineBehaviorSatisfiesGlobalInvariant( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, actor:Actor, proc:Proc, pos:int ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) requires 0 <= pos < |sb.states| ensures cr.global_inv(sb.states[pos].state) ensures cr.state_ok(sb.states[pos].state) { if pos == 0 { return; } var sspec := GetStraightlineSpec(cr, actor, proc); var prev := pos-1; var ss := sb.states[prev]; var ss' := sb.states[prev+1]; var sstep := sb.trace[prev]; assert ActionTuple(ss, ss', sstep) in sspec.next; assert StraightlineSpecNext(cr, ss, ss', sstep, actor, proc); assert StraightlineSpecNextCommon(cr, ss, ss', sstep, actor); assert cr.global_inv(ss'.state); } lemma lemma_StraightlineBehaviorNeverChangesStack( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, actor:Actor, proc:Proc, pos:int ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) requires 0 <= pos < |sb.states| ensures var ss0 := sb.states[0]; var ss' := sb.states[pos]; var s0 := ss0.state; var StraightlineState(s', aux') := ss'; && cr.get_actor_pc_stack(s0, actor).Some? && cr.get_actor_pc_stack(s', actor).Some? && var PCStack(pc0, stack0) := cr.get_actor_pc_stack(s0, actor).v; var PCStack(pc', stack') := cr.get_actor_pc_stack(s', actor).v; if aux'.phase.StraightlinePhaseCalled? || aux'.phase.StraightlinePhaseEnsured? then && |stack'| > 0 && stack'[0] == proc && stack'[1..] == stack0 else && cr.pc_to_proc(pc') == proc && stack' == stack0 decreases pos { if pos == 0 { return; } var prev := pos-1; var ss := sb.states[prev]; var ss' := sb.states[prev+1]; var sstep := sb.trace[prev]; var sspec := GetStraightlineSpec(cr, actor, proc); assert ActionTuple(ss, ss', sstep) in sspec.next; lemma_StraightlineBehaviorNeverChangesStack(cr, sb, actor, proc, pos-1); assert StraightlineSpecNext(cr, ss, ss', sstep, actor, proc); } lemma lemma_VisitedLoopsOnlyContainsLoopHeads( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, actor:Actor, proc:Proc, pc:PC, pos:int ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) requires 0 <= pos < |sb.states| ensures pc in sb.states[pos].aux.visited_loops ==> cr.is_loop_head(pc) { if pos == 0 { return; } var prev := pos-1; var ss := sb.states[prev]; var ss' := sb.states[prev+1]; var step := sb.trace[prev]; var sspec := GetStraightlineSpec(cr, actor, proc); assert ActionTuple(ss, ss', step) in sspec.next; lemma_VisitedLoopsOnlyContainsLoopHeads(cr, sb, actor, proc, pc, prev); } lemma lemma_PCVisitedIfInLoopedPhase( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, actor:Actor, proc:Proc, pos:int ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) requires 0 <= pos < |sb.states| ensures cr.get_actor_pc_stack(sb.states[pos].state, actor).Some? ensures var StraightlineState(s, aux) := sb.states[pos]; var pc := cr.get_actor_pc_stack(s, actor).v.pc; var phase := aux.phase; && (cr.is_loop_head(pc) && phase.StraightlinePhaseLooped? ==> pc in aux.visited_loops) && (pc in aux.visited_loops ==> cr.is_loop_head(pc)) { if pos == 0 { return; } var prev := pos-1; lemma_PCVisitedIfInLoopedPhase(cr, sb, actor, proc, prev); var sspec := GetStraightlineSpec(cr, actor, proc); assert ActionTuple(sb.states[prev], sb.states[prev+1], sb.trace[prev]) in sspec.next; var pc := cr.get_actor_pc_stack(sb.states[pos].state, actor).v.pc; lemma_VisitedLoopsOnlyContainsLoopHeads(cr, sb, actor, proc, pc, pos); } /////////////////////////////////////////// // Lemmas about overlays /////////////////////////////////////////// lemma lemma_FindWhenLoopBegan( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, actor:Actor, proc:Proc, overlay:StraightlineOverlay, pc:PC, pos:int ) returns ( earlier_pos:int ) requires IsValidConcurrentHoareLogicRequest(cr) requires StraightlineOverlayValid(cr, overlay, b, actor, proc) requires 0 <= pos < |overlay.sb.states| requires pc in overlay.sb.states[pos].aux.visited_loops requires cr.get_actor_pc_stack(overlay.sb.states[pos].state, actor).Some? ensures 0 <= earlier_pos < pos ensures cr.is_loop_head(pc) ensures cr.get_actor_pc_stack(overlay.sb.states[earlier_pos].state, actor).Some? ensures cr.get_actor_pc_stack(overlay.sb.states[earlier_pos].state, actor).v.pc == pc ensures overlay.sb.states[earlier_pos].aux.phase.StraightlinePhaseYielded? ensures pc !in overlay.sb.states[earlier_pos].aux.visited_loops ensures pc in overlay.sb.states[earlier_pos+1].aux.visited_loops ensures overlay.sb.states[earlier_pos+1].aux.visited_loops[pc] == overlay.sb.states[pos].aux.visited_loops[pc] == overlay.sb.states[earlier_pos].state ensures overlay.sb.trace[earlier_pos] == StraightlineStepLoopModifies(pc) { if pos == 0 { assert false; return; } lemma_VisitedLoopsOnlyContainsLoopHeads(cr, overlay.sb, actor, proc, pc, pos); var sspec := GetStraightlineSpec(cr, actor, proc); var prev := pos-1; assert ActionTuple(overlay.sb.states[prev], overlay.sb.states[prev+1], overlay.sb.trace[prev]) in sspec.next; lemma_PCVisitedIfInLoopedPhase(cr, overlay.sb, actor, proc, prev); if pc !in overlay.sb.states[prev].aux.visited_loops { earlier_pos := prev; return; } earlier_pos := lemma_FindWhenLoopBegan(cr, b, actor, proc, overlay, pc, pos-1); } lemma lemma_ExtendYieldingOverlayOneStep( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, actor:Actor, proc:Proc, pos:int, overlay_prev:StraightlineOverlay ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires StraightlineOverlayLeadsToPos(cr, overlay_prev, b, actor, proc, pos-1) requires last(overlay_prev.sb.states).aux.phase.StraightlinePhaseYielded? requires 0 < pos < |b.states| requires cr.state_ok(b.states[pos]) requires cr.yield_pred(b.states[pos-1], b.states[pos], actor) requires cr.global_inv(b.states[pos]) requires cr.get_actor_pc_stack(b.states[pos], actor) == cr.get_actor_pc_stack(b.states[pos-1], actor) ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? { var s' := b.states[pos]; var sspec := GetStraightlineSpec(cr, actor, proc); var opos := |overlay_prev.sb.trace| - 1; var ss := overlay_prev.sb.states[opos]; var ss' := overlay_prev.sb.states[opos+1]; var sstep := overlay_prev.sb.trace[opos]; assert ActionTuple(ss, ss', sstep) in sspec.next; assert StraightlineSpecNextYield(cr, ss, ss', sstep, actor, proc); var new_aux' := StraightlineUpdateAux(cr, ss, actor, sstep, s'); var new_ss' := StraightlineState(s', new_aux'); var sstates := all_but_last(overlay_prev.sb.states) + [new_ss']; lemma_InvariantPredicateHoldsAtStep(b, overlay_prev.positions[opos], cr.spec, cr.established_inv); lemma_StraightlineBehaviorSatisfiesGlobalInvariant(cr, overlay_prev.sb, actor, proc, opos); lemma_InvariantPredicateHoldsAtStep(b, pos, cr.spec, cr.established_inv); assert StraightlineSpecNextYield(cr, ss, new_ss', sstep, actor, proc); assert StraightlineSpecNext(cr, ss, new_ss', sstep, actor, proc); lemma_ReplaceStateOnlyStateNextSeqRight(overlay_prev.sb.states, overlay_prev.sb.trace, sspec.next, new_ss'); lemma_ReplaceLastSequenceMonotonic(overlay_prev.positions, pos); overlay := overlay_prev.(sb := overlay_prev.sb.(states := sstates), positions := all_but_last(overlay_prev.positions) + [pos]); } lemma lemma_ExtendOverlayWithYielding( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, actor:Actor, proc:Proc, pos:int, overlay_prev:StraightlineOverlay ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires StraightlineOverlayValid(cr, overlay_prev, b, actor, proc) requires last(overlay_prev.positions) == pos requires last(overlay_prev.sb.states).aux.phase.StraightlinePhaseNormal? ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? { var ss := last(overlay_prev.sb.states); var StraightlineState(s, aux) := ss; var phase := aux.phase; var overlay_pos := |overlay_prev.sb.trace|; lemma_StraightlineBehaviorNeverChangesStack(cr, overlay_prev.sb, actor, proc, overlay_pos); var pc := cr.get_actor_pc_stack(s, actor).v.pc; var sspec := GetStraightlineSpec(cr, actor, proc); var AnnotatedBehavior(sstates, strace) := overlay_prev.sb; var positions := overlay_prev.positions; lemma_StraightlineBehaviorSatisfiesGlobalInvariant(cr, overlay_prev.sb, actor, proc, overlay_pos); lemma_InvariantPredicateHoldsAtStep(b, last(overlay_prev.positions), cr.spec, cr.established_inv); var sstep := StraightlineStepYield(); var aux2 := StraightlineUpdateAux(cr, last(sstates), actor, sstep, s); var sstate' := StraightlineState(s, aux2); assert StraightlineSpecNextYield(cr, last(sstates), sstate', sstep, actor, proc); assert StraightlineSpecNext(cr, last(sstates), sstate', sstep, actor, proc); lemma_ExtendStateNextSeqRight(sstates, strace, sspec.next, sstate', sstep); sstates := sstates + [sstate']; strace := strace + [sstep]; lemma_ExtendSequenceMonotonic(positions, pos); positions := positions + [pos]; overlay := overlay_prev.(sb := AnnotatedBehavior(sstates, strace), positions := positions); } lemma lemma_ExtendOverlayWithLooping( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, actor:Actor, proc:Proc, pos:int, overlay_prev:StraightlineOverlay ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires StraightlineOverlayValid(cr, overlay_prev, b, actor, proc) requires last(overlay_prev.positions) == pos requires last(overlay_prev.sb.states).aux.phase.StraightlinePhaseYielded? requires cr.get_actor_pc_stack(last(overlay_prev.sb.states).state, actor).Some? requires var s := last(overlay_prev.sb.states).state; var pc := cr.get_actor_pc_stack(s, actor).v.pc; cr.is_loop_head(pc) ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseLooped? { var ss := last(overlay_prev.sb.states); var StraightlineState(s, aux) := ss; var phase := aux.phase; var overlay_pos := |overlay_prev.sb.trace|; lemma_StraightlineBehaviorNeverChangesStack(cr, overlay_prev.sb, actor, proc, overlay_pos); var pc := cr.get_actor_pc_stack(s, actor).v.pc; var sspec := GetStraightlineSpec(cr, actor, proc); lemma_StraightlineBehaviorSatisfiesGlobalInvariant(cr, overlay_prev.sb, actor, proc, overlay_pos); if pc in aux.visited_loops { lemma_VisitedLoopsOnlyContainsLoopHeads(cr, overlay_prev.sb, actor, proc, pc, overlay_pos); assert StraightlineBehaviorsSatisfyLoopModifiesClausesOnJumpBackConditions(cr, overlay_prev.sb, actor, proc); var earlier_pos := lemma_FindWhenLoopBegan(cr, b, actor, proc, overlay_prev, pc, overlay_pos); var ss_earlier := overlay_prev.sb.states[earlier_pos]; var sstep := overlay_prev.sb.trace[earlier_pos]; var aux_new := StraightlineUpdateAux(cr, ss_earlier, actor, sstep, s); var ss_new := StraightlineState(s, aux_new); var states := overlay_prev.sb.states[..earlier_pos + 1] + [ss_new]; var trace := overlay_prev.sb.trace[..earlier_pos] + [sstep]; var positions := overlay_prev.positions[..earlier_pos + 1] + [pos]; lemma_TakeSequenceMonotonic(overlay_prev.positions, earlier_pos + 1); lemma_UseSequenceMonotonic(overlay_prev.positions, earlier_pos, |overlay_prev.positions|-1); lemma_ExtendSequenceMonotonic(overlay_prev.positions[..earlier_pos + 1], pos); lemma_TakeStateNextSeq(overlay_prev.sb.states, overlay_prev.sb.trace, sspec.next, earlier_pos); lemma_InvariantPredicateHoldsAtStep(b, pos, cr.spec, cr.established_inv); lemma_StraightlineBehaviorNeverChangesStack(cr, overlay_prev.sb, actor, proc, 0); lemma_StraightlineBehaviorNeverChangesStack(cr, overlay_prev.sb, actor, proc, earlier_pos); lemma_StraightlineBehaviorNeverChangesStack(cr, overlay_prev.sb, actor, proc, |overlay_prev.sb.states|-1); assert StraightlineSpecNextLoopModifies(cr, ss_earlier, ss_new, sstep, actor, proc, pc); assert StraightlineSpecNext(cr, ss_earlier, ss_new, sstep, actor, proc); lemma_ExtendStateNextSeqRight(overlay_prev.sb.states[..earlier_pos + 1], overlay_prev.sb.trace[..earlier_pos], sspec.next, ss_new, sstep); var sb := AnnotatedBehavior(states, trace); assert AnnotatedBehaviorSatisfiesSpec(sb, sspec); overlay := StraightlineOverlay(sb, overlay_prev.how_started, positions); } else { var AnnotatedBehavior(sstates, strace) := overlay_prev.sb; var positions := overlay_prev.positions; var sstep0 := StraightlineStepLoopModifies(pc); var aux1 := StraightlineUpdateAux(cr, last(sstates), actor, sstep0, s); var sstate1 := StraightlineState(s, aux1); lemma_InvariantPredicateHoldsAtStep(b, last(overlay_prev.positions), cr.spec, cr.established_inv); assert StraightlineBehaviorsSatisfyLoopModifiesClausesOnEntryConditions(cr, overlay_prev.sb, actor, proc); assert StraightlineSpecNextLoopModifies(cr, last(sstates), sstate1, sstep0, actor, proc, pc); assert StraightlineSpecNext(cr, last(sstates), sstate1, sstep0, actor, proc); lemma_ExtendStateNextSeqRight(sstates, strace, sspec.next, sstate1, sstep0); sstates := sstates + [sstate1]; strace := strace + [sstep0]; lemma_ExtendSequenceMonotonic(positions, pos); positions := positions + [pos]; overlay := overlay_prev.(sb := AnnotatedBehavior(sstates, strace), positions := positions); } } /////////////////////////////////////////////// // Cases of obtaining straightline behaviors /////////////////////////////////////////////// lemma lemma_GetStraightlineBehaviorCaseInit( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires cr.get_actor_pc_stack(b.states[0], actor).Some? requires ActorInProc(cr, b.states[0], actor, proc) ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, 0) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? { var positions := [0]; lemma_EstablishSequenceMonotonic(positions); var s := b.states[0]; assert ActorsBeginAtEntryPointsWithEmptyStacksConditions(cr, s, actor); assert RequiresClausesSatisfiedInitiallyConditions(cr, s, actor); var ss := StraightlineState(s, StraightlineInitAux()); var sb := AnnotatedBehavior([ss], []); lemma_InvariantPredicateHoldsAtStart(s, cr.spec, cr.established_inv); assert StraightlineSpecInit(cr, sb.states[0], actor, proc); var overlay_prev := StraightlineOverlay(sb, HowStartedInit, [0]); overlay := lemma_ExtendOverlayWithYielding(cr, b, actor, proc, 0, overlay_prev); } lemma lemma_GetStraightlineBehaviorCaseNormal( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, pos:int, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 < pos < |b.states| requires cr.state_ok(b.states[pos]) requires ActorInProc(cr, b.states[pos], actor, proc) requires cr.step_to_actor(b.trace[pos-1]).Some? requires cr.step_to_actor(b.trace[pos-1]).v == actor requires cr.step_to_effect(b.trace[pos-1]).CHLStepEffectNormal? ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? decreases pos, 1 { var prev := pos-1; var s := b.states[prev]; var s' := b.states[prev+1]; var step := b.trace[prev]; assert ActionTuple(s, s', step) in cr.spec.next; var pc := cr.get_actor_pc_stack(s, actor).v.pc; var pc' := cr.get_actor_pc_stack(s', actor).v.pc; assert cr.pc_to_proc(pc') == cr.pc_to_proc(pc); var overlay_prev := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos-1, actor, proc); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, overlay_prev.sb, step, s'); assert StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, overlay_prev.sb, step, s'); if cr.is_loop_head(pc) { overlay_prev := lemma_ExtendOverlayWithLooping(cr, b, actor, proc, pos-1, overlay_prev); } var sspec := GetStraightlineSpec(cr, actor, proc); var sstates := overlay_prev.sb.states; var strace := overlay_prev.sb.trace; var ss := last(sstates); var sstep := StraightlineStepNormal(step); var aux := ss.aux; var aux' := StraightlineUpdateAux(cr, ss, actor, sstep, s'); var ss' := StraightlineState(s', aux'); var overlay_pos := |overlay_prev.sb.states| - 1; lemma_StraightlineBehaviorSatisfiesGlobalInvariant(cr, overlay_prev.sb, actor, proc, overlay_pos); lemma_PCVisitedIfInLoopedPhase(cr, overlay_prev.sb, actor, proc, overlay_pos); lemma_InvariantPredicateHoldsAtStep(b, pos, cr.spec, cr.established_inv); assert StraightlineSpecNextNormal(cr, last(sstates), ss', sstep, actor, proc, step); lemma_ExtendStateNextSeqRight(sstates, strace, sspec.next, ss', sstep); var sb_next := AnnotatedBehavior(sstates + [ss'], strace + [sstep]); var positions := overlay_prev.positions + [pos]; lemma_ExtendSequenceMonotonic(overlay_prev.positions, pos); var overlay_next := StraightlineOverlay(sb_next, overlay_prev.how_started, positions); overlay := lemma_ExtendOverlayWithYielding(cr, b, actor, proc, pos, overlay_next); } lemma lemma_GetStraightlineBehaviorCaseFork( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, pos:int, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 < pos < |b.states| requires cr.state_ok(b.states[pos]) requires cr.get_actor_pc_stack(b.states[pos-1], actor).None? requires cr.get_actor_pc_stack(b.states[pos], actor).Some? requires ActorInProc(cr, b.states[pos], actor, proc) requires cr.get_actor_pc_stack(b.states[pos], actor).Some? requires cr.step_to_actor(b.trace[pos-1]).Some? requires cr.step_to_actor(b.trace[pos-1]).v != actor ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? decreases pos, 0 { var prev := pos-1; var s := b.states[prev]; var s' := b.states[prev+1]; var step := b.trace[prev]; var effect := cr.step_to_effect(step); assert ActionTuple(s, s', step) in cr.spec.next; var pc' := cr.get_actor_pc_stack(s', actor).v.pc; var forker_actor := cr.step_to_actor(step).v; var PCStack(forker_pc, forker_stack) := cr.get_actor_pc_stack(s, forker_actor).v; assert ForkedActorsStartAtEntryPointsWithEmptyStacksConditions(cr, s, s', step, actor); assert !effect.CHLStepEffectExit?; var forker_proc, forker_overlay; if effect.CHLStepEffectReturn? || effect.CHLStepEffectReturnThenCall? { var overlay_alt := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, prev, forker_actor, cr.pc_to_proc(forker_pc)); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, overlay_alt.sb, step, s'); forker_proc := forker_stack[0]; forker_overlay := lemma_GetStraightlineBehaviorForCallerProc(cr, b, prev, forker_actor, forker_proc); } else { forker_proc := cr.step_to_proc(step); forker_overlay := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, prev, forker_actor, forker_proc); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, forker_overlay.sb, step, s'); } assert StraightlineBehaviorsSatisfyPreconditionsForForksConditions(cr, forker_overlay.sb, step, s', actor); assert StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, forker_overlay.sb, step, s'); assert cr.global_inv(s'); var myb := forker_overlay.sb; assert AnnotatedBehaviorSatisfiesSpec(myb, GetStraightlineSpec(cr, forker_actor, forker_proc)); assert ActionTuple(s, s', step) in cr.spec.next; assert cr.step_to_actor(step).v == forker_actor; assert cr.get_actor_pc_stack(s, forker_actor).Some?; assert cr.get_actor_pc_stack(s, actor).None?; assert cr.get_actor_pc_stack(s', actor).Some?; assert pc' == cr.get_actor_pc_stack(s', actor).v.pc; assert proc == cr.pc_to_proc(pc'); assert cr.requires_clauses(proc, s', actor); var ss := StraightlineState(s', StraightlineInitAux()); var overlay_prev := StraightlineOverlay(AnnotatedBehavior([ss], []), HowStartedFork, [pos]); lemma_EstablishSequenceMonotonic(overlay_prev.positions); lemma_InvariantPredicateHoldsAtStep(b, pos, cr.spec, cr.established_inv); assert IsForkStep(cr, actor, s); assert StraightlineSpecInit(cr, ss, actor, proc); assert StraightlineOverlayValid(cr, overlay_prev, b, actor, proc); overlay := lemma_ExtendOverlayWithYielding(cr, b, actor, proc, pos, overlay_prev); } lemma lemma_GetStraightlineBehaviorCaseOtherActor( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, pos:int, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 < pos < |b.states| requires cr.state_ok(b.states[pos]) requires cr.get_actor_pc_stack(b.states[pos], actor).Some? requires ActorInProc(cr, b.states[pos], actor, proc) requires cr.step_to_actor(b.trace[pos-1]).Some? requires cr.step_to_actor(b.trace[pos-1]).v != actor ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? decreases pos, 1 { var prev := pos-1; var s := b.states[prev]; var s' := b.states[prev+1]; var step := b.trace[prev]; assert ActionTuple(s, s', step) in cr.spec.next; if cr.get_actor_pc_stack(s, actor).None? { overlay := lemma_GetStraightlineBehaviorCaseFork(cr, b, pos, actor, proc); return; } var other_actor := cr.step_to_actor(step).v; var PCStack(other_pc, other_stack) := cr.get_actor_pc_stack(s, other_actor).v; var effect := cr.step_to_effect(step); var other_proc, overlay_other; if effect.CHLStepEffectReturn? || effect.CHLStepEffectReturnThenCall? { var overlay_alt := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos - 1, other_actor, cr.pc_to_proc(other_pc)); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, overlay_alt.sb, step, s'); other_proc := other_stack[0]; overlay_other := lemma_GetStraightlineBehaviorForCallerProc(cr, b, pos - 1, other_actor, other_proc); } else { other_proc := cr.step_to_proc(step); overlay_other := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos - 1, other_actor, other_proc); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, overlay_other.sb, step, s'); } assert StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, overlay_other.sb, step, s'); assert StraightlineBehaviorsSatisfyYieldPredicateConditions(cr, overlay_other.sb, step, s', actor); assert StepsDontChangeOtherActorsExceptViaForkConditions(cr, s, s', step, actor); var overlay_prev := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos - 1, actor, proc); overlay := lemma_ExtendYieldingOverlayOneStep(cr, b, actor, proc, pos, overlay_prev); } lemma lemma_GetStraightlineBehaviorCaseActorless( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, pos:int, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 < pos < |b.states| requires cr.state_ok(b.states[pos]) requires cr.get_actor_pc_stack(b.states[pos], actor).Some? requires ActorInProc(cr, b.states[pos], actor, proc) requires cr.step_to_actor(b.trace[pos-1]).None? ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? decreases pos, 1 { var prev := pos-1; var s := b.states[prev]; var s' := b.states[prev+1]; var step := b.trace[prev]; assert ActionTuple(s, s', step) in cr.spec.next; lemma_InvariantPredicateHoldsAtStep(b, prev, cr.spec, cr.established_inv); assert ActorlessStepsDontChangeActorsConditions(cr, s, s', step, actor); var overlay_prev := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos-1, actor, proc); lemma_InvariantPredicateHoldsAtStep(b, pos, cr.spec, cr.established_inv); lemma_StraightlineBehaviorSatisfiesGlobalInvariant(cr, overlay_prev.sb, actor, proc, |overlay_prev.sb.states|-1); assert ActorlessStepsMaintainYieldPredicateAndGlobalInvariantConditions(cr, s, s', step, actor); overlay := lemma_ExtendYieldingOverlayOneStep(cr, b, actor, proc, pos, overlay_prev); } lemma lemma_GetStraightlineBehaviorCaseCall( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, pos:int, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 < pos < |b.states| requires cr.state_ok(b.states[pos]) requires ActorInProc(cr, b.states[pos], actor, proc) requires cr.step_to_actor(b.trace[pos-1]).Some? requires cr.step_to_actor(b.trace[pos-1]).v == actor requires var effect := cr.step_to_effect(b.trace[pos-1]); effect.CHLStepEffectCall? || effect.CHLStepEffectReturnThenCall? ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? decreases pos, 1 { var prev := pos-1; var s := b.states[prev]; var s' := b.states[prev+1]; var step := b.trace[prev]; assert ActionTuple(s, s', step) in cr.spec.next; var effect := cr.step_to_effect(step); var pc' := cr.get_actor_pc_stack(s', actor).v.pc; var caller_pc := cr.get_actor_pc_stack(s, actor).v.pc; var caller_proc := cr.step_to_proc(step); var caller_overlay; if effect.CHLStepEffectCall? { caller_overlay := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos-1, actor, caller_proc); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, caller_overlay.sb, step, s'); } else { var overlay_alt := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos-1, actor, cr.pc_to_proc(caller_pc)); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, overlay_alt.sb, step, s'); caller_overlay := lemma_GetStraightlineBehaviorForCallerProc(cr, b, pos-1, actor, caller_proc); } assert StraightlineBehaviorsSatisfyPreconditionsForCallsConditions(cr, caller_overlay.sb, step, s'); assert StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, caller_overlay.sb, step, s'); assert cr.requires_clauses(proc, s', actor); assert cr.global_inv(s'); var positions := [pos]; lemma_EstablishSequenceMonotonic(positions); var ss := StraightlineState(s', StraightlineInitAux()); var sb := AnnotatedBehavior([ss], []); lemma_InvariantPredicateHoldsAtStep(b, pos, cr.spec, cr.established_inv); var overlay_prev := StraightlineOverlay(sb, HowStartedCall, positions); assert StraightlineSpecInit(cr, ss, actor, proc); overlay := lemma_ExtendOverlayWithYielding(cr, b, actor, proc, pos, overlay_prev); } lemma lemma_GetStraightlineBehaviorCaseReturn( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, pos:int, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 < pos < |b.states| requires cr.state_ok(b.states[pos]) requires ActorInProc(cr, b.states[pos], actor, proc) requires cr.step_to_actor(b.trace[pos-1]).Some? requires cr.step_to_actor(b.trace[pos-1]).v == actor requires cr.step_to_effect(b.trace[pos-1]).CHLStepEffectReturn? ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? decreases pos, 1 { var prev := pos-1; var s := b.states[prev]; var s' := b.states[prev+1]; var step := b.trace[prev]; assert ActionTuple(s, s', step) in cr.spec.next; assert s' == b.states[pos]; var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; var PCStack(pc', stack') := cr.get_actor_pc_stack(s', actor).v; var caller_proc := cr.pc_to_proc(pc); var caller_overlay := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos-1, actor, caller_proc); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, caller_overlay.sb, step, s'); var overlay_prev := lemma_GetStraightlineBehaviorForCallerProc(cr, b, pos-1, actor, proc); var sspec := GetStraightlineSpec(cr, actor, proc); var sstates := overlay_prev.sb.states; var strace := overlay_prev.sb.trace; var ss := last(sstates); var aux := ss.aux; var sstep := StraightlineStepReturn(step); var aux' := StraightlineUpdateAux(cr, ss, actor, sstep, s'); var ss' := StraightlineState(s', aux'); var overlay_pos := |overlay_prev.sb.states| - 1; lemma_StraightlineBehaviorSatisfiesGlobalInvariant(cr, overlay_prev.sb, actor, proc, overlay_pos); lemma_PCVisitedIfInLoopedPhase(cr, overlay_prev.sb, actor, proc, overlay_pos); assert StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, overlay_prev.sb, step, s'); lemma_InvariantPredicateHoldsAtStep(b, pos, cr.spec, cr.established_inv); assert StraightlineSpecNextReturn(cr, last(sstates), ss', sstep, actor, proc, step); lemma_ExtendStateNextSeqRight(sstates, strace, sspec.next, ss', sstep); var sb_next := AnnotatedBehavior(sstates + [ss'], strace + [sstep]); var positions := overlay_prev.positions + [pos]; lemma_ExtendSequenceMonotonic(overlay_prev.positions, pos); var overlay_next := StraightlineOverlay(sb_next, overlay_prev.how_started, positions); overlay := lemma_ExtendOverlayWithYielding(cr, b, actor, proc, pos, overlay_next); } /////////////////////////////////////////// // Obtaining straightline behaviors /////////////////////////////////////////// lemma lemma_GetStraightlineBehaviorForCallerProc( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, pos:int, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 <= pos < |b.states| requires cr.state_ok(b.states[pos]) requires cr.get_actor_pc_stack(b.states[pos], actor).Some? requires var PCStack(pc, stack) := cr.get_actor_pc_stack(b.states[pos], actor).v; |stack| > 0 && stack[0] == proc && cr.is_return_site(pc) ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseEnsured? decreases pos, 3 { var s := b.states[pos]; var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; var callee := cr.pc_to_proc(pc); var overlay_alt := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, pos, actor, callee); lemma_StraightlineBehaviorNeverChangesStack(cr, overlay_alt.sb, actor, callee, 0); lemma_StraightlineBehaviorNeverChangesStack(cr, overlay_alt.sb, actor, callee, |overlay_alt.sb.states|-1); var call_pos := overlay_alt.positions[0] - 1; if overlay_alt.how_started.HowStartedInit? { assert ActorsBeginAtEntryPointsWithEmptyStacksConditions(cr, b.states[overlay_alt.positions[0]], actor); assert false; } var call_s := b.states[call_pos]; var call_s' := b.states[call_pos+1]; var call_step := b.trace[call_pos]; assert ActionTuple(call_s, call_s', call_step) in cr.spec.next; if overlay_alt.how_started.HowStartedFork? { assert ForkedActorsStartAtEntryPointsWithEmptyStacksConditions(cr, call_s, call_s', call_step, actor); assert false; } assert overlay_alt.how_started.HowStartedCall?; assert IsCallStep(cr, actor, call_step); assert proc == cr.step_to_proc(call_step); var call_effect := cr.step_to_effect(call_step); assert call_effect.CHLStepEffectCall? || call_effect.CHLStepEffectReturnThenCall?; var simple_call := call_effect.CHLStepEffectCall?; assert cr.requires_clauses(callee, call_s', actor); assert StraightlineBehaviorsSatisfyPostconditionsConditions(cr, overlay_alt.sb, actor, callee); assert cr.ensures_clauses(callee, overlay_alt.sb.states[0].state, last(overlay_alt.sb.states).state, actor); assert cr.ensures_clauses(callee, call_s', s, actor); lemma_UseSequenceMonotonic(overlay_alt.positions, 0, |overlay_alt.positions|-1); lemma_StraightlineBehaviorNeverChangesStack(cr, overlay_alt.sb, actor, callee, |overlay_alt.sb.states| - 1); var overlay_prev; if simple_call { overlay_prev := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, call_pos, actor, proc); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, overlay_prev.sb, call_step, call_s'); assert StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, overlay_prev.sb, call_step, call_s'); if cr.is_loop_head(cr.get_actor_pc_stack(call_s, actor).v.pc) { overlay_prev := lemma_ExtendOverlayWithLooping(cr, b, actor, proc, call_pos, overlay_prev); } } else { var caller_pc := cr.get_actor_pc_stack(call_s, actor).v.pc; var caller_proc := cr.pc_to_proc(caller_pc); var caller_overlay := lemma_GetStraightlineBehaviorForCurrentProc(cr, b, call_pos, actor, caller_proc); assert StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, caller_overlay.sb, call_step, call_s'); overlay_prev := lemma_GetStraightlineBehaviorForCallerProc(cr, b, call_pos, actor, proc); assert StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, overlay_prev.sb, call_step, call_s'); } var sspec := GetStraightlineSpec(cr, actor, proc); var sstates := overlay_prev.sb.states; var strace := overlay_prev.sb.trace; var positions := overlay_prev.positions; var sstep0; if simple_call { sstep0 := StraightlineStepCall(call_step); } else { sstep0 := StraightlineStepReturnThenCall(call_step); } var aux0 := StraightlineUpdateAux(cr, last(sstates), actor, sstep0, call_s'); var sstate0 := StraightlineState(call_s', aux0); var sstep1 := StraightlineStepEnsures(callee); var aux1 := StraightlineUpdateAux(cr, sstate0, actor, sstep1, s); var sstate1 := StraightlineState(s, aux1); var overlay_pos := |overlay_prev.sb.states| - 1; lemma_ExtendSequenceMonotonic(positions, call_pos + 1); lemma_PCVisitedIfInLoopedPhase(cr, overlay_prev.sb, actor, proc, overlay_pos); if simple_call { assert StraightlineSpecNextCall(cr, last(sstates), sstate0, sstep0, actor, proc, call_step); } else { assert StraightlineSpecNextReturnThenCall(cr, last(sstates), sstate0, sstep0, actor, proc, call_step); } assert StraightlineSpecNext(cr, last(sstates), sstate0, sstep0, actor, proc); lemma_ExtendStateNextSeqRight(sstates, strace, sspec.next, sstate0, sstep0); lemma_ExtendSequenceMonotonic(positions + [call_pos + 1], pos); lemma_StraightlineBehaviorSatisfiesGlobalInvariant(cr, overlay_alt.sb, actor, callee, |overlay_alt.sb.states| - 1); lemma_InvariantPredicateHoldsAtStep(b, pos, cr.spec, cr.established_inv); assert StraightlineSpecNextEnsures(cr, sstate0, sstate1, sstep1, actor, proc, callee); assert StraightlineSpecNext(cr, sstate0, sstate1, sstep1, actor, proc); lemma_ExtendStateNextSeqRight(sstates + [sstate0], strace + [sstep0], sspec.next, sstate1, sstep1); var sb := AnnotatedBehavior(sstates + [sstate0] + [sstate1], strace + [sstep0] + [sstep1]); overlay := StraightlineOverlay(sb, overlay_prev.how_started, positions + [call_pos + 1] + [pos]); } lemma lemma_GetStraightlineBehaviorForCurrentProc( cr:ConcurrentHoareLogicRequest, b:AnnotatedBehavior, pos:int, actor:Actor, proc:Proc ) returns ( overlay:StraightlineOverlay ) requires IsValidConcurrentHoareLogicRequest(cr) requires AnnotatedBehaviorSatisfiesSpec(b, cr.spec) requires 0 <= pos < |b.states| requires cr.get_actor_pc_stack(b.states[pos], actor).Some? requires cr.state_ok(b.states[pos]) requires ActorInProc(cr, b.states[pos], actor, proc) ensures StraightlineOverlayLeadsToPos(cr, overlay, b, actor, proc, pos) ensures last(overlay.sb.states).aux.phase.StraightlinePhaseYielded? decreases pos, 2 { if pos == 0 { overlay := lemma_GetStraightlineBehaviorCaseInit(cr, b, actor, proc); return; } var prev := pos-1; var step := b.trace[prev]; if cr.step_to_actor(step).Some? && cr.step_to_actor(step).v != actor { overlay := lemma_GetStraightlineBehaviorCaseOtherActor(cr, b, pos, actor, proc); return; } assert ActionTuple(b.states[prev], b.states[prev+1], b.trace[prev]) in cr.spec.next; var effect := cr.step_to_effect(step); match effect { case CHLStepEffectNormal => overlay := lemma_GetStraightlineBehaviorCaseNormal(cr, b, pos, actor, proc); case CHLStepEffectCall(_) => overlay := lemma_GetStraightlineBehaviorCaseCall(cr, b, pos, actor, proc); case CHLStepEffectReturnThenCall(_) => overlay := lemma_GetStraightlineBehaviorCaseCall(cr, b, pos, actor, proc); case CHLStepEffectReturn => overlay := lemma_GetStraightlineBehaviorCaseReturn(cr, b, pos, actor, proc); case CHLStepEffectActorless => overlay := lemma_GetStraightlineBehaviorCaseActorless(cr, b, pos, actor, proc); case CHLStepEffectExit => assert false; } } } ================================================ FILE: Armada/strategies/chl/ConcurrentHoareLogicSpec.i.dfy ================================================ include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../../util/collections/maps.s.dfy" include "../../util/collections/seqs.s.dfy" include "../../util/option.s.dfy" module ConcurrentHoareLogicSpecModule { // Yields are dealt with as follows. // * On a call, the callee has to deal with a yield happening after the requires clause is satisfied. // * On reaching a loop head for the first time, the loop modifies clause must hold before the yield. // * On reaching a loop a subsequent time, the loop modifies clause must hold between s1 and s2, where s1 is the // state on reaching the loop head before a yield, and s2 is the state on returning to the loop head before yielding. // * After executing a loop-modifies clause, a yield can happen. // * On a return, the callee/returner must deal with a yield happening after reaching the return site. That is, // it must ensure the postcondition even after a yield. // * After executing an ensures clause, one does not have to worry about a yield happening. // The result of calling cr.step_to_proc on a step indicates which procedure that step is part of. // * A step that calls from method X into method Y is considered part of method X. // * A step that returns from method Y to method X is considered part of method X. That's not a typo: it's considered // part of the caller, not the callee. // * A step that returns from method Y to method X and then calls method Z is likewise considered part of method X. import opened util_collections_maps_s import opened util_collections_seqs_s import opened util_option_s import opened AnnotatedBehaviorModule import opened InvariantsModule ///////////////////////////////////////////////////// // Type definitions ///////////////////////////////////////////////////// datatype StraightlineStep = StraightlineStepNormal(step:Step) | StraightlineStepLoopModifies(pc:PC) | StraightlineStepYield | StraightlineStepCall(step:Step) | StraightlineStepEnsures(callee:Proc) | StraightlineStepReturn(step:Step) | StraightlineStepReturnThenCall(step:Step) datatype StraightlinePhase = StraightlinePhaseNormal | StraightlinePhaseLooped | StraightlinePhaseYielded | StraightlinePhaseCalled | StraightlinePhaseEnsured datatype StraightlineAux = StraightlineAux(phase:StraightlinePhase, visited_loops:map) datatype StraightlineState = StraightlineState(state:State, aux:StraightlineAux) datatype PCStack = PCStack(pc:PC, stack:seq) datatype CHLStepEffect = CHLStepEffectNormal | CHLStepEffectCall(callee:Proc) | CHLStepEffectReturn | CHLStepEffectExit | CHLStepEffectReturnThenCall(callee:Proc) | CHLStepEffectActorless | CHLStepEffectStop datatype ConcurrentHoareLogicRequest = ConcurrentHoareLogicRequest( spec:AnnotatedBehaviorSpec, step_to_actor:Step->Option, step_to_proc:Step->Proc, get_actor_pc_stack:(State, Actor)->Option>, state_ok:State->bool, pc_to_proc:PC->Proc, is_entry_point:PC->bool, is_loop_head:PC->bool, is_return_site:PC->bool, step_to_effect:Step->CHLStepEffect, established_inv:State->bool, global_inv:State->bool, local_inv:(State, Actor)->bool, yield_pred:(State, State, Actor)->bool, requires_clauses:(Proc, State, Actor)->bool, ensures_clauses:(Proc, State, State, Actor)->bool, loop_modifies_clauses:(PC, State, State, Actor)->bool ) ///////////////////////////////////////////////////// // Straightline behavior specification ///////////////////////////////////////////////////// function StraightlineInitAux() : StraightlineAux { StraightlineAux(StraightlinePhaseNormal, map []) } predicate StraightlineSpecInit( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, actor:Actor, proc:Proc ) { // A straightline behavior begins with the actor at the beginning of a procedure var s := ss.state; && cr.get_actor_pc_stack(s, actor).Some? && var pc := cr.get_actor_pc_stack(s, actor).v.pc; && cr.pc_to_proc(pc) == proc && cr.is_entry_point(pc) && cr.established_inv(s) && cr.global_inv(s) && cr.state_ok(s) && cr.requires_clauses(proc, s, actor) && ss.aux == StraightlineInitAux() } function StraightlineUpdateAux( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, actor:Actor, sstep:StraightlineStep, s':State ) : StraightlineAux { var phase' := match sstep case StraightlineStepNormal(_) => StraightlinePhaseNormal case StraightlineStepLoopModifies(_) => StraightlinePhaseLooped case StraightlineStepYield() => StraightlinePhaseYielded case StraightlineStepCall(_) => StraightlinePhaseCalled case StraightlineStepEnsures(_) => StraightlinePhaseEnsured case StraightlineStepReturn(_) => StraightlinePhaseNormal case StraightlineStepReturnThenCall(_) => StraightlinePhaseCalled ; var StraightlineState(s, aux) := ss; var visited_loops' := if sstep.StraightlineStepLoopModifies? && cr.get_actor_pc_stack(s, actor).Some? then aux.visited_loops[cr.get_actor_pc_stack(s, actor).v.pc := s] else aux.visited_loops; StraightlineAux(phase', visited_loops') } predicate StraightlineSpecNextCommon( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor ) { var StraightlineState(s, aux) := ss; var StraightlineState(s', aux') := ss'; // The actor must be present both before and after the step. && cr.get_actor_pc_stack(s, actor).Some? && cr.get_actor_pc_stack(s', actor).Some? // The type of step the state machine can take is dictated by the current phase. (The phase depends on the previous // step, so this restriction restricts consecutive pairs of steps.) && var pc := cr.get_actor_pc_stack(s, actor).v.pc; && (match aux.phase case StraightlinePhaseNormal => sstep.StraightlineStepYield? case StraightlinePhaseLooped => sstep.StraightlineStepNormal? || sstep.StraightlineStepCall? case StraightlinePhaseYielded => if cr.is_loop_head(pc) then sstep.StraightlineStepLoopModifies? else sstep.StraightlineStepNormal? || sstep.StraightlineStepCall? case StraightlinePhaseCalled => sstep.StraightlineStepEnsures? case StraightlinePhaseEnsured => sstep.StraightlineStepReturn? || sstep.StraightlineStepReturnThenCall?) // The established and global invariants are always preserved, and the state is always OK. && cr.established_inv(s') && cr.global_inv(s') && cr.state_ok(s') // The auxiliary information is a deterministic function of the current state and the straightline step taken. && aux' == StraightlineUpdateAux(cr, ss, actor, sstep, s') // If the state machine has reached an already-visited loop head and yielded, it can't execute further. && !(pc in aux.visited_loops && aux.phase.StraightlinePhaseYielded?) // The state machine can never return, except to return from a call that it made to another method. && (sstep.StraightlineStepReturn? || sstep.StraightlineStepReturnThenCall? ==> ss.aux.phase.StraightlinePhaseEnsured?) } predicate StraightlineSpecNextStep( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc, step:Step ) { && StraightlineSpecNextCommon(cr, ss, ss', sstep, actor) && cr.step_to_actor(step).Some? && cr.step_to_actor(step).v == actor && cr.step_to_proc(step) == proc && ActionTuple(ss.state, ss'.state, step) in cr.spec.next && cr.local_inv(ss.state, actor) } predicate StraightlineSpecNextNormal( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc, step:Step ) requires sstep.StraightlineStepNormal? { && StraightlineSpecNextStep(cr, ss, ss', sstep, actor, proc, step) && cr.step_to_effect(step).CHLStepEffectNormal? } predicate StraightlineSpecNextCall( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc, step:Step ) requires sstep.StraightlineStepCall? { && StraightlineSpecNextStep(cr, ss, ss', sstep, actor, proc, step) && cr.step_to_effect(step).CHLStepEffectCall? && var s' := ss'.state; var callee := cr.step_to_effect(step).callee; && callee == cr.pc_to_proc(cr.get_actor_pc_stack(s', actor).v.pc) && cr.requires_clauses(callee, s', actor) } predicate StraightlineSpecNextReturn( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc, step:Step ) requires sstep.StraightlineStepReturn? { && StraightlineSpecNextStep(cr, ss, ss', sstep, actor, proc, step) && cr.step_to_effect(step).CHLStepEffectReturn? } predicate StraightlineSpecNextReturnThenCall( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc, step:Step ) requires sstep.StraightlineStepReturnThenCall? { && StraightlineSpecNextStep(cr, ss, ss', sstep, actor, proc, step) && cr.step_to_effect(step).CHLStepEffectReturnThenCall? && var s' := ss'.state; var callee := cr.step_to_effect(step).callee; && callee == cr.pc_to_proc(cr.get_actor_pc_stack(s', actor).v.pc) && cr.requires_clauses(callee, s', actor) } predicate StraightlineSpecNextYield( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc ) requires sstep.StraightlineStepYield? { && StraightlineSpecNextCommon(cr, ss, ss', sstep, actor) && var s := ss.state; var s' := ss'.state; && cr.get_actor_pc_stack(s', actor) == cr.get_actor_pc_stack(s, actor) && cr.yield_pred(s, s', actor) } predicate StraightlineSpecNextLoopModifies( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc, pc:PC ) requires sstep.StraightlineStepLoopModifies? { && StraightlineSpecNextCommon(cr, ss, ss', sstep, actor) && var s := ss.state; var s' := ss'.state; && pc == cr.get_actor_pc_stack(s, actor).v.pc && cr.is_loop_head(pc) && cr.get_actor_pc_stack(s', actor) == cr.get_actor_pc_stack(s, actor) && cr.loop_modifies_clauses(pc, s, s', actor) } predicate StraightlineSpecNextEnsures( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc, callee:Proc ) requires sstep.StraightlineStepEnsures? { && StraightlineSpecNextCommon(cr, ss, ss', sstep, actor) && var s := ss.state; var s' := ss'.state; var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; var PCStack(pc', stack') := cr.get_actor_pc_stack(s', actor).v; && callee == cr.pc_to_proc(pc) && callee == cr.pc_to_proc(pc') && cr.is_return_site(pc') && stack' == stack && cr.ensures_clauses(callee, s, s', actor) } predicate StraightlineSpecNext( cr:ConcurrentHoareLogicRequest, ss:StraightlineState, ss':StraightlineState, sstep:StraightlineStep, actor:Actor, proc:Proc ) { match sstep case StraightlineStepNormal(step) => StraightlineSpecNextNormal(cr, ss, ss', sstep, actor, proc, step) case StraightlineStepLoopModifies(pc) => StraightlineSpecNextLoopModifies(cr, ss, ss', sstep, actor, proc, pc) case StraightlineStepYield() => StraightlineSpecNextYield(cr, ss, ss', sstep, actor, proc) case StraightlineStepCall(step) => StraightlineSpecNextCall(cr, ss, ss', sstep, actor, proc, step) case StraightlineStepEnsures(callee) => StraightlineSpecNextEnsures(cr, ss, ss', sstep, actor, proc, callee) case StraightlineStepReturn(step) => StraightlineSpecNextReturn(cr, ss, ss', sstep, actor, proc, step) case StraightlineStepReturnThenCall(step) => StraightlineSpecNextReturnThenCall(cr, ss, ss', sstep, actor, proc, step) } function GetStraightlineSpec( cr:ConcurrentHoareLogicRequest, actor:Actor, proc:Proc ) : AnnotatedBehaviorSpec, StraightlineStep> { AnnotatedBehaviorSpec(iset s | StraightlineSpecInit(cr, s, actor, proc), iset s, s', step | StraightlineSpecNext(cr, s, s', step, actor, proc) :: ActionTuple(s, s', step)) } ///////////////////////////////////////////////////// // Simple parameter validity ///////////////////////////////////////////////////// predicate LoopHeadsArentReturnSites( is_loop_head:PC->bool, is_return_site:PC->bool ) { forall pc :: !(is_loop_head(pc) && is_return_site(pc)) } predicate YieldPredicateReflexive( cr:ConcurrentHoareLogicRequest ) { forall s, actor {:trigger cr.yield_pred(s, s, actor)} :: cr.state_ok(s) && cr.get_actor_pc_stack(s, actor).Some? ==> cr.yield_pred(s, s, actor) } predicate YieldPredicateTransitive( cr:ConcurrentHoareLogicRequest ) { forall s1, s2, s3, actor {:trigger cr.yield_pred(s1, s2, actor), cr.yield_pred(s2, s3, actor)} :: && cr.established_inv(s1) && cr.global_inv(s1) && cr.established_inv(s2) && cr.global_inv(s2) && cr.yield_pred(s1, s2, actor) && cr.yield_pred(s2, s3, actor) ==> cr.yield_pred(s1, s3, actor) } ///////////////////////////////////////////////////// // State initialization ///////////////////////////////////////////////////// predicate ActorsBeginAtEntryPointsWithEmptyStacksConditions( cr:ConcurrentHoareLogicRequest, s:State, actor:Actor ) { && s in cr.spec.init && cr.get_actor_pc_stack(s, actor).Some? } predicate ActorsBeginAtEntryPointsWithEmptyStacks( cr:ConcurrentHoareLogicRequest ) { // At initialization, each thread is at a procedure entry point with an empty stack forall s, actor {:trigger ActorsBeginAtEntryPointsWithEmptyStacksConditions(cr, s, actor)} :: ActorsBeginAtEntryPointsWithEmptyStacksConditions(cr, s, actor) ==> var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; && cr.is_entry_point(pc) && |stack| == 0 } predicate GlobalInvariantSatisfiedInitially( cr:ConcurrentHoareLogicRequest ) { forall s :: s in cr.spec.init ==> cr.global_inv(s) && cr.state_ok(s) } predicate RequiresClausesSatisfiedInitiallyConditions( cr:ConcurrentHoareLogicRequest, s:State, actor:Actor ) { && s in cr.spec.init && cr.get_actor_pc_stack(s, actor).Some? } predicate RequiresClausesSatisfiedInitially( cr:ConcurrentHoareLogicRequest ) { forall s, actor {:trigger RequiresClausesSatisfiedInitiallyConditions(cr, s, actor)} :: RequiresClausesSatisfiedInitiallyConditions(cr, s, actor) ==> var pc := cr.get_actor_pc_stack(s, actor).v.pc; var proc := cr.pc_to_proc(pc); cr.requires_clauses(proc, s, actor) } ///////////////////////////////////////////////////// // Actorless steps ///////////////////////////////////////////////////// predicate ActorlessStepsDontChangeActorsConditions( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step, actor:Actor ) { && cr.established_inv(s) && ActionTuple(s, s', step) in cr.spec.next && cr.step_to_actor(step).None? } predicate ActorlessStepsDontChangeActors( cr:ConcurrentHoareLogicRequest ) { forall s, s', step, actor {:trigger ActorlessStepsDontChangeActorsConditions(cr, s, s', step, actor)} :: ActorlessStepsDontChangeActorsConditions(cr, s, s', step, actor) ==> cr.get_actor_pc_stack(s', actor) == cr.get_actor_pc_stack(s, actor) } predicate ActorlessStepsMaintainYieldPredicateAndGlobalInvariantConditions( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step, actor:Actor ) { && cr.established_inv(s) && cr.global_inv(s) && cr.established_inv(s') && ActionTuple(s, s', step) in cr.spec.next && cr.step_to_actor(step).None? && cr.get_actor_pc_stack(s, actor).Some? } predicate ActorlessStepsMaintainYieldPredicateAndGlobalInvariant( cr:ConcurrentHoareLogicRequest ) { forall s, s', step, actor {:trigger ActorlessStepsMaintainYieldPredicateAndGlobalInvariantConditions(cr, s, s', step, actor)} :: ActorlessStepsMaintainYieldPredicateAndGlobalInvariantConditions(cr, s, s', step, actor) ==> cr.yield_pred(s, s', actor) && cr.global_inv(s') && cr.state_ok(s') } ///////////////////////////////////////////////////// // Step effects ///////////////////////////////////////////////////// predicate CorrectCHLStepEffectNormal( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step ) { && cr.state_ok(s) && cr.state_ok(s') && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; && cr.get_actor_pc_stack(s, actor).Some? && cr.get_actor_pc_stack(s', actor).Some? && var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; var PCStack(pc', stack') := cr.get_actor_pc_stack(s', actor).v; && !cr.is_return_site(pc) && cr.pc_to_proc(pc') == cr.pc_to_proc(pc) == cr.step_to_proc(step) && stack' == stack } predicate CorrectCHLStepEffectCall( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step, callee:Proc ) { && cr.state_ok(s) && cr.state_ok(s') && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; && cr.get_actor_pc_stack(s, actor).Some? && cr.get_actor_pc_stack(s', actor).Some? && var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; var PCStack(pc', stack') := cr.get_actor_pc_stack(s', actor).v; && !cr.is_return_site(pc) && cr.is_entry_point(pc') && cr.pc_to_proc(pc') == callee && |stack'| > 0 && stack'[0] == cr.pc_to_proc(pc) == cr.step_to_proc(step) && stack'[1..] == stack } predicate CorrectCHLStepEffectReturn( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step ) { && cr.state_ok(s) && cr.state_ok(s') && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; && cr.get_actor_pc_stack(s, actor).Some? && cr.get_actor_pc_stack(s', actor).Some? && var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; var PCStack(pc', stack') := cr.get_actor_pc_stack(s', actor).v; && cr.is_return_site(pc) && |stack| > 0 && cr.pc_to_proc(pc') == stack[0] == cr.step_to_proc(step) && stack' == stack[1..] } predicate CorrectCHLStepEffectExit( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step ) { && cr.state_ok(s) && cr.state_ok(s') && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; && cr.get_actor_pc_stack(s, actor).Some? && cr.get_actor_pc_stack(s', actor).None? && var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; && cr.is_return_site(pc) && cr.pc_to_proc(pc) == cr.step_to_proc(step) && |stack| == 0 } predicate CorrectCHLStepEffectReturnThenCall( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step, callee:Proc ) { // Step that returns to a caller, then does another call && cr.state_ok(s) && cr.state_ok(s') && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; && cr.get_actor_pc_stack(s, actor).Some? && cr.get_actor_pc_stack(s', actor).Some? && var PCStack(pc, stack) := cr.get_actor_pc_stack(s, actor).v; var PCStack(pc', stack') := cr.get_actor_pc_stack(s', actor).v; && cr.is_return_site(pc) && |stack| > 0 && stack[0] == cr.step_to_proc(step) && cr.pc_to_proc(pc') == callee && cr.is_entry_point(pc') && stack' == stack } predicate CorrectCHLStepEffectActorless( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step ) { && cr.state_ok(s) && cr.state_ok(s') && cr.step_to_actor(step).None? } predicate CorrectCHLStepEffectStop( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step ) { && cr.state_ok(s) && !cr.state_ok(s') && (cr.step_to_actor(step).Some? ==> cr.get_actor_pc_stack(s, cr.step_to_actor(step).v).Some?) } predicate CorrectCHLStepEffect( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step ) { match cr.step_to_effect(step) case CHLStepEffectNormal => CorrectCHLStepEffectNormal(cr, s, s', step) case CHLStepEffectCall(callee) => CorrectCHLStepEffectCall(cr, s, s', step, callee) case CHLStepEffectReturn => CorrectCHLStepEffectReturn(cr, s, s', step) case CHLStepEffectExit => CorrectCHLStepEffectExit(cr, s, s', step) case CHLStepEffectReturnThenCall(callee) => CorrectCHLStepEffectReturnThenCall(cr, s, s', step, callee) case CHLStepEffectActorless => CorrectCHLStepEffectActorless(cr, s, s', step) case CHLStepEffectStop => CorrectCHLStepEffectStop(cr, s, s', step) } predicate CHLStepEffectsCorrect( cr:ConcurrentHoareLogicRequest ) { forall s, s', step :: ActionTuple(s, s', step) in cr.spec.next ==> CorrectCHLStepEffect(cr, s, s', step) } ///////////////////////////////////////////////////// // Other control flow ///////////////////////////////////////////////////// predicate ForkedActorsStartAtEntryPointsWithEmptyStacksConditions( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step, forked_actor:Actor ) { && ActionTuple(s, s', step) in cr.spec.next && cr.get_actor_pc_stack(s, forked_actor).None? && cr.get_actor_pc_stack(s', forked_actor).Some? } predicate ForkedActorsStartAtEntryPointsWithEmptyStacks( cr:ConcurrentHoareLogicRequest ) { // When a thread starts (i.e., is forked), the forking step isn't actorless or an exit and the forked thread // is at a procedure entry point with an empty stack forall s, s', step, forked_actor {:trigger ForkedActorsStartAtEntryPointsWithEmptyStacksConditions(cr, s, s', step, forked_actor)} :: ForkedActorsStartAtEntryPointsWithEmptyStacksConditions(cr, s, s', step, forked_actor) ==> && cr.step_to_actor(step).Some? && !cr.step_to_effect(step).CHLStepEffectExit? && var PCStack(pc', stack') := cr.get_actor_pc_stack(s', forked_actor).v; && cr.is_entry_point(pc') && |stack'| == 0 } predicate StepsDontChangeOtherActorsExceptViaForkConditions( cr:ConcurrentHoareLogicRequest, s:State, s':State, step:Step, other_actor:Actor ) { && ActionTuple(s, s', step) in cr.spec.next && cr.step_to_actor(step) != Some(other_actor) && cr.get_actor_pc_stack(s, other_actor).Some? } predicate StepsDontChangeOtherActorsExceptViaFork( cr:ConcurrentHoareLogicRequest ) { // One actor taking a step leaves each other actor's PC and stack unchanged // (unless that other actor didn't have a PC before, i.e., this actor was forking that actor) forall s, s', step, other_actor {:trigger StepsDontChangeOtherActorsExceptViaForkConditions(cr, s, s', step, other_actor)} :: StepsDontChangeOtherActorsExceptViaForkConditions(cr, s, s', step, other_actor) ==> cr.get_actor_pc_stack(s', other_actor) == cr.get_actor_pc_stack(s, other_actor) } ///////////////////////////////////////////////////// // Requirements for straightline behaviors ///////////////////////////////////////////////////// predicate StraightlineBehaviorsSatisfyGlobalInvariantConditions( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, step:Step, s':State ) { && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; var proc := cr.step_to_proc(step); var effect := cr.step_to_effect(step); && AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) && var ss := last(sb.states); var s := ss.state; var phase := ss.aux.phase; && cr.local_inv(s, actor) && ActionTuple(s, s', step) in cr.spec.next && !effect.CHLStepEffectStop? && (if effect.CHLStepEffectReturn? || effect.CHLStepEffectReturnThenCall? then phase.StraightlinePhaseEnsured? else phase.StraightlinePhaseYielded?) } predicate StraightlineBehaviorsSatisfyGlobalInvariant( cr:ConcurrentHoareLogicRequest ) { forall sb, step, s' {:trigger StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, sb, step, s')} :: StraightlineBehaviorsSatisfyGlobalInvariantConditions(cr, sb, step, s') ==> cr.global_inv(s') } predicate StraightlineBehaviorsSatisfyLocalInvariantConditions( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, step:Step, s':State ) { && cr.step_to_actor(step).Some? && |sb.states| > 0 && var ss := last(sb.states); var s := ss.state; var phase := ss.aux.phase; var actor := cr.step_to_actor(step).v; && cr.get_actor_pc_stack(s, actor).Some? && var pc := cr.get_actor_pc_stack(s, actor).v.pc; var proc := cr.pc_to_proc(pc); && AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) && ActionTuple(s, s', step) in cr.spec.next && phase.StraightlinePhaseYielded? } predicate StraightlineBehaviorsSatisfyLocalInvariant( cr:ConcurrentHoareLogicRequest ) { forall sb, step, s' {:trigger StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, sb, step, s')} :: StraightlineBehaviorsSatisfyLocalInvariantConditions(cr, sb, step, s') ==> cr.local_inv(last(sb.states).state, cr.step_to_actor(step).v) } predicate StraightlineBehaviorsSatisfyPreconditionsForCallsConditions( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, step:Step, s':State ) { && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; var proc := cr.step_to_proc(step); var effect := cr.step_to_effect(step); && AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) && var ss := last(sb.states); var s := ss.state; var phase := ss.aux.phase; && cr.local_inv(s, actor) && ActionTuple(s, s', step) in cr.spec.next && (|| (effect.CHLStepEffectCall? && phase.StraightlinePhaseYielded?) || (effect.CHLStepEffectReturnThenCall? && phase.StraightlinePhaseEnsured?)) && cr.get_actor_pc_stack(s', actor).Some? } predicate StraightlineBehaviorsSatisfyPreconditionsForCalls( cr:ConcurrentHoareLogicRequest ) { forall sb, step, s' {:trigger StraightlineBehaviorsSatisfyPreconditionsForCallsConditions(cr, sb, step, s')} :: StraightlineBehaviorsSatisfyPreconditionsForCallsConditions(cr, sb, step, s') ==> var actor := cr.step_to_actor(step).v; var callee := cr.step_to_effect(step).callee; cr.requires_clauses(callee, s', actor) } predicate StraightlineBehaviorsSatisfyPreconditionsForForksConditions( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, step:Step, s':State, forked_actor:Actor ) { && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; var proc := cr.step_to_proc(step); var effect := cr.step_to_effect(step); && AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) && var ss := last(sb.states); var s := ss.state; var phase := ss.aux.phase; && cr.local_inv(s, actor) && ActionTuple(s, s', step) in cr.spec.next && !effect.CHLStepEffectStop? && (if effect.CHLStepEffectReturn? || effect.CHLStepEffectReturnThenCall? then phase.StraightlinePhaseEnsured? else phase.StraightlinePhaseYielded?) && actor != forked_actor && cr.get_actor_pc_stack(s, forked_actor).None? && cr.get_actor_pc_stack(s', forked_actor).Some? } predicate StraightlineBehaviorsSatisfyPreconditionsForForks( cr:ConcurrentHoareLogicRequest ) { forall sb, step, s', forked_actor {:trigger StraightlineBehaviorsSatisfyPreconditionsForForksConditions(cr, sb, step, s', forked_actor)} :: StraightlineBehaviorsSatisfyPreconditionsForForksConditions(cr, sb, step, s', forked_actor) ==> var forked_pc := cr.get_actor_pc_stack(s', forked_actor).v.pc; var forked_proc := cr.pc_to_proc(forked_pc); cr.requires_clauses(forked_proc, s', forked_actor) } predicate StraightlineBehaviorsSatisfyPostconditionsConditions( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, actor:Actor, proc:Proc ) { && AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) && var ss := last(sb.states); var s := ss.state; var phase := ss.aux.phase; && cr.get_actor_pc_stack(s, actor).Some? && var pc := cr.get_actor_pc_stack(s, actor).v.pc; && cr.is_return_site(pc) && phase.StraightlinePhaseYielded? } predicate StraightlineBehaviorsSatisfyPostconditions( cr:ConcurrentHoareLogicRequest ) { forall sb, actor, proc {:trigger StraightlineBehaviorsSatisfyPostconditionsConditions(cr, sb, actor, proc)} :: StraightlineBehaviorsSatisfyPostconditionsConditions(cr, sb, actor, proc) ==> cr.ensures_clauses(proc, sb.states[0].state, last(sb.states).state, actor) } predicate StraightlineBehaviorsSatisfyLoopModifiesClausesOnEntryConditions( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, actor:Actor, proc:Proc ) { && AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) && var ss := last(sb.states); var StraightlineState(s, aux) := ss; var phase := aux.phase; && cr.get_actor_pc_stack(s, actor).Some? && var pc := cr.get_actor_pc_stack(s, actor).v.pc; && cr.is_loop_head(pc) && pc !in aux.visited_loops && phase.StraightlinePhaseYielded? } predicate StraightlineBehaviorsSatisfyLoopModifiesClausesOnEntry( cr:ConcurrentHoareLogicRequest ) { forall sb, actor, proc {:trigger StraightlineBehaviorsSatisfyLoopModifiesClausesOnEntryConditions(cr, sb, actor, proc)} :: StraightlineBehaviorsSatisfyLoopModifiesClausesOnEntryConditions(cr, sb, actor, proc) ==> var s := last(sb.states).state; var pc := cr.get_actor_pc_stack(s, actor).v.pc; cr.loop_modifies_clauses(pc, s, s, actor) } predicate StraightlineBehaviorsSatisfyLoopModifiesClausesOnJumpBackConditions( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, actor:Actor, proc:Proc ) { && AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) && var ss := last(sb.states); var s := ss.state; var phase := ss.aux.phase; && cr.get_actor_pc_stack(s, actor).Some? && var pc := cr.get_actor_pc_stack(s, actor).v.pc; && cr.is_loop_head(pc) && pc in ss.aux.visited_loops && phase.StraightlinePhaseYielded? } predicate StraightlineBehaviorsSatisfyLoopModifiesClausesOnJumpBack( cr:ConcurrentHoareLogicRequest ) { forall sb, actor, proc {:trigger StraightlineBehaviorsSatisfyLoopModifiesClausesOnJumpBackConditions(cr, sb, actor, proc)} :: StraightlineBehaviorsSatisfyLoopModifiesClausesOnJumpBackConditions(cr, sb, actor, proc) ==> var StraightlineState(s, aux) := last(sb.states); var pc := cr.get_actor_pc_stack(s, actor).v.pc; cr.loop_modifies_clauses(pc, aux.visited_loops[pc], s, actor) } predicate StraightlineBehaviorsSatisfyYieldPredicateConditions( cr:ConcurrentHoareLogicRequest, sb:AnnotatedBehavior, StraightlineStep>, step:Step, s':State, other_actor:Actor ) { && cr.step_to_actor(step).Some? && var actor := cr.step_to_actor(step).v; var proc := cr.step_to_proc(step); var effect := cr.step_to_effect(step); && AnnotatedBehaviorSatisfiesSpec(sb, GetStraightlineSpec(cr, actor, proc)) && var ss := last(sb.states); var s := ss.state; var phase := ss.aux.phase; && cr.local_inv(s, actor) && ActionTuple(s, s', step) in cr.spec.next && !effect.CHLStepEffectStop? && (if effect.CHLStepEffectReturn? || effect.CHLStepEffectReturnThenCall? then phase.StraightlinePhaseEnsured? else phase.StraightlinePhaseYielded?) && actor != other_actor && cr.get_actor_pc_stack(s, other_actor).Some? } predicate StraightlineBehaviorsSatisfyYieldPredicate( cr:ConcurrentHoareLogicRequest ) { forall sb, step, s', other_actor {:trigger StraightlineBehaviorsSatisfyYieldPredicateConditions(cr, sb, step, s', other_actor)} :: StraightlineBehaviorsSatisfyYieldPredicateConditions(cr, sb, step, s', other_actor) ==> cr.yield_pred(last(sb.states).state, s', other_actor) } ///////////////////////////////////////////////////// // Request validity ///////////////////////////////////////////////////// predicate IsValidConcurrentHoareLogicRequest( cr:ConcurrentHoareLogicRequest ) { // Simple parameter validity && IsInvariantPredicateOfSpec(cr.established_inv, cr.spec) && LoopHeadsArentReturnSites(cr.is_loop_head, cr.is_return_site) && YieldPredicateReflexive(cr) && YieldPredicateTransitive(cr) // Initialization && ActorsBeginAtEntryPointsWithEmptyStacks(cr) && GlobalInvariantSatisfiedInitially(cr) && RequiresClausesSatisfiedInitially(cr) // Actorless steps && ActorlessStepsDontChangeActors(cr) && ActorlessStepsMaintainYieldPredicateAndGlobalInvariant(cr) // Control flow && CHLStepEffectsCorrect(cr) && ForkedActorsStartAtEntryPointsWithEmptyStacks(cr) && StepsDontChangeOtherActorsExceptViaFork(cr) // Straightline behaviors && StraightlineBehaviorsSatisfyGlobalInvariant(cr) && StraightlineBehaviorsSatisfyLocalInvariant(cr) && StraightlineBehaviorsSatisfyPreconditionsForCalls(cr) && StraightlineBehaviorsSatisfyPreconditionsForForks(cr) && StraightlineBehaviorsSatisfyPostconditions(cr) && StraightlineBehaviorsSatisfyLoopModifiesClausesOnEntry(cr) && StraightlineBehaviorsSatisfyLoopModifiesClausesOnJumpBack(cr) && StraightlineBehaviorsSatisfyYieldPredicate(cr) } } ================================================ FILE: Armada/strategies/combining/ArmadaCombining.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file contains a lemma for performing refinement via combining on an Armada behavior. Such a // combining takes a behavior satisfying a low-level spec, which has a certain atomic step // represented as a multistep consisting of two or more mini-steps, and returns a behavior // satisfying a higher-level specification that has that atomic step consist of a single mini-step. // It does so by lifting multi-step multisteps in the lower-level specification to single-step // multisteps in the higher-level specification. // // To use this lemma, first create a request r of type ArmadaCombiningRequest (defined in // ArmadaCombiningSpec.i.dfy). Then, prove that it's a valid request, i.e., that // ValidArmadaCombiningRequest(r). // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "ArmadaCombiningSpec.i.dfy" include "ArmadaCombiningLemmas.i.dfy" module ArmadaCombiningModule { import opened util_collections_seqs_s import opened util_collections_seqs_i import opened GeneralRefinementModule import opened AnnotatedBehaviorModule import opened ArmadaCombiningSpecModule import opened ArmadaCombiningLemmasModule import opened GenericArmadaLemmasModule import opened ArmadaCommonDefinitions import opened GenericArmadaSpecModule lemma lemma_PerformArmadaCombining( acr:ArmadaCombiningRequest, lb:AnnotatedBehavior> ) returns ( hb:AnnotatedBehavior> ) requires ValidArmadaCombiningRequest(acr) requires AnnotatedBehaviorSatisfiesSpec(lb, SpecFunctionsToSpec(acr.l)) ensures BehaviorRefinesBehavior(lb.states, hb.states, acr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, SpecFunctionsToSpec(acr.h)) { var hstates := MapSeqToSeq(lb.states, acr.lstate_to_hstate); var htrace := []; var pos := 0; while pos < |lb.trace| invariant 0 <= pos <= |lb.trace| invariant |htrace| == pos invariant AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(hstates[..pos+1], htrace), SpecFunctionsToSpec(acr.h)) invariant acr.inv(lb.states[pos]) { lemma_MultistepNextMaintainsInv(acr.l, acr.inv, lb.states[pos], lb.states[pos+1], lb.trace[pos]); lemma_AllThreadsYieldingAtPos(acr.l, lb, pos); lemma_AllThreadsYieldingAtPos(acr.l, lb, pos+1); var hstep := lemma_LiftMultistep(acr, lb.states[pos], lb.states[pos+1], lb.trace[pos], hstates[pos], hstates[pos+1]); htrace := htrace + [hstep]; pos := pos + 1; } hb := AnnotatedBehavior(hstates, htrace); var lh_map := ConvertMapToSeq(|lb.states|, map i | 0 <= i < |lb.states| :: RefinementRange(i, i)); assert BehaviorRefinesBehaviorUsingRefinementMap(lb.states, hstates, acr.relation, lh_map); } } ================================================ FILE: Armada/strategies/combining/ArmadaCombiningLemmas.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file contains lemmas useful in effecting a refinement via combining on an Armada behavior. // They support lemma_PerformArmadaCombining (in ArmadaCombining.i.dfy). // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "ArmadaCombiningSpec.i.dfy" include "../../util/collections/seqs.i.dfy" include "../generic/GenericArmadaLemmas.i.dfy" module ArmadaCombiningLemmasModule { import opened util_collections_seqs_s import opened util_collections_seqs_i import opened util_option_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened ArmadaCombiningSpecModule import opened GenericArmadaSpecModule import opened GenericArmadaLemmasModule import opened ArmadaCommonDefinitions lemma lemma_LiftMultistepCaseNoSteps( acr:ArmadaCombiningRequest, ls:LState, ls':LState, lstep:Armada_Multistep, hs:HState, hs':HState ) returns ( hstep:Armada_Multistep ) requires ValidArmadaCombiningRequest(acr) requires acr.inv(ls) requires ActionTuple(ls, ls', lstep) in SpecFunctionsToSpec(acr.l).next requires forall tid :: Armada_ThreadYielding(acr.l, ls, tid) requires forall tid :: Armada_ThreadYielding(acr.l, ls', tid) requires |lstep.steps| == 0 requires hs == acr.lstate_to_hstate(ls) requires hs' == acr.lstate_to_hstate(ls') ensures ActionTuple(hs, hs', hstep) in SpecFunctionsToSpec(acr.h).next { hstep := Armada_Multistep([], lstep.tid, lstep.tau); } lemma lemma_LiftMultistepCaseTau( acr:ArmadaCombiningRequest, ls:LState, ls':LState, lstep:Armada_Multistep, hs:HState, hs':HState ) returns ( hstep:Armada_Multistep ) requires ValidArmadaCombiningRequest(acr) requires acr.inv(ls) requires ActionTuple(ls, ls', lstep) in SpecFunctionsToSpec(acr.l).next requires forall tid :: Armada_ThreadYielding(acr.l, ls, tid) requires forall tid :: Armada_ThreadYielding(acr.l, ls', tid) requires lstep.tau requires |lstep.steps| == 1 requires hs == acr.lstate_to_hstate(ls) requires hs' == acr.lstate_to_hstate(ls') ensures ActionTuple(hs, hs', hstep) in SpecFunctionsToSpec(acr.h).next { var lonestep := lstep.steps[0]; assert acr.l.step_valid(ls, lonestep); assert acr.l.state_ok(ls); var tid := acr.l.step_to_thread(lonestep); assert Armada_NextMultipleSteps(acr.l, acr.l.step_next(ls, lonestep), ls', lstep.steps[1..]); assert lstep.steps[1..] == []; assert ls' == acr.l.step_next(ls, lonestep); assert Armada_ThreadYielding(acr.l, ls, tid); assert !IsInnerStep(acr, ls, ls', lonestep); var honestep := acr.lonestep_to_honestep(lonestep); assert NonInnerStepsLiftableConditions(acr, ls, lonestep); assert acr.h.step_valid(hs, honestep) && hs' == acr.h.step_next(hs, honestep); hstep := Armada_Multistep([honestep], acr.h.step_to_thread(honestep), acr.h.is_step_tau(honestep)); assert Armada_NextMultistep(acr.h, hs, hs', hstep.steps, hstep.tid, hstep.tau); } lemma lemma_LiftMultistepCaseCombinable( acr:ArmadaCombiningRequest, ls:LState, ls':LState, lstep:Armada_Multistep, hs:HState, hs':HState ) returns ( hstep:Armada_Multistep ) requires ValidArmadaCombiningRequest(acr) requires acr.inv(ls) requires ActionTuple(ls, ls', lstep) in SpecFunctionsToSpec(acr.l).next requires forall tid :: Armada_ThreadYielding(acr.l, ls, tid) requires forall tid :: Armada_ThreadYielding(acr.l, ls', tid) requires !lstep.tau requires |lstep.steps| > 0 requires IsInnerOptionalPC(acr, acr.l.get_thread_pc(acr.l.step_next(ls, lstep.steps[0]), lstep.tid)) requires hs == acr.lstate_to_hstate(ls) requires hs' == acr.lstate_to_hstate(ls') ensures ActionTuple(hs, hs', hstep) in SpecFunctionsToSpec(acr.h).next { assert CanCombineInnerStepsConditions(acr, ls, ls', lstep.steps, lstep.tid); var honestep :| InnerStepsCombined(acr, ls, ls', honestep); assert !acr.h.is_step_tau(honestep) && acr.h.step_valid(hs, honestep) && hs' == acr.h.step_next(hs, honestep); hstep := Armada_Multistep([honestep], acr.h.step_to_thread(honestep), false); assert Armada_ThreadYielding(acr.l, ls, hstep.tid); assert Armada_ThreadYielding(acr.l, ls', hstep.tid); assert Armada_NextMultistep(acr.h, hs, hs', hstep.steps, hstep.tid, hstep.tau); assert ActionTuple(hs, hs', hstep) in SpecFunctionsToSpec(acr.h).next; } lemma lemma_LiftMultistepCaseAllNonInnerSteps ( acr:ArmadaCombiningRequest, ls:LState, ls':LState, lstep:Armada_Multistep, hs:HState, hs':HState, lstates:seq ) returns ( hstep:Armada_Multistep ) requires ValidArmadaCombiningRequest(acr) requires acr.inv(ls) requires ActionTuple(ls, ls', lstep) in SpecFunctionsToSpec(acr.l).next requires forall tid :: Armada_ThreadYielding(acr.l, ls, tid) requires forall tid :: Armada_ThreadYielding(acr.l, ls', tid) requires lstates == Armada_GetStateSequence(acr.l, ls, lstep.steps); requires !lstep.tau requires |lstep.steps| > 0 requires forall i :: 0 <= i < |lstep.steps| ==> !IsInnerStep(acr, lstates[i], lstates[i+1], lstep.steps[i]) requires hs == acr.lstate_to_hstate(ls) requires hs' == acr.lstate_to_hstate(ls') ensures ActionTuple(hs, hs', hstep) in SpecFunctionsToSpec(acr.h).next { var tid := lstep.tid; var lsteps := lstep.steps; var hsteps := MapSeqToSeq(lsteps, acr.lonestep_to_honestep); hstep := Armada_Multistep(hsteps, tid, lstep.tau); var hstates := Armada_GetStateSequence(acr.h, hs, hsteps); var pos := 0; while pos < |hsteps| invariant 0 <= pos <= |hsteps| invariant forall i :: 0 <= i <= pos ==> hstates[i] == acr.lstate_to_hstate(lstates[i]) invariant forall i :: 0 <= i < pos ==> acr.h.step_to_thread(hsteps[i]) == acr.l.step_to_thread(lsteps[i]) invariant forall i :: 0 <= i < pos ==> acr.h.is_step_tau(hsteps[i]) == acr.l.is_step_tau(lsteps[i]) invariant forall i :: 0 <= i < pos ==> acr.h.step_valid(hstates[i], hsteps[i]) invariant forall i :: 0 <= i < pos ==> hstates[i+1] == acr.h.step_next(hstates[i], hsteps[i]) invariant forall i :: 0 < i < pos ==> var pc := acr.h.get_thread_pc(hstates[i], tid); pc.Some? && acr.h.is_pc_nonyielding(pc.v) { lemma_InvariantHoldsAtIntermediateState(acr.l, acr.inv, ls, ls', lsteps, lstates, pos); lemma_ArmadaNextMultipleStepsImpliesValidStep(acr.l, ls, ls', lsteps, lstates, pos); assert !IsInnerStep(acr, lstates[pos], lstates[pos+1], lsteps[pos]); assert NonInnerStepsLiftableConditions(acr, lstates[pos], lsteps[pos]); lemma_ArmadaGetStateSequenceOnePos(acr.h, hs, hsteps, pos); assert hstates[pos+1] == acr.h.step_next(hstates[pos], hsteps[pos]); assert hstates[pos+1] == acr.lstate_to_hstate(lstates[pos+1]); pos := pos + 1; } lemma_ArmadaNextMultipleStepsLastElement(acr.l, ls, ls', lsteps, lstates); lemma_IfAllStepsValidThenArmadaNextMultipleSteps(acr.h, hstates, hsteps); assert Armada_NextMultistep(acr.h, hs, hs', hsteps, tid, lstep.tau); } lemma lemma_LiftMultistepCaseNonInnerPC( acr:ArmadaCombiningRequest, ls:LState, ls':LState, lstep:Armada_Multistep, hs:HState, hs':HState ) returns ( hstep:Armada_Multistep ) requires ValidArmadaCombiningRequest(acr) requires acr.inv(ls) requires ActionTuple(ls, ls', lstep) in SpecFunctionsToSpec(acr.l).next requires forall tid :: Armada_ThreadYielding(acr.l, ls, tid) requires forall tid :: Armada_ThreadYielding(acr.l, ls', tid) requires !lstep.tau requires |lstep.steps| > 0 requires !IsInnerOptionalPC(acr, acr.l.get_thread_pc(acr.l.step_next(ls, lstep.steps[0]), lstep.tid)) requires hs == acr.lstate_to_hstate(ls) requires hs' == acr.lstate_to_hstate(ls') ensures ActionTuple(hs, hs', hstep) in SpecFunctionsToSpec(acr.h).next { var tid := lstep.tid; var lsteps := lstep.steps; var lstates := Armada_GetStateSequence(acr.l, ls, lstep.steps); var pos := 1; while pos < |lsteps| invariant 1 <= pos <= |lsteps| invariant forall i :: 0 <= i <= pos ==> !IsInnerOptionalPC(acr, acr.l.get_thread_pc(lstates[i], tid)) { var pc := acr.l.get_thread_pc(lstates[pos], tid); var pc' := acr.l.get_thread_pc(lstates[pos+1], tid); lemma_ArmadaNextMultipleStepsImpliesValidStep(acr.l, ls, ls', lsteps, lstates, pos); assert !acr.l.is_step_tau(lsteps[pos]); assert acr.l.step_valid(lstates[pos], lsteps[pos]); assert lstates[pos+1] == acr.l.step_next(lstates[pos], lsteps[pos]); if pc'.Some? && acr.is_inner_pc(pc'.v) { assert InnerPCPrecededByInnerOrYieldingPCConditions(acr, lstates[pos], lsteps[pos]); assert pc.Some? && (acr.is_inner_pc(pc.v) || !acr.l.is_pc_nonyielding(pc.v)); assert !IsInnerOptionalPC(acr, pc); assert !acr.l.is_pc_nonyielding(pc.v); assert false; } assert !IsInnerOptionalPC(acr, pc'); pos := pos + 1; } assert forall i :: 0 <= i <= |lsteps| ==> !IsInnerOptionalPC(acr, acr.l.get_thread_pc(lstates[i], tid)); hstep := lemma_LiftMultistepCaseAllNonInnerSteps(acr, ls, ls', lstep, hs, hs', lstates); } lemma lemma_LiftMultistep( acr:ArmadaCombiningRequest, ls:LState, ls':LState, lstep:Armada_Multistep, hs:HState, hs':HState ) returns ( hstep:Armada_Multistep ) requires ValidArmadaCombiningRequest(acr) requires acr.inv(ls) requires ActionTuple(ls, ls', lstep) in SpecFunctionsToSpec(acr.l).next requires forall tid :: Armada_ThreadYielding(acr.l, ls, tid) requires forall tid :: Armada_ThreadYielding(acr.l, ls', tid) requires hs == acr.lstate_to_hstate(ls) requires hs' == acr.lstate_to_hstate(ls') ensures ActionTuple(hs, hs', hstep) in SpecFunctionsToSpec(acr.h).next { var lstates := Armada_GetStateSequence(acr.l, ls, lstep.steps); if |lstep.steps| == 0 { hstep := lemma_LiftMultistepCaseNoSteps(acr, ls, ls', lstep, hs, hs'); } else if lstep.tau { hstep := lemma_LiftMultistepCaseTau(acr, ls, ls', lstep, hs, hs'); } else if IsInnerOptionalPC(acr, acr.l.get_thread_pc(acr.l.step_next(ls, lstep.steps[0]), lstep.tid)) { hstep := lemma_LiftMultistepCaseCombinable(acr, ls, ls', lstep, hs, hs'); } else { hstep := lemma_LiftMultistepCaseNonInnerPC(acr, ls, ls', lstep, hs, hs'); } } } ================================================ FILE: Armada/strategies/combining/ArmadaCombiningSpec.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file is the specification for a request to perform combining on an Armada behavior. Such a // combining takes a behavior satisfying a low-level spec, which has a certain atomic step // represented as a multistep consisting of two or more mini-steps, and returns a behavior // satisfying a higher-level specification that has that atomic step consist of a single mini-step. // It does so by lifting multi-step multisteps in the lower-level specification to single-step // multisteps in the higher-level specification. // // To use this specification, first create a request r of type ArmadaCombiningRequest. Then, prove // that it's a valid request, i.e., that ValidArmadaCombiningRequest(r). Finally, call // lemma_PerformArmadaCombining (in ArmadaCombining.i.dfy). // // The request describes the Armada state machine in an abstract way. It models the Armada // multisteps as instances of ArrStepSequence, which include a sequence of steps of type LOneStep. // It models the steps as being either tau steps or non-tau steps. // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "../../util/option.s.dfy" include "../../util/collections/seqs.s.dfy" include "../refinement/GeneralRefinementLemmas.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../generic/GenericArmadaSpec.i.dfy" module ArmadaCombiningSpecModule { import opened util_option_s import opened util_collections_seqs_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GenericArmadaSpecModule import opened ArmadaCommonDefinitions datatype ArmadaCombiningRequest = ArmadaCombiningRequest( l:Armada_SpecFunctions, h:Armada_SpecFunctions, relation:RefinementRelation, inv:LState->bool, lstate_to_hstate:LState->HState, lonestep_to_honestep:LOneStep->HOneStep, lpc_to_hpc:LPC->HPC, is_inner_pc:LPC->bool ) predicate IsInnerOptionalPC ( acr:ArmadaCombiningRequest, pc:Option ) { pc.Some? && acr.is_inner_pc(pc.v) } predicate IsInnerStep ( acr:ArmadaCombiningRequest, s:LState, s':LState, step:LOneStep ) { var tid := acr.l.step_to_thread(step); || IsInnerOptionalPC(acr, acr.l.get_thread_pc(s, tid)) || IsInnerOptionalPC(acr, acr.l.get_thread_pc(s', tid)) } predicate LInitImpliesHInit( acr:ArmadaCombiningRequest ) { forall ls :: acr.l.init(ls) ==> acr.h.init(acr.lstate_to_hstate(ls)) } predicate LStateToHStateMapsPCsCorrectly ( acr:ArmadaCombiningRequest ) { forall ls, tid :: var hs := acr.lstate_to_hstate(ls); var lpc := acr.l.get_thread_pc(ls, tid); var hpc := acr.h.get_thread_pc(hs, tid); hpc == if lpc.Some? then Some(acr.lpc_to_hpc(lpc.v)) else None() } predicate LHYieldingCorrespondence( acr:ArmadaCombiningRequest ) { forall lpc :: var hpc := acr.lpc_to_hpc(lpc); acr.is_inner_pc(lpc) || (acr.h.is_pc_nonyielding(hpc) <==> acr.l.is_pc_nonyielding(lpc)) } predicate StateConversionPreservesOK( acr:ArmadaCombiningRequest ) { forall ls :: acr.h.state_ok(acr.lstate_to_hstate(ls)) == acr.l.state_ok(ls) } predicate StateConversionSatisfiesRelation ( acr:ArmadaCombiningRequest ) { forall ls :: RefinementPair(ls, acr.lstate_to_hstate(ls)) in acr.relation } predicate InnerPCsDontYield( acr:ArmadaCombiningRequest ) { forall pc :: acr.is_inner_pc(pc) ==> acr.l.is_pc_nonyielding(pc) } predicate InnerPCPrecededByInnerOrYieldingPCConditions ( acr:ArmadaCombiningRequest, ls:LState, lstep:LOneStep ) { var ls' := acr.l.step_next(ls, lstep); var tid := acr.l.step_to_thread(lstep); var pc' := acr.l.get_thread_pc(ls', tid); && !acr.l.is_step_tau(lstep) && acr.l.step_valid(ls, lstep) && pc'.Some? && acr.is_inner_pc(pc'.v) } predicate InnerPCPrecededByInnerOrYieldingPC ( acr:ArmadaCombiningRequest ) { forall ls, lstep {:trigger InnerPCPrecededByInnerOrYieldingPCConditions(acr, ls, lstep)} :: InnerPCPrecededByInnerOrYieldingPCConditions(acr, ls, lstep) ==> var tid := acr.l.step_to_thread(lstep); var pc := acr.l.get_thread_pc(ls, tid); pc.Some? && (acr.is_inner_pc(pc.v) || !acr.l.is_pc_nonyielding(pc.v)) } predicate InnerPCSucceededByInnerOrYieldingPCConditions ( acr:ArmadaCombiningRequest, ls:LState, lstep:LOneStep ) { var tid := acr.l.step_to_thread(lstep); var pc := acr.l.get_thread_pc(ls, tid); && !acr.l.is_step_tau(lstep) && acr.l.step_valid(ls, lstep) && pc.Some? && acr.is_inner_pc(pc.v) } predicate InnerPCSucceededByInnerOrYieldingPC ( acr:ArmadaCombiningRequest ) { forall ls, lstep {:trigger InnerPCSucceededByInnerOrYieldingPCConditions(acr, ls, lstep)} :: InnerPCSucceededByInnerOrYieldingPCConditions(acr, ls, lstep) ==> var tid := acr.l.step_to_thread(lstep); var ls' := acr.l.step_next(ls, lstep); var pc' := acr.l.get_thread_pc(ls', tid); pc'.Some? && (acr.is_inner_pc(pc'.v) || !acr.l.is_pc_nonyielding(pc'.v)) } predicate NonInnerStepsLiftableConditions ( acr:ArmadaCombiningRequest, ls:LState, lstep:LOneStep ) { var ls' := acr.l.step_next(ls, lstep); && acr.inv(ls) && acr.l.step_valid(ls, lstep) && !IsInnerStep(acr, ls, ls', lstep) } predicate NonInnerStepsLiftable( acr:ArmadaCombiningRequest ) { forall ls, lstep {:trigger NonInnerStepsLiftableConditions(acr, ls, lstep)} :: NonInnerStepsLiftableConditions(acr, ls, lstep) ==> var ls' := acr.l.step_next(ls, lstep); var hs := acr.lstate_to_hstate(ls); var hstep := acr.lonestep_to_honestep(lstep); var hs' := acr.lstate_to_hstate(ls'); && acr.l.step_to_thread(lstep) == acr.h.step_to_thread(hstep) && acr.l.is_step_tau(lstep) == acr.h.is_step_tau(hstep) && acr.h.step_valid(hs, hstep) && hs' == acr.h.step_next(hs, hstep) } predicate CanCombineInnerStepsConditions ( acr:ArmadaCombiningRequest, ls:LState, ls':LState, lsteps:seq, tid:Armada_ThreadHandle ) { && acr.inv(ls) && Armada_NextMultistep(acr.l, ls, ls', lsteps, tid, false) && |lsteps| > 0 && IsInnerOptionalPC(acr, acr.l.get_thread_pc(acr.l.step_next(ls, lsteps[0]), tid)) } predicate InnerStepsCombined ( acr:ArmadaCombiningRequest, ls:LState, ls':LState, hstep:HOneStep ) { var hs := acr.lstate_to_hstate(ls); var hs' := acr.lstate_to_hstate(ls'); !acr.h.is_step_tau(hstep) && acr.h.step_valid(hs, hstep) && hs' == acr.h.step_next(hs, hstep) } predicate CanCombineInnerSteps ( acr:ArmadaCombiningRequest ) { forall ls, ls', lsteps, tid {:trigger CanCombineInnerStepsConditions(acr, ls, ls', lsteps, tid)} :: CanCombineInnerStepsConditions(acr, ls, ls', lsteps, tid) ==> exists hstep :: InnerStepsCombined(acr, ls, ls', hstep) } predicate ValidArmadaCombiningRequest( acr:ArmadaCombiningRequest ) { && InitImpliesInv(acr.l, acr.inv) && OneStepPreservesInv(acr.l, acr.inv) && OneStepRequiresOK(acr.l) && InitImpliesYielding(acr.l) && InitImpliesOK(acr.l) && SteppingThreadHasPC(acr.l) && TauLeavesPCUnchanged(acr.l) && ThreadCantAffectOtherThreadPCExceptViaFork(acr.l) && LInitImpliesHInit(acr) && LStateToHStateMapsPCsCorrectly(acr) && LHYieldingCorrespondence(acr) && StateConversionPreservesOK(acr) && StateConversionSatisfiesRelation(acr) && InnerPCsDontYield(acr) && InnerPCPrecededByInnerOrYieldingPC(acr) && InnerPCSucceededByInnerOrYieldingPC(acr) && NonInnerStepsLiftable(acr) && CanCombineInnerSteps(acr) } } ================================================ FILE: Armada/strategies/generic/GenericArmadaAtomic.i.dfy ================================================ include "GenericArmadaSpec.i.dfy" include "GenericArmadaLemmas.i.dfy" include "../invariants.i.dfy" include "../../util/collections/seqs.i.dfy" include "../../strategies/refinement/GeneralRefinementLemmas.i.dfy" module GenericArmadaAtomicModule { import opened util_collections_seqs_s import opened util_collections_seqs_i import opened util_option_s import opened GenericArmadaSpecModule import opened AnnotatedBehaviorModule import opened ArmadaCommonDefinitions import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened InvariantsModule import opened GenericArmadaLemmasModule ////////////////////////////////////////////// // DEFINITIONS ////////////////////////////////////////////// datatype AtomicPathType = AtomicPathType_Tau | AtomicPathType_YY | AtomicPathType_YS | AtomicPathType_YR | AtomicPathType_RY | AtomicPathType_RS | AtomicPathType_RR datatype AtomicTraceEntry = AtomicTraceEntry_Stutter() | AtomicTraceEntry_Tau(tau_tid: Armada_ThreadHandle, tau: Path) | AtomicTraceEntry_Normal(normal_tid: Armada_ThreadHandle, path: Path) | AtomicTraceEntry_Recurrent(recurrent_tid: Armada_ThreadHandle, yr: Path, rrs: seq, rx: Path) datatype AtomicSpecFunctions = AtomicSpecFunctions( init: State->bool, path_valid: (State, Path, Armada_ThreadHandle)->bool, path_next: (State, Path, Armada_ThreadHandle)->State, path_type: Path->AtomicPathType, state_ok: State->bool, get_thread_pc: (State, Armada_ThreadHandle)->Option, is_pc_nonyielding: PC->bool ) predicate AtomicValidPathSequence( asf: AtomicSpecFunctions, s: State, paths: seq, tid: Armada_ThreadHandle ) { |paths| > 0 ==> && asf.path_type(paths[0]).AtomicPathType_RR? && asf.path_valid(s, paths[0], tid) && AtomicValidPathSequence(asf, asf.path_next(s, paths[0], tid), paths[1..], tid) } function AtomicGetStateAfterPaths( asf: AtomicSpecFunctions, s: State, paths: seq, tid: Armada_ThreadHandle ) : State { if |paths| == 0 then s else AtomicGetStateAfterPaths(asf, asf.path_next(s, paths[0], tid), paths[1..], tid) } function AtomicGetStateSequence( asf: AtomicSpecFunctions, s: State, paths: seq, tid: Armada_ThreadHandle ) : (states: seq) ensures |states| == |paths| + 1 { if |paths| == 0 then [s] else [s] + AtomicGetStateSequence(asf, asf.path_next(s, paths[0], tid), paths[1..], tid) } function AtomicGetNextStateAlways( asf: AtomicSpecFunctions, s: State, entry: AtomicTraceEntry ) : State { match entry case AtomicTraceEntry_Stutter() => s case AtomicTraceEntry_Tau(tid, path) => asf.path_next(s, path, tid) case AtomicTraceEntry_Normal(tid, path) => asf.path_next(s, path, tid) case AtomicTraceEntry_Recurrent(tid, yr, rrs, rx) => var s1 := asf.path_next(s, yr, tid); var s2 := AtomicGetStateAfterPaths(asf, s1, rrs, tid); asf.path_next(s2, rx, tid) } predicate AtomicValidRecursiveStep( asf: AtomicSpecFunctions, s: State, tid: Armada_ThreadHandle, yr: Path, rrs: seq, rx: Path ) { // The first path has the type YR && asf.path_type(yr).AtomicPathType_YR? // All the paths are valid && var s1 := asf.path_next(s, yr, tid); var s2 := AtomicGetStateAfterPaths(asf, s1, rrs, tid); var s3 := asf.path_next(s2, rx, tid); && asf.path_valid(s, yr, tid) && AtomicValidPathSequence(asf, s1, rrs, tid) && asf.path_valid(s2, rx, tid) // The final state has ok-ness matching the path type && (if asf.state_ok(s3) then asf.path_type(rx).AtomicPathType_RY? else asf.path_type(rx).AtomicPathType_RS?) } predicate AtomicValidStep( asf: AtomicSpecFunctions, s: State, entry: AtomicTraceEntry ) { match entry case AtomicTraceEntry_Stutter() => true case AtomicTraceEntry_Tau(tid, path) => && asf.path_type(path).AtomicPathType_Tau? && asf.path_valid(s, path, tid) case AtomicTraceEntry_Normal(tid, path) => && asf.path_valid(s, path, tid) && (if asf.state_ok(asf.path_next(s, path, tid)) then asf.path_type(path).AtomicPathType_YY? else asf.path_type(path).AtomicPathType_YS?) case AtomicTraceEntry_Recurrent(tid, yr, rrs, rx) => AtomicValidRecursiveStep(asf, s, tid, yr, rrs, rx) } predicate AtomicNext( asf: AtomicSpecFunctions, s: State, s': State, entry: AtomicTraceEntry ) { && AtomicValidStep(asf, s, entry) && s' == AtomicGetNextStateAlways(asf, s, entry) } function AtomicAnnotatedSpec(asf: AtomicSpecFunctions) : AnnotatedBehaviorSpec> { AnnotatedBehaviorSpec(iset s | asf.init(s) :: s, iset s, s', entry | AtomicNext(asf, s, s', entry) :: ActionTuple(s, s', entry)) } function AtomicSpec(asf: AtomicSpecFunctions) : Spec { Spec(iset s | asf.init(s) :: s, iset s, s', entry: AtomicTraceEntry | AtomicNext(asf, s, s', entry) :: StatePair(s, s')) } function GenericAtomicLiftTraceEntry(lentry: AtomicTraceEntry, pathLift: LPath->HPath) : AtomicTraceEntry { match lentry case AtomicTraceEntry_Stutter() => AtomicTraceEntry_Stutter() case AtomicTraceEntry_Tau(tid, path) => AtomicTraceEntry_Tau(tid, pathLift(path)) case AtomicTraceEntry_Normal(tid, path) => AtomicTraceEntry_Normal(tid, pathLift(path)) case AtomicTraceEntry_Recurrent(tid, yr, rrs, rx) => AtomicTraceEntry_Recurrent(tid, pathLift(yr), MapSeqToSeq(rrs, pathLift), pathLift(rx)) } function GenericAtomicLiftPathSeqStateDependent( asf: AtomicSpecFunctions, ls: LState, paths: seq, tid: Armada_ThreadHandle, lift_fn: (LState, LPath)-->HPath ) : seq requires AtomicValidPathSequence(asf, ls, paths, tid) requires forall ls, lpath :: asf.path_valid(ls, lpath, tid) ==> lift_fn.requires(ls, lpath) decreases |paths| { if |paths| == 0 then [] else var ls_next := asf.path_next(ls, paths[0], tid); [lift_fn(ls, paths[0])] + GenericAtomicLiftPathSeqStateDependent(asf, ls_next, paths[1..], tid, lift_fn) } function GenericAtomicLiftTraceEntryStateDependent( asf: AtomicSpecFunctions, ls: LState, lentry: AtomicTraceEntry, lift_fn: (LState, LPath)-->HPath ) : AtomicTraceEntry requires AtomicValidStep(asf, ls, lentry) requires forall ls, lpath, tid :: asf.path_valid(ls, lpath, tid) ==> lift_fn.requires(ls, lpath) { match lentry case AtomicTraceEntry_Stutter() => AtomicTraceEntry_Stutter() case AtomicTraceEntry_Tau(tid, path) => AtomicTraceEntry_Tau(tid, lift_fn(ls, path)) case AtomicTraceEntry_Normal(tid, path) => AtomicTraceEntry_Normal(tid, lift_fn(ls, path)) case AtomicTraceEntry_Recurrent(tid, yr, rrs, rx) => var ls1 := asf.path_next(ls, yr, tid); var ls2 := AtomicGetStateAfterPaths(asf, ls1, rrs, tid); var rrs' := GenericAtomicLiftPathSeqStateDependent(asf, ls1, rrs, tid, lift_fn); AtomicTraceEntry_Recurrent(tid, lift_fn(ls, yr), rrs', lift_fn(ls2, rx)) } predicate AtomicInitImpliesInv( asf: AtomicSpecFunctions, inv: State->bool ) { forall s :: asf.init(s) ==> inv(s) } predicate AtomicInitImpliesYielding( asf: AtomicSpecFunctions ) { forall s, tid :: asf.init(s) && asf.get_thread_pc(s, tid).Some? ==> !asf.is_pc_nonyielding(asf.get_thread_pc(s, tid).v) } predicate AtomicInitImpliesOK( asf: AtomicSpecFunctions ) { forall s :: asf.init(s) ==> asf.state_ok(s) } predicate AtomicPathPreservesInv( asf: AtomicSpecFunctions, inv: State->bool ) { forall s, path, tid :: inv(s) && asf.path_valid(s, path, tid) ==> inv(asf.path_next(s, path, tid)) } predicate AtomicPathRequiresOK( asf: AtomicSpecFunctions ) { forall s, path, tid :: asf.path_valid(s, path, tid) ==> asf.state_ok(s) } predicate AtomicSteppingThreadHasPC( asf: AtomicSpecFunctions ) { forall s, path, tid :: asf.path_valid(s, path, tid) ==> asf.get_thread_pc(s, tid).Some? } predicate AtomicTauLeavesPCUnchanged( asf: AtomicSpecFunctions ) { forall s, path, tid :: asf.path_valid(s, path, tid) && asf.path_type(path).AtomicPathType_Tau? ==> var s' := asf.path_next(s, path, tid); asf.get_thread_pc(s', tid) == asf.get_thread_pc(s, tid) } predicate AtomicThreadYielding( asf: AtomicSpecFunctions, s: State, tid: Armada_ThreadHandle ) { var pc := asf.get_thread_pc(s, tid); !asf.state_ok(s) || pc.None? || !asf.is_pc_nonyielding(pc.v) } predicate AtomicThreadCantAffectOtherThreadPCExceptViaFork( asf: AtomicSpecFunctions ) { forall s, path, tid, other_tid :: asf.path_valid(s, path, tid) && tid != other_tid ==> var s' := asf.path_next(s, path, tid); var pc := asf.get_thread_pc(s, other_tid); var pc' := asf.get_thread_pc(s', other_tid); (pc' != pc ==> pc.None? && !asf.is_pc_nonyielding(pc'.v)) } predicate AtomicPathTypeMatchesPCTypes( asf: AtomicSpecFunctions, s: State, path: Path, tid: Armada_ThreadHandle ) { var s' := asf.path_next(s, path, tid); match asf.path_type(path) case AtomicPathType_Tau => true case AtomicPathType_YY => AtomicThreadYielding(asf, s, tid) && asf.state_ok(s') && AtomicThreadYielding(asf, s', tid) case AtomicPathType_YS => AtomicThreadYielding(asf, s, tid) && !asf.state_ok(s') case AtomicPathType_YR => AtomicThreadYielding(asf, s, tid) && !AtomicThreadYielding(asf, s', tid) case AtomicPathType_RY => !AtomicThreadYielding(asf, s, tid) && asf.state_ok(s') && AtomicThreadYielding(asf, s', tid) case AtomicPathType_RS => !AtomicThreadYielding(asf, s, tid) && !asf.state_ok(s') case AtomicPathType_RR => !AtomicThreadYielding(asf, s, tid) && !AtomicThreadYielding(asf, s', tid) } predicate AtomicPathTypeAlwaysMatchesPCTypes( asf: AtomicSpecFunctions ) { forall s, path, tid :: asf.path_valid(s, path, tid) ==> AtomicPathTypeMatchesPCTypes(asf, s, path, tid) } ////////////////////////////////////////////// // UTILITY LEMMAS ////////////////////////////////////////////// lemma lemma_AtomicGetStateSequenceOnePos( asf: AtomicSpecFunctions, s: State, paths: seq, tid: Armada_ThreadHandle, pos: int ) requires 0 <= pos < |paths| ensures var states := AtomicGetStateSequence(asf, s, paths, tid); states[pos+1] == asf.path_next(states[pos], paths[pos], tid) { if |paths| > 0 && pos > 0 { lemma_AtomicGetStateSequenceOnePos(asf, asf.path_next(s, paths[0], tid), paths[1..], tid, pos-1); } } lemma lemma_AtomicValidPathSequenceImpliesValidPath( asf: AtomicSpecFunctions, s: State, s': State, paths: seq, tid: Armada_ThreadHandle, states: seq, i: int ) requires AtomicValidPathSequence(asf, s, paths, tid) requires 0 <= i < |paths| requires states == AtomicGetStateSequence(asf, s, paths, tid) ensures asf.path_valid(states[i], paths[i], tid) ensures states[i+1] == asf.path_next(states[i], paths[i], tid) { if i > 0 { var s_mid := asf.path_next(s, paths[0], tid); lemma_AtomicValidPathSequenceImpliesValidPath(asf, s_mid, s', paths[1..], tid, states[1..], i-1); } } lemma lemma_AtomicNextLastElement( asf: AtomicSpecFunctions, s: State, s': State, paths: seq, tid: Armada_ThreadHandle, states: seq ) requires s' == AtomicGetStateAfterPaths(asf, s, paths, tid) requires states == AtomicGetStateSequence(asf, s, paths, tid) ensures last(states) == s' { if |paths| > 0 { var s_mid := asf.path_next(s, paths[0], tid); lemma_AtomicNextLastElement(asf, s_mid, s', paths[1..], tid, states[1..]); } } lemma lemma_ExtendAtomicGetStateAfterPaths( asf: AtomicSpecFunctions, s: State, s': State, paths: seq, tid: Armada_ThreadHandle, path: Path ) requires s' == AtomicGetStateAfterPaths(asf, s, paths, tid) ensures asf.path_next(s', path, tid) == AtomicGetStateAfterPaths(asf, s, paths + [path], tid) decreases |paths| { if |paths| > 0 { var s_next := asf.path_next(s, paths[0], tid); lemma_ExtendAtomicGetStateAfterPaths(asf, s_next, s', paths[1..], tid, path); assert paths + [path] == [paths[0]] + (paths[1..] + [path]); } } lemma lemma_ExtendAtomicValidPathSequence( asf: AtomicSpecFunctions, s: State, s': State, paths: seq, tid: Armada_ThreadHandle, path: Path ) requires AtomicValidPathSequence(asf, s, paths, tid) requires s' == AtomicGetStateAfterPaths(asf, s, paths, tid) requires asf.path_valid(s', path, tid) requires asf.path_type(path).AtomicPathType_RR? ensures AtomicValidPathSequence(asf, s, paths + [path], tid) decreases |paths| { if |paths| > 0 { var s_next := asf.path_next(s, paths[0], tid); lemma_ExtendAtomicValidPathSequence(asf, s_next, s', paths[1..], tid, path); assert paths + [path] == [paths[0]] + (paths[1..] + [path]); } } lemma lemma_AtomicBehaviorToAnnotatedBehavior( asf:AtomicSpecFunctions, b:seq ) returns ( ab:AnnotatedBehavior> ) requires BehaviorSatisfiesSpec(b, AtomicSpec(asf)) ensures AnnotatedBehaviorSatisfiesSpec(ab, AtomicAnnotatedSpec(asf)) ensures ab.states == b { var trace:seq> := []; var pos := 0; while pos < |b| - 1 invariant 0 <= pos < |b| invariant |trace| == pos invariant AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(b[..pos + 1], trace), AtomicAnnotatedSpec(asf)) { assert StatePair(b[pos], b[pos + 1]) in AtomicSpec(asf).next; var entry :| AtomicNext(asf, b[pos], b[pos + 1], entry); lemma_ExtendStateNextSeqRight(b[..pos + 1], trace, AtomicAnnotatedSpec(asf).next, b[pos + 1], entry); assert b[..pos + 1] + [b[pos + 1]] == b[..(pos + 1) + 1]; trace := trace + [entry]; pos := pos + 1; } assert b[..pos + 1] == b; ab := AnnotatedBehavior(b, trace); } lemma lemma_AtomicAnnotatedBehaviorSatisfiesAtomicSpec( asf:AtomicSpecFunctions, ab:AnnotatedBehavior> ) requires AnnotatedBehaviorSatisfiesSpec(ab, AtomicAnnotatedSpec(asf)) ensures BehaviorSatisfiesSpec(ab.states, AtomicSpec(asf)) { var b := ab.states; var spec := AtomicSpec(asf); forall i {:trigger StatePair(b[i], b[i + 1]) in spec.next} | 0 <= i < |b| - 1 ensures StatePair(b[i], b[i + 1]) in spec.next { assert ActionTuple(ab.states[i], ab.states[i + 1], ab.trace[i]) in AtomicAnnotatedSpec(asf).next; } } } ================================================ FILE: Armada/strategies/generic/GenericArmadaLemmas.i.dfy ================================================ include "GenericArmadaSpec.i.dfy" include "../refinement/GeneralRefinementLemmas.i.dfy" module GenericArmadaLemmasModule { import opened util_collections_seqs_s import opened AnnotatedBehaviorModule import opened ArmadaCommonDefinitions import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened GenericArmadaSpecModule lemma lemma_BehaviorToAnnotatedBehavior( asf:Armada_SpecFunctions, b:seq ) returns ( ab:AnnotatedBehavior> ) requires BehaviorSatisfiesSpec(b, Armada_SpecFunctionsToSpec(asf)) ensures AnnotatedBehaviorSatisfiesSpec(ab, SpecFunctionsToAnnotatedSpec(asf)) ensures ab.states == b { var spec := Armada_SpecFunctionsToSpec(asf); var aspec := SpecFunctionsToAnnotatedSpec(asf); var pos := 0; var trace := []; while pos < |b|-1 invariant pos == |trace| invariant pos < |b| invariant AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(b[..pos+1], trace), aspec) { var s := b[pos]; var s' := b[pos+1]; assert StatePair(s, s') in spec.next; var steps, tid, tau :| Armada_NextMultistep(asf, s, s', steps, tid, tau); var multistep := Armada_Multistep(steps, tid, tau); assert StatePair(b[pos], b[pos+1]) in spec.next; lemma_ExtendStateNextSeqRight(b[..pos+1], trace, aspec.next, b[pos+1], multistep); assert b[..(pos+1)+1] == b[..pos+1] + [b[pos+1]]; trace := trace + [multistep]; pos := pos + 1; } return AnnotatedBehavior(b, trace); } lemma lemma_IfAnnotatedBehaviorSatisfiesSpecThenBehaviorDoes( asf:Armada_SpecFunctions, ab:AnnotatedBehavior> ) requires AnnotatedBehaviorSatisfiesSpec(ab, SpecFunctionsToAnnotatedSpec(asf)) ensures BehaviorSatisfiesSpec(ab.states, Armada_SpecFunctionsToSpec(asf)) { var aspec := SpecFunctionsToAnnotatedSpec(asf); var spec := Armada_SpecFunctionsToSpec(asf); forall pos | 0 <= pos < |ab.states|-1 ensures StatePair(ab.states[pos], ab.states[pos+1]) in spec.next { assert ActionTuple(ab.states[pos], ab.states[pos+1], ab.trace[pos]) in aspec.next; } } lemma lemma_ExtendArmadaNextMultipleSteps( asf: Armada_SpecFunctions, s: State, s': State, s'': State, steps: seq, step: OneStep, tid: Armada_ThreadHandle ) requires Armada_NextMultipleSteps(asf, s, s', steps, tid) requires asf.step_valid(s', step, tid) requires s'' == asf.step_next(s', step, tid) ensures Armada_NextMultipleSteps(asf, s, s'', steps + [step], tid) decreases |steps| { if |steps| > 0 { var s_next := asf.step_next(s, steps[0], tid); lemma_ExtendArmadaNextMultipleSteps(asf, s_next, s', s'', steps[1..], step, tid); var all_steps := steps + [step]; assert all_steps[0] == steps[0]; assert all_steps[1..] == steps[1..] + [step]; assert Armada_NextMultipleSteps(asf, s_next, s'', all_steps[1..], tid); } } lemma lemma_ExtendArmadaStepsStartNonyielding( asf: Armada_SpecFunctions, s: State, s': State, s'': State, steps: seq, step: OneStep, tid: Armada_ThreadHandle ) requires Armada_StepsStartNonyielding(asf, s, s', steps, tid) requires Armada_NextMultipleSteps(asf, s, s', steps, tid) requires !Armada_ThreadYielding(asf, s', tid) requires !asf.is_step_tau(step) requires asf.step_valid(s', step, tid) requires s'' == asf.step_next(s', step, tid) ensures Armada_StepsStartNonyielding(asf, s, s'', steps + [step], tid) ensures Armada_NextMultipleSteps(asf, s, s'', steps + [step], tid) decreases |steps| { if |steps| > 0 { var s_next := asf.step_next(s, steps[0], tid); lemma_ExtendArmadaStepsStartNonyielding(asf, s_next, s', s'', steps[1..], step, tid); var all_steps := steps + [step]; assert all_steps[0] == steps[0]; assert all_steps[1..] == steps[1..] + [step]; assert Armada_StepsStartNonyielding(asf, s_next, s'', all_steps[1..], tid); assert Armada_StepsStartNonyielding(asf, s, s'', steps + [step], tid); assert Armada_NextMultipleSteps(asf, s, s'', steps + [step], tid); } else { assert Armada_StepsStartNonyielding(asf, s, s'', steps + [step], tid); assert Armada_NextMultipleSteps(asf, s, s'', steps + [step], tid); } } lemma lemma_CombineArmadaNextMultipleSteps( asf: Armada_SpecFunctions, s: State, s': State, s'': State, steps1: seq, steps2: seq, tid: Armada_ThreadHandle ) requires Armada_NextMultipleSteps(asf, s, s', steps1, tid) requires Armada_NextMultipleSteps(asf, s', s'', steps2, tid) ensures Armada_NextMultipleSteps(asf, s, s'', steps1 + steps2, tid) decreases |steps2| { if |steps2| == 0 { assert s'' == s'; assert steps1 + steps2 == steps1; return; } var s_next := asf.step_next(s', steps2[0], tid); lemma_ExtendArmadaNextMultipleSteps(asf, s, s', s_next, steps1, steps2[0], tid); lemma_CombineArmadaNextMultipleSteps(asf, s, s_next, s'', steps1 + [steps2[0]], steps2[1..], tid); calc { steps1 + steps2; steps1 + ([steps2[0]] + steps2[1..]); (steps1 + [steps2[0]]) + steps2[1..]; } } lemma lemma_CombineArmadaStepsStartNonyielding( asf: Armada_SpecFunctions, s: State, s': State, s'': State, steps1: seq, steps2: seq, tid: Armada_ThreadHandle ) requires Armada_StepsStartNonyielding(asf, s, s', steps1, tid) requires Armada_NextMultipleSteps(asf, s, s', steps1, tid) requires Armada_StepsStartNonyielding(asf, s', s'', steps2, tid) requires Armada_NextMultipleSteps(asf, s', s'', steps2, tid) ensures Armada_StepsStartNonyielding(asf, s, s'', steps1 + steps2, tid) ensures Armada_NextMultipleSteps(asf, s, s'', steps1 + steps2, tid) decreases |steps2| { if |steps2| == 0 { assert s'' == s'; assert steps1 + steps2 == steps1; return; } var s_next := asf.step_next(s', steps2[0], tid); lemma_ExtendArmadaStepsStartNonyielding(asf, s, s', s_next, steps1, steps2[0], tid); lemma_CombineArmadaStepsStartNonyielding(asf, s, s_next, s'', steps1 + [steps2[0]], steps2[1..], tid); calc { steps1 + steps2; steps1 + ([steps2[0]] + steps2[1..]); (steps1 + [steps2[0]]) + steps2[1..]; } } } ================================================ FILE: Armada/strategies/generic/GenericArmadaPlus.i.dfy ================================================ include "GenericArmadaSpec.i.dfy" include "GenericArmadaLemmas.i.dfy" include "../refinement/GeneralRefinementLemmas.i.dfy" module GenericArmadaPlusModule { import opened util_collections_seqs_s import opened AnnotatedBehaviorModule import opened ArmadaCommonDefinitions import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened GenericArmadaLemmasModule predicate InitsMatch( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState ) { forall ls :: lasf.init(ls) ==> exists hs :: hasf.init(hs) && ls == convert(hs) } predicate NextsMatch( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState ) { forall ls, hs, step, tid :: lasf.step_valid(ls, step, tid) && ls == convert(hs) ==> hasf.step_valid(hs, step, tid) && lasf.step_next(ls, step, tid) == convert(hasf.step_next(hs, step, tid)) } predicate TausMatch( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState ) { forall step :: lasf.is_step_tau(step) <==> hasf.is_step_tau(step) } predicate ThreadPCsMatch( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState ) { forall ls, hs, tid :: ls == convert(hs) ==> lasf.get_thread_pc(ls, tid) == hasf.get_thread_pc(hs, tid) } predicate NonyieldingPCsMatch( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState ) { forall pc :: lasf.is_pc_nonyielding(pc) <==> hasf.is_pc_nonyielding(pc) } predicate StateOKsMatch( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState ) { forall ls, hs :: ls == convert(hs) ==> lasf.state_ok(ls) == hasf.state_ok(hs) } predicate RequirementsForSpecRefinesPlusSpec( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState ) { && InitsMatch(lasf, hasf, convert) && NextsMatch(lasf, hasf, convert) && TausMatch(lasf, hasf, convert) && ThreadPCsMatch(lasf, hasf, convert) && NonyieldingPCsMatch(lasf, hasf, convert) && StateOKsMatch(lasf, hasf, convert) } lemma lemma_LiftStepsStartNonyielding( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState, ls: LState, ls': LState, hs: HState, steps: seq, tid: Armada_ThreadHandle ) returns ( hs': HState ) requires RequirementsForSpecRefinesPlusSpec(lasf, hasf, convert) requires Armada_NextMultipleSteps(lasf, ls, ls', steps, tid) requires Armada_StepsStartNonyielding(lasf, ls, ls', steps, tid) requires ls == convert(hs) ensures Armada_NextMultipleSteps(hasf, hs, hs', steps, tid) ensures Armada_StepsStartNonyielding(hasf, hs, hs', steps, tid) ensures ls' == convert(hs') { if |steps| == 0 { hs' := hs; return; } var step := steps[0]; var ls_next := lasf.step_next(ls, step, tid); var hs_next := hasf.step_next(hs, step, tid); hs' := lemma_LiftStepsStartNonyielding(lasf, hasf, convert, ls_next, ls', hs_next, steps[1..], tid); } lemma lemma_LiftMultistep( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState, ls: LState, ls': LState, hs: HState, steps: seq, tid: Armada_ThreadHandle, tau: bool ) returns ( hs': HState ) requires RequirementsForSpecRefinesPlusSpec(lasf, hasf, convert) requires Armada_NextMultistep(lasf, ls, ls', steps, tid, tau) requires ls == convert(hs) ensures Armada_NextMultistep(hasf, hs, hs', steps, tid, tau) ensures ls' == convert(hs') { if |steps| == 0 { hs' := hs; return; } var step := steps[0]; var ls_next := lasf.step_next(ls, step, tid); var hs_next := hasf.step_next(hs, step, tid); if tau { hs' := hs_next; assert Armada_NextMultipleSteps(lasf, ls_next, ls', steps[1..], tid); } else { hs' := lemma_LiftStepsStartNonyielding(lasf, hasf, convert, ls_next, ls', hs_next, steps[1..], tid); } } lemma lemma_LiftBehavior( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState, refinement_relation: RefinementRelation, lb: seq ) returns ( hb: seq ) requires RequirementsForSpecRefinesPlusSpec(lasf, hasf, convert) requires BehaviorSatisfiesSpec(lb, Armada_SpecFunctionsToSpec(lasf)) requires refinement_relation == iset ls, hs | ls == convert(hs) :: RefinementPair(ls, hs) ensures BehaviorRefinesBehavior(lb, hb, refinement_relation) ensures BehaviorSatisfiesSpec(hb, Armada_SpecFunctionsToSpec(hasf)) { assert lasf.init(lb[0]); var hs0 :| hasf.init(hs0) && lb[0] == convert(hs0); hb := [hs0]; var pos := 0; var lspec := Armada_SpecFunctionsToSpec(lasf); var hspec := Armada_SpecFunctionsToSpec(hasf); assert BehaviorRefinesBehaviorUsingRefinementMap(lb[..pos+1], hb, refinement_relation, [RefinementRange(0, 0)]); assert BehaviorRefinesBehavior(lb[..pos+1], hb, refinement_relation); while pos + 1 < |lb| invariant |hb| == pos + 1 invariant pos < |lb| invariant BehaviorRefinesBehavior(lb[..pos+1], hb, refinement_relation) invariant BehaviorSatisfiesSpec(hb, hspec) { var ls := lb[pos]; var ls' := lb[pos+1]; var hs := hb[pos]; assert RefinementPair(ls, hs) in refinement_relation; assert StatePair(ls, ls') in lspec.next; var steps, tid, tau :| Armada_NextMultistep(lasf, ls, ls', steps, tid, tau); var hs' := lemma_LiftMultistep(lasf, hasf, convert, ls, ls', hs, steps, tid, tau); assert Armada_NextMultistep(hasf, hs, hs', steps, tid, tau); assert StatePair(hs, hs') in hspec.next; lemma_ExtendBehaviorRefinesBehaviorRightOne_LH(lb[..pos+1], hb, refinement_relation, ls', hs'); lemma_ExtendBehaviorSatisfiesSpecRightOne(hb, hspec, hs'); assert lb[..(pos+1)+1] == lb[..pos+1] + [ls']; pos := pos + 1; hb := hb + [hs']; } assert lb[..pos+1] == lb; } lemma lemma_SpecRefinesPlusSpec( lasf: Armada_SpecFunctions, hasf: Armada_SpecFunctions, convert: HState->LState ) returns ( refinement_relation: RefinementRelation ) requires RequirementsForSpecRefinesPlusSpec(lasf, hasf, convert) ensures SpecRefinesSpec(Armada_SpecFunctionsToSpec(lasf), Armada_SpecFunctionsToSpec(hasf), refinement_relation) ensures refinement_relation == iset ls, hs | ls == convert(hs) :: RefinementPair(ls, hs) { refinement_relation := iset ls, hs | ls == convert(hs) :: RefinementPair(ls, hs); var hspec := Armada_SpecFunctionsToSpec(hasf); forall lb | BehaviorSatisfiesSpec(lb, Armada_SpecFunctionsToSpec(lasf)) ensures exists hb :: BehaviorRefinesBehavior(lb, hb, refinement_relation) && BehaviorSatisfiesSpec(hb, hspec) { var hb := lemma_LiftBehavior(lasf, hasf, convert, refinement_relation, lb); assert BehaviorRefinesBehavior(lb, hb, refinement_relation) && BehaviorSatisfiesSpec(hb, hspec); } } } ================================================ FILE: Armada/strategies/generic/GenericArmadaSpec.i.dfy ================================================ include "../../util/option.s.dfy" include "../refinement/AnnotatedBehavior.i.dfy" include "../../ArmadaCommonDefinitions.dfy" module GenericArmadaSpecModule { import opened util_option_s import opened AnnotatedBehaviorModule import opened ArmadaCommonDefinitions ///////////////////////////////////////////// // Constructing an AnnotatedBehaviorSpec ///////////////////////////////////////////// datatype Armada_Multistep = Armada_Multistep(steps:seq, tid:Armada_ThreadHandle, tau:bool) function SpecFunctionsToAnnotatedSpec( asf: Armada_SpecFunctions ) : AnnotatedBehaviorSpec> { AnnotatedBehaviorSpec( iset s | asf.init(s) :: s, iset s, s', entry:Armada_Multistep | Armada_NextMultistep(asf, s, s', entry.steps, entry.tid, entry.tau) :: ActionTuple(s, s', entry) ) } predicate Armada_Next( asf: Armada_SpecFunctions, s: State, s': State, multistep: Armada_Multistep ) { Armada_NextMultistep(asf, s, s', multistep.steps, multistep.tid, multistep.tau) } ///////////////////////////////////////////// // Predicates about an Armada spec ///////////////////////////////////////////// predicate InitImpliesInv( asf:Armada_SpecFunctions, inv:State->bool ) { forall s :: asf.init(s) ==> inv(s) } predicate InitImpliesYielding( asf:Armada_SpecFunctions ) { forall s, tid :: asf.init(s) && asf.get_thread_pc(s, tid).Some? ==> !asf.is_pc_nonyielding(asf.get_thread_pc(s, tid).v) } predicate InitImpliesOK( asf:Armada_SpecFunctions ) { forall s :: asf.init(s) ==> asf.state_ok(s) } predicate OneStepPreservesInv( asf:Armada_SpecFunctions, inv:State->bool ) { forall s, step, tid :: inv(s) && asf.step_valid(s, step, tid) ==> inv(asf.step_next(s, step, tid)) } predicate OneStepRequiresOK( asf:Armada_SpecFunctions ) { forall s, step, tid :: asf.step_valid(s, step, tid) ==> asf.state_ok(s) } predicate SteppingThreadHasPC( asf:Armada_SpecFunctions ) { forall s, step, tid :: asf.step_valid(s, step, tid) ==> asf.get_thread_pc(s, tid).Some? } predicate TauLeavesPCUnchanged( asf:Armada_SpecFunctions ) { forall s, step, tid :: asf.step_valid(s, step, tid) && asf.is_step_tau(step) ==> var s' := asf.step_next(s, step, tid); asf.get_thread_pc(s', tid) == asf.get_thread_pc(s, tid) } predicate ThreadCantAffectOtherThreadPCExceptViaFork( asf:Armada_SpecFunctions ) { forall s, step, tid, other_tid :: asf.step_valid(s, step, tid) && tid != other_tid ==> var s' := asf.step_next(s, step, tid); var pc := asf.get_thread_pc(s, other_tid); var pc' := asf.get_thread_pc(s', other_tid); (pc' != pc ==> pc.None? && !asf.is_pc_nonyielding(pc'.v)) } //////////////////////////////////////////////////////////////////////// // Extracting a sequence of states from a sequence of steps //////////////////////////////////////////////////////////////////////// function Armada_GetStateSequence( asf: Armada_SpecFunctions, s: State, steps: seq, tid: Armada_ThreadHandle ) : (states: seq) ensures |states| == |steps| + 1 ensures states[0] == s decreases |steps| { if |steps| == 0 then [s] else [s] + Armada_GetStateSequence(asf, asf.step_next(s, steps[0], tid), steps[1..], tid) } } ================================================ FILE: Armada/strategies/generic/LiftAtomicToAtomic.i.dfy ================================================ include "GenericArmadaAtomic.i.dfy" module LiftAtomicToAtomicModule { import opened util_collections_seqs_s import opened util_collections_seqs_i import opened util_option_s import opened GenericArmadaSpecModule import opened AnnotatedBehaviorModule import opened ArmadaCommonDefinitions import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened InvariantsModule import opened GenericArmadaLemmasModule import opened GenericArmadaAtomicModule ////////////////////////////////////////////// // LIFTING BETWEEN TWO ATOMIC SPECS ////////////////////////////////////////////// predicate LiftAtomicPathSuccessful( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, ls: LState, lpath: LPath, tid: Armada_ThreadHandle, hs: HState, hpath: HPath ) { var ls' := lasf.path_next(ls, lpath, tid); var hs' := hasf.path_next(hs, hpath, tid); && inv(ls') && hasf.path_valid(hs, hpath, tid) && relation(ls', hs') && hasf.path_type(hpath) == lasf.path_type(lpath) && lasf.state_ok(ls') == hasf.state_ok(hs') } predicate LiftAtomicTraceEntrySuccessful( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, ls: LState, lentry: AtomicTraceEntry, hs: HState, hentry: AtomicTraceEntry ) { var ls' := AtomicGetNextStateAlways(lasf, ls, lentry); && inv(ls') && AtomicValidStep(hasf, hs, hentry) && relation(ls', AtomicGetNextStateAlways(hasf, hs, hentry)) } predicate AtomicPathSkippable( lasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, ls: LState, lpath: LPath, tid: Armada_ThreadHandle, hs: HState ) { var ls' := lasf.path_next(ls, lpath, tid); var ty := lasf.path_type(lpath); && inv(ls') && relation(ls', hs) && lasf.state_ok(ls') && (ty.AtomicPathType_Tau? || ty.AtomicPathType_YY? || ty.AtomicPathType_YS? || ty.AtomicPathType_RR?) } predicate ProgressMade(value: (int, int), value': (int, int)) { var (x, y) := value; var (x', y') := value'; && 0 <= x && 0 <= x' && 0 <= y && 0 <= y' && (x' < x || (x' == x && y' < y)) } predicate AtomicPathIntroduced( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, relation: (LState, HState)->bool, progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), ls: LState, lpath: LPath, tid: Armada_ThreadHandle, hs: HState, hpath: HPath ) { var lty := lasf.path_type(lpath); var hty := hasf.path_type(hpath); var hs' := hasf.path_next(hs, hpath, tid); && hasf.path_valid(hs, hpath, tid) && relation(ls, hs') && hasf.state_ok(hs') && ProgressMade(progress_measure(hs, lpath, tid), progress_measure(hs', lpath, tid)) && match lty case AtomicPathType_Tau => hty.AtomicPathType_Tau? case AtomicPathType_YY => hty.AtomicPathType_YY? || hty.AtomicPathType_Tau? case AtomicPathType_YS => hty.AtomicPathType_YY? || hty.AtomicPathType_Tau? case AtomicPathType_YR => hty.AtomicPathType_YY? || hty.AtomicPathType_Tau? case AtomicPathType_RY => hty.AtomicPathType_RR? case AtomicPathType_RS => hty.AtomicPathType_RR? case AtomicPathType_RR => hty.AtomicPathType_RR? } function GetTraceEntryProgressMeasure( progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), hs: HState, entry: AtomicTraceEntry ) : (int, int) { match entry case AtomicTraceEntry_Stutter() => (0, 0) case AtomicTraceEntry_Tau(tid, path) => progress_measure(hs, path, tid) case AtomicTraceEntry_Normal(tid, path) => progress_measure(hs, path, tid) case AtomicTraceEntry_Recurrent(tid, yr, _, _) => progress_measure(hs, yr, tid) } predicate IntroduceAtomicTraceEntrySuccessful( hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), ls: LState, lentry: AtomicTraceEntry, hs: HState, hentry: AtomicTraceEntry ) { var hs' := AtomicGetNextStateAlways(hasf, hs, hentry); && AtomicValidStep(hasf, hs, hentry) && relation(ls, hs') && ProgressMade(GetTraceEntryProgressMeasure(progress_measure, hs, lentry), GetTraceEntryProgressMeasure(progress_measure, hs', lentry)) } lemma lemma_LiftPathSequenceGivenAtomicPathsLiftable( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), ls: LState, tid: Armada_ThreadHandle, lpaths: seq, hs: HState ) returns ( hpaths: seq ) requires forall ls0, lpath0, hs0 :: && inv(ls0) && relation(ls0, hs0) && lasf.path_valid(ls0, lpath0, tid) && !AtomicPathSkippable(lasf, inv, relation, ls0, lpath0, tid, hs0) ==> exists hpath :: AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls0, lpath0, tid, hs0, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls0, lpath0, tid, hs0, hpath) requires inv(ls) requires relation(ls, hs) requires AtomicValidPathSequence(lasf, ls, lpaths, tid) ensures inv(AtomicGetStateAfterPaths(lasf, ls, lpaths, tid)) ensures AtomicValidPathSequence(hasf, hs, hpaths, tid) ensures relation(AtomicGetStateAfterPaths(lasf, ls, lpaths, tid), AtomicGetStateAfterPaths(hasf, hs, hpaths, tid)) decreases |lpaths|, if |lpaths| > 0 then progress_measure(hs, lpaths[0], tid).0 else 0, if |lpaths| > 0 then progress_measure(hs, lpaths[0], tid).1 else 0 { if |lpaths| == 0 { hpaths := []; return; } var ls_next := lasf.path_next(ls, lpaths[0], tid); if AtomicPathSkippable(lasf, inv, relation, ls, lpaths[0], tid, hs) { hpaths := lemma_LiftPathSequenceGivenAtomicPathsLiftable(lasf, hasf, inv, relation, progress_measure, ls_next, tid, lpaths[1..], hs); return; } var hpath0 :| AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpaths[0], tid, hs, hpath0) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpaths[0], tid, hs, hpath0); var lty := lasf.path_type(lpaths[0]); var hty := hasf.path_type(hpath0); var hs_next := hasf.path_next(hs, hpath0, tid); if AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpaths[0], tid, hs, hpath0) { var hpaths_next := lemma_LiftPathSequenceGivenAtomicPathsLiftable(lasf, hasf, inv, relation, progress_measure, ls, tid, lpaths, hs_next); hpaths := [hpath0] + hpaths_next; assert hpaths[0] == hpath0; assert hpaths[1..] == hpaths_next; return; } var hpaths_next := lemma_LiftPathSequenceGivenAtomicPathsLiftable(lasf, hasf, inv, relation, progress_measure, ls_next, tid, lpaths[1..], hs_next); hpaths := [hpath0] + hpaths_next; assert hpaths[0] == hpath0; assert hpaths[1..] == hpaths_next; } lemma lemma_LiftRecurrentAtomicTraceEntryGivenAtomicPathsLiftable( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), ls: LState, tid: Armada_ThreadHandle, lyr: LPath, lrrs: seq, lrx: LPath, hs: HState, hyr: HPath ) returns ( hrrs: seq, hrx: HPath ) requires forall ls0, lpath0, hs0 :: && inv(ls0) && relation(ls0, hs0) && lasf.path_valid(ls0, lpath0, tid) && !AtomicPathSkippable(lasf, inv, relation, ls0, lpath0, tid, hs0) ==> exists hpath :: AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls0, lpath0, tid, hs0, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls0, lpath0, tid, hs0, hpath) requires inv(ls) requires relation(ls, hs) requires AtomicValidRecursiveStep(lasf, ls, tid, lyr, lrrs, lrx) requires LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lyr, tid, hs, hyr) ensures AtomicValidRecursiveStep(hasf, hs, tid, hyr, hrrs, hrx) ensures var ls1 := lasf.path_next(ls, lyr, tid); var ls2 := AtomicGetStateAfterPaths(lasf, ls1, lrrs, tid); var ls3 := lasf.path_next(ls2, lrx, tid); var hs1 := hasf.path_next(hs, hyr, tid); var hs2 := AtomicGetStateAfterPaths(hasf, hs1, hrrs, tid); var hs3 := hasf.path_next(hs2, hrx, tid); inv(ls3) && relation(ls3, hs3) { var ls1 := lasf.path_next(ls, lyr, tid); var ls2 := AtomicGetStateAfterPaths(lasf, ls1, lrrs, tid); var ls3 := lasf.path_next(ls2, lrx, tid); var hs1 := hasf.path_next(hs, hyr, tid); hrrs := lemma_LiftPathSequenceGivenAtomicPathsLiftable(lasf, hasf, inv, relation, progress_measure, ls1, tid, lrrs, hs1); var hs2 := AtomicGetStateAfterPaths(hasf, hs1, hrrs, tid); hrx :| AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls2, lrx, tid, hs2, hrx) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls2, lrx, tid, hs2, hrx); while AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls2, lrx, tid, hs2, hrx) invariant AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls2, lrx, tid, hs2, hrx) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls2, lrx, tid, hs2, hrx); invariant AtomicValidPathSequence(hasf, hs1, hrrs, tid) invariant hs2 == AtomicGetStateAfterPaths(hasf, hs1, hrrs, tid) invariant relation(ls2, hs2) decreases progress_measure(hs2, lrx, tid).0, progress_measure(hs2, lrx, tid).1 { assert AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls2, lrx, tid, hs2, hrx); if lasf.path_type(lrx).AtomicPathType_RY? { assert hasf.path_type(hrx).AtomicPathType_RR?; } else { assert lasf.path_type(lrx).AtomicPathType_RS?; assert hasf.path_type(hrx).AtomicPathType_RR?; } lemma_ExtendAtomicGetStateAfterPaths(hasf, hs1, hs2, hrrs, tid, hrx); lemma_ExtendAtomicValidPathSequence(hasf, hs1, hs2, hrrs, tid, hrx); hrrs := hrrs + [hrx]; hs2 := hasf.path_next(hs2, hrx, tid); hrx :| AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls2, lrx, tid, hs2, hrx) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls2, lrx, tid, hs2, hrx); } var hs3 := hasf.path_next(hs2, hrx, tid); } lemma lemma_LiftAtomicTraceEntryGivenAtomicPathsLiftable( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), ls: LState, lentry: AtomicTraceEntry, hs: HState ) returns ( hentry: AtomicTraceEntry ) requires forall ls0, lpath0, hs0, tid :: && inv(ls0) && relation(ls0, hs0) && lasf.path_valid(ls0, lpath0, tid) && !AtomicPathSkippable(lasf, inv, relation, ls0, lpath0, tid, hs0) ==> exists hpath :: AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls0, lpath0, tid, hs0, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls0, lpath0, tid, hs0, hpath) requires inv(ls) requires relation(ls, hs) requires AtomicValidStep(lasf, ls, lentry) ensures AtomicValidStep(hasf, hs, hentry) ensures || IntroduceAtomicTraceEntrySuccessful(hasf, inv, relation, progress_measure, ls, lentry, hs, hentry) || LiftAtomicTraceEntrySuccessful(lasf, hasf, inv, relation, ls, lentry, hs, hentry) { match lentry { case AtomicTraceEntry_Stutter() => hentry := AtomicTraceEntry_Stutter(); case AtomicTraceEntry_Tau(tid, lpath) => if AtomicPathSkippable(lasf, inv, relation, ls, lpath, tid, hs) { hentry := AtomicTraceEntry_Stutter(); } else { var hpath :| AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpath, tid, hs, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath); hentry := AtomicTraceEntry_Tau(tid, hpath); } case AtomicTraceEntry_Normal(tid, lpath) => if AtomicPathSkippable(lasf, inv, relation, ls, lpath, tid, hs) { hentry := AtomicTraceEntry_Stutter(); } else { var hpath :| AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpath, tid, hs, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath); if AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpath, tid, hs, hpath) { if hasf.path_type(hpath).AtomicPathType_Tau? { hentry := AtomicTraceEntry_Tau(tid, hpath); } else { hentry := AtomicTraceEntry_Normal(tid, hpath); } } else { hentry := AtomicTraceEntry_Normal(tid, hpath); } } case AtomicTraceEntry_Recurrent(tid, lyr, lrrs, lrx) => var hpath :| AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lyr, tid, hs, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lyr, tid, hs, hpath); if AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lyr, tid, hs, hpath) { if hasf.path_type(hpath).AtomicPathType_Tau? { hentry := AtomicTraceEntry_Tau(tid, hpath); } else { hentry := AtomicTraceEntry_Normal(tid, hpath); } } else { var hrrs, hrx := lemma_LiftRecurrentAtomicTraceEntryGivenAtomicPathsLiftable(lasf, hasf, inv, relation, progress_measure, ls, tid, lyr, lrrs, lrx, hs, hpath); hentry := AtomicTraceEntry_Recurrent(tid, hpath, hrrs, hrx); } } } lemma lemma_LiftAtomicBehaviorGivenAtomicPathsLiftableGeneral( lb: AnnotatedBehavior>, lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), refinement_relation: RefinementRelation ) returns ( hb: AnnotatedBehavior> ) requires forall ls, lpath, hs, tid :: && inv(ls) && relation(ls, hs) && lasf.path_valid(ls, lpath, tid) && !AtomicPathSkippable(lasf, inv, relation, ls, lpath, tid, hs) ==> exists hpath :: AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpath, tid, hs, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath) requires AtomicInitImpliesInv(lasf, inv) requires forall ls :: lasf.init(ls) ==> exists hs :: hasf.init(hs) && relation(ls, hs) requires forall ls, hs :: inv(ls) && relation(ls, hs) ==> RefinementPair(ls, hs) in refinement_relation requires AnnotatedBehaviorSatisfiesSpec(lb, AtomicAnnotatedSpec(lasf)) ensures AnnotatedBehaviorSatisfiesSpec(hb, AtomicAnnotatedSpec(hasf)) ensures BehaviorRefinesBehavior(lb.states, hb.states, refinement_relation) { var hs0 :| hasf.init(hs0) && relation(lb.states[0], hs0); hb := AnnotatedBehavior([hs0], []); var pos := 0; var lh_map := [RefinementRange(0, 0)]; assert BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos+1], hb.states, refinement_relation, lh_map); while pos < |lb.trace| invariant 0 <= pos <= |lb.trace| invariant |hb.states| > 0 invariant BehaviorRefinesBehavior(lb.states[..pos+1], hb.states, refinement_relation) invariant AnnotatedBehaviorSatisfiesSpec(hb, AtomicAnnotatedSpec(hasf)) invariant relation(lb.states[pos], last(hb.states)) invariant inv(lb.states[pos]) decreases |lb.states| - pos, if pos < |lb.trace| then GetTraceEntryProgressMeasure(progress_measure, last(hb.states), lb.trace[pos]).0 else 0, if pos < |lb.trace| then GetTraceEntryProgressMeasure(progress_measure, last(hb.states), lb.trace[pos]).1 else 0 { var ls := lb.states[pos]; var ls' := lb.states[pos+1]; var lentry := lb.trace[pos]; var hs := last(hb.states); assert ActionTuple(ls, ls', lentry) in AtomicAnnotatedSpec(lasf).next; var hentry := lemma_LiftAtomicTraceEntryGivenAtomicPathsLiftable(lasf, hasf, inv, relation, progress_measure, ls, lentry, hs); var hs' := AtomicGetNextStateAlways(hasf, hs, hentry); if LiftAtomicTraceEntrySuccessful(lasf, hasf, inv, relation, ls, lentry, hs, hentry) { lemma_ExtendBehaviorRefinesBehaviorRightOne_LH(lb.states[..pos+1], hb.states, refinement_relation, ls', hs'); assert lb.states[..pos+1] + [ls'] == lb.states[..pos+1+1]; pos := pos + 1; } else { lemma_ExtendBehaviorRefinesBehaviorRightOne_LStutter(lb.states[..pos+1], hb.states, refinement_relation, hs'); } hb := AnnotatedBehavior(hb.states + [hs'], hb.trace + [hentry]); } assert lb.states[..pos+1] == lb.states; assert BehaviorRefinesBehavior(lb.states, hb.states, refinement_relation); } lemma lemma_AtomicBehaviorToAnnotatedBehavior( asf: AtomicSpecFunctions, b: seq ) returns ( ab: AnnotatedBehavior> ) requires BehaviorSatisfiesSpec(b, AtomicSpec(asf)) ensures AnnotatedBehaviorSatisfiesSpec(ab, AtomicAnnotatedSpec(asf)) ensures ab.states == b { var pos := 0; var trace := []; while pos < |b| - 1 invariant 0 <= pos <= |b| - 1 invariant |trace| == pos invariant AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(b[..pos + 1], trace), AtomicAnnotatedSpec(asf)) { assert StatePair(b[pos], b[pos+1]) in AtomicSpec(asf).next; var entry :| AtomicNext(asf, b[pos], b[pos + 1], entry); lemma_ExtendStateNextSeqRight(b[..pos + 1], trace, AtomicAnnotatedSpec(asf).next, b[pos + 1], entry); assert b[..pos + 1] + [b[pos + 1]] == b[..(pos + 1) + 1]; trace := trace + [entry]; pos := pos + 1; } assert b[..pos + 1] == b; ab := AnnotatedBehavior(b, trace); } lemma lemma_AtomicAnnotatedBehaviorSatisfiesAtomicSpec( asf: AtomicSpecFunctions, ab: AnnotatedBehavior> ) requires AnnotatedBehaviorSatisfiesSpec(ab, AtomicAnnotatedSpec(asf)) ensures BehaviorSatisfiesSpec(ab.states, AtomicSpec(asf)) { forall pos | 0 <= pos < |ab.trace| ensures StatePair(ab.states[pos], ab.states[pos + 1]) in AtomicSpec(asf).next { assert ActionTuple(ab.states[pos], ab.states[pos + 1], ab.trace[pos]) in AtomicAnnotatedSpec(asf).next; } } lemma lemma_LiftAtomicToAtomicGivenAtomicPathsLiftableGeneral( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), refinement_relation: RefinementRelation ) requires forall ls, lpath, hs, tid :: && inv(ls) && relation(ls, hs) && lasf.path_valid(ls, lpath, tid) && !AtomicPathSkippable(lasf, inv, relation, ls, lpath, tid, hs) ==> exists hpath :: AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpath, tid, hs, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath) requires AtomicInitImpliesInv(lasf, inv) requires forall ls :: lasf.init(ls) ==> exists hs :: hasf.init(hs) && relation(ls, hs) requires forall ls, hs :: inv(ls) && relation(ls, hs) ==> RefinementPair(ls, hs) in refinement_relation ensures SpecRefinesSpec(AtomicSpec(lasf), AtomicSpec(hasf), refinement_relation) { forall lb | BehaviorSatisfiesSpec(lb, AtomicSpec(lasf)) ensures exists hb :: BehaviorRefinesBehavior(lb, hb, refinement_relation) && BehaviorSatisfiesSpec(hb, AtomicSpec(hasf)) { var alb := lemma_AtomicBehaviorToAnnotatedBehavior(lasf, lb); var ahb := lemma_LiftAtomicBehaviorGivenAtomicPathsLiftableGeneral(alb, lasf, hasf, inv, relation, progress_measure, refinement_relation); lemma_AtomicAnnotatedBehaviorSatisfiesAtomicSpec(hasf, ahb); var hb := ahb.states; assert BehaviorRefinesBehavior(lb, hb, refinement_relation) && BehaviorSatisfiesSpec(hb, AtomicSpec(hasf)); } } lemma lemma_LiftAtomicToAtomicGivenAtomicPathsLiftable( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, refinement_relation: RefinementRelation ) requires forall ls, lpath, hs, tid :: && inv(ls) && relation(ls, hs) && lasf.path_valid(ls, lpath, tid) ==> exists hpath :: LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath) requires AtomicInitImpliesInv(lasf, inv) requires forall ls, hs :: relation(ls, hs) ==> lasf.state_ok(ls) == hasf.state_ok(hs) requires forall ls :: lasf.init(ls) ==> exists hs :: hasf.init(hs) && relation(ls, hs) requires forall ls, hs :: inv(ls) && relation(ls, hs) ==> RefinementPair(ls, hs) in refinement_relation ensures SpecRefinesSpec(AtomicSpec(lasf), AtomicSpec(hasf), refinement_relation) { var progress_measure:(HState, LPath, Armada_ThreadHandle)->(int, int) := (hs: HState, ls: LPath, tid: Armada_ThreadHandle) => (0, 0); lemma_LiftAtomicToAtomicGivenAtomicPathsLiftableGeneral(lasf, hasf, inv, relation, progress_measure, refinement_relation); } lemma lemma_LiftAtomicToAtomicGivenAtomicPathsSkippablyLiftable( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, refinement_relation: RefinementRelation ) requires forall ls, lpath, hs, tid :: && inv(ls) && relation(ls, hs) && lasf.path_valid(ls, lpath, tid) && !AtomicPathSkippable(lasf, inv, relation, ls, lpath, tid, hs) ==> exists hpath :: LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath) requires AtomicInitImpliesInv(lasf, inv) requires forall ls :: lasf.init(ls) ==> exists hs :: hasf.init(hs) && relation(ls, hs) requires forall ls, hs :: inv(ls) && relation(ls, hs) ==> RefinementPair(ls, hs) in refinement_relation ensures SpecRefinesSpec(AtomicSpec(lasf), AtomicSpec(hasf), refinement_relation) { var progress_measure:(HState, LPath, Armada_ThreadHandle)->(int, int) := (hs: HState, ls: LPath, tid: Armada_ThreadHandle) => (0, 0); lemma_LiftAtomicToAtomicGivenAtomicPathsLiftableGeneral(lasf, hasf, inv, relation, progress_measure, refinement_relation); } lemma lemma_LiftAtomicToAtomicGivenAtomicPathsIntroduciblyLiftable( lasf: AtomicSpecFunctions, hasf: AtomicSpecFunctions, inv: LState->bool, relation: (LState, HState)->bool, progress_measure: (HState, LPath, Armada_ThreadHandle)->(int, int), refinement_relation: RefinementRelation ) requires forall ls, lpath, hs, tid :: && inv(ls) && relation(ls, hs) && lasf.path_valid(ls, lpath, tid) ==> exists hpath :: AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpath, tid, hs, hpath) || LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath) requires AtomicInitImpliesInv(lasf, inv) requires forall ls :: lasf.init(ls) ==> exists hs :: hasf.init(hs) && relation(ls, hs) requires forall ls, hs :: inv(ls) && relation(ls, hs) ==> RefinementPair(ls, hs) in refinement_relation ensures SpecRefinesSpec(AtomicSpec(lasf), AtomicSpec(hasf), refinement_relation) { lemma_LiftAtomicToAtomicGivenAtomicPathsLiftableGeneral(lasf, hasf, inv, relation, progress_measure, refinement_relation); } } ================================================ FILE: Armada/strategies/generic/LiftFromAtomic.i.dfy ================================================ include "GenericArmadaAtomic.i.dfy" module LiftFromAtomicModule { import opened util_collections_seqs_s import opened util_collections_seqs_i import opened util_option_s import opened GenericArmadaSpecModule import opened AnnotatedBehaviorModule import opened ArmadaCommonDefinitions import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened InvariantsModule import opened GenericArmadaLemmasModule import opened GenericArmadaAtomicModule ////////////////////////////////////////////// // LIFTING FROM ATOMIC ////////////////////////////////////////////// predicate AtomicLiftPathFromAtomicPossible( asf: AtomicSpecFunctions, s: State, s': State, path: Path, tid: Armada_ThreadHandle ) { && asf.path_valid(s, path, tid) && s' == asf.path_next(s, path, tid) } predicate AtomicStepsLiftPathFromAtomic( asf: Armada_SpecFunctions, s: State, s': State, steps: seq, tid: Armada_ThreadHandle, startsYielding: bool, mustEndYielding: bool, ok: bool ) { && |steps| > 0 && !asf.is_step_tau(steps[0]) && (startsYielding == Armada_ThreadYielding(asf, s, tid)) && (mustEndYielding ==> Armada_ThreadYielding(asf, s', tid)) && Armada_StepsStartNonyielding(asf, asf.step_next(s, steps[0], tid), s', steps[1..], tid) && asf.state_ok(s') == ok } predicate AtomicLiftPathFromAtomicSuccessful( lasf: AtomicSpecFunctions, hasf: Armada_SpecFunctions, s: State, s': State, path: Path, tid: Armada_ThreadHandle, steps: seq ) { && Armada_NextMultipleSteps(hasf, s, s', steps, tid) && match lasf.path_type(path) case AtomicPathType_Tau => |steps| == 1 && hasf.is_step_tau(steps[0]) case AtomicPathType_YY => AtomicStepsLiftPathFromAtomic(hasf, s, s', steps, tid, true, true, true) case AtomicPathType_YS => AtomicStepsLiftPathFromAtomic(hasf, s, s', steps, tid, true, true, false) case AtomicPathType_YR => AtomicStepsLiftPathFromAtomic(hasf, s, s', steps, tid, true, false, true) case AtomicPathType_RY => AtomicStepsLiftPathFromAtomic(hasf, s, s', steps, tid, false, true, true) case AtomicPathType_RS => AtomicStepsLiftPathFromAtomic(hasf, s, s', steps, tid, false, true, false) case AtomicPathType_RR => AtomicStepsLiftPathFromAtomic(hasf, s, s', steps, tid, false, false, true) } lemma lemma_LiftRecurrentEntryFromAtomic( lasf: AtomicSpecFunctions, hasf: Armada_SpecFunctions, s: State, s': State, tid: Armada_ThreadHandle, yr: Path, rrs: seq, rx: Path ) returns ( hsteps: seq ) requires forall s :: lasf.state_ok(s) <==> hasf.state_ok(s) requires AtomicValidRecursiveStep(lasf, s, tid, yr, rrs, rx) requires s' == lasf.path_next(AtomicGetStateAfterPaths(lasf, lasf.path_next(s, yr, tid), rrs, tid), rx, tid) requires forall ls, ls', path :: AtomicLiftPathFromAtomicPossible(lasf, ls, ls', path, tid) ==> exists hsteps' :: AtomicLiftPathFromAtomicSuccessful(lasf, hasf, ls, ls', path, tid, hsteps') ensures Armada_NextMultistep(hasf, s, s', hsteps, tid, false) { var ls1 := lasf.path_next(s, yr, tid); assert AtomicLiftPathFromAtomicPossible(lasf, s, ls1, yr, tid); var hsteps_yr :| AtomicLiftPathFromAtomicSuccessful(lasf, hasf, s, ls1, yr, tid, hsteps_yr); var hstep0 := hsteps_yr[0]; var hsteps_current := hsteps_yr[1..]; var s_next := hasf.step_next(s, hstep0, tid); var current_state := ls1; var rrs_remaining := rrs; var ls2 := AtomicGetStateAfterPaths(lasf, ls1, rrs, tid); while |rrs_remaining| > 0 invariant AtomicValidPathSequence(lasf, current_state, rrs_remaining, tid) invariant ls2 == AtomicGetStateAfterPaths(lasf, current_state, rrs_remaining, tid) invariant Armada_StepsStartNonyielding(hasf, s_next, current_state, hsteps_current, tid) invariant Armada_NextMultipleSteps(hasf, s_next, current_state, hsteps_current, tid) { var next_state := lasf.path_next(current_state, rrs_remaining[0], tid); assert AtomicLiftPathFromAtomicPossible(lasf, current_state, next_state, rrs_remaining[0], tid); var hsteps_next :| AtomicLiftPathFromAtomicSuccessful(lasf, hasf, current_state, next_state, rrs_remaining[0], tid, hsteps_next); lemma_CombineArmadaStepsStartNonyielding(hasf, s_next, current_state, next_state, hsteps_current, hsteps_next, tid); assert Armada_StepsStartNonyielding(hasf, s_next, next_state, hsteps_current + hsteps_next, tid); hsteps_current := hsteps_current + hsteps_next; rrs_remaining := rrs_remaining[1..]; current_state := next_state; } assert current_state == ls2; assert AtomicLiftPathFromAtomicPossible(lasf, ls2, s', rx, tid); var hsteps_next :| AtomicLiftPathFromAtomicSuccessful(lasf, hasf, ls2, s', rx, tid, hsteps_next); lemma_CombineArmadaStepsStartNonyielding(hasf, s_next, ls2, s', hsteps_current, hsteps_next, tid); assert Armada_StepsStartNonyielding(hasf, s_next, s', hsteps_current + hsteps_next, tid); assert Armada_NextMultipleSteps(hasf, s_next, s', hsteps_current + hsteps_next, tid); hsteps := [hstep0] + (hsteps_current + hsteps_next); assert hsteps[0] == hstep0; assert hsteps[1..] == hsteps_current + hsteps_next; assert Armada_NextMultistep(hasf, s, s', hsteps, tid, false); } lemma lemma_LiftEntryFromAtomic( lasf: AtomicSpecFunctions, hasf: Armada_SpecFunctions, s: State, s': State, lentry: AtomicTraceEntry ) returns ( hsteps: seq, tid: Armada_ThreadHandle, tau: bool ) requires forall s :: lasf.state_ok(s) <==> hasf.state_ok(s) requires AtomicNext(lasf, s, s', lentry) requires forall ls, ls', path, tid :: AtomicLiftPathFromAtomicPossible(lasf, ls, ls', path, tid) ==> exists hsteps' :: AtomicLiftPathFromAtomicSuccessful(lasf, hasf, ls, ls', path, tid, hsteps') ensures Armada_NextMultistep(hasf, s, s', hsteps, tid, tau) { match lentry { case AtomicTraceEntry_Stutter => hsteps := []; // No need to set tid, since the tid used for a stutter can be arbitrary. tau := false; case AtomicTraceEntry_Tau(tau_tid, path) => tid := tau_tid; assert AtomicLiftPathFromAtomicPossible(lasf, s, s', path, tid); hsteps :| AtomicLiftPathFromAtomicSuccessful(lasf, hasf, s, s', path, tid, hsteps); tau := true; case AtomicTraceEntry_Normal(normal_tid, path) => tid := normal_tid; assert AtomicLiftPathFromAtomicPossible(lasf, s, s', path, tid); hsteps :| AtomicLiftPathFromAtomicSuccessful(lasf, hasf, s, s', path, tid, hsteps); tau := false; case AtomicTraceEntry_Recurrent(entry_tid, yr, rrs, rx) => tid := entry_tid; hsteps := lemma_LiftRecurrentEntryFromAtomic(lasf, hasf, s, s', tid, yr, rrs, rx); tau := false; } } lemma lemma_LiftBehaviorFromAtomic( lasf: AtomicSpecFunctions, hasf: Armada_SpecFunctions, b: seq ) requires BehaviorSatisfiesSpec(b, AtomicSpec(lasf)) requires forall s :: lasf.init(s) ==> hasf.init(s) requires forall s :: lasf.state_ok(s) <==> hasf.state_ok(s) requires forall ls, ls', path, tid :: AtomicLiftPathFromAtomicPossible(lasf, ls, ls', path, tid) ==> exists hsteps :: AtomicLiftPathFromAtomicSuccessful(lasf, hasf, ls, ls', path, tid, hsteps) ensures BehaviorSatisfiesSpec(b, Armada_SpecFunctionsToSpec(hasf)) { var lspec := AtomicSpec(lasf); var hspec := Armada_SpecFunctionsToSpec(hasf); forall i {:trigger StatePair(b[i], b[i + 1]) in hspec.next} | 0 <= i < |b| - 1 ensures StatePair(b[i], b[i + 1]) in hspec.next { assert StatePair(b[i], b[i + 1]) in lspec.next; var lentry :| AtomicNext(lasf, b[i], b[i + 1], lentry); var hsteps, tid, tau := lemma_LiftEntryFromAtomic(lasf, hasf, b[i], b[i + 1], lentry); assert StatePair(b[i], b[i + 1]) in hspec.next; } } lemma lemma_AtomicSpecRefinesSpec( lasf: AtomicSpecFunctions, hasf: Armada_SpecFunctions ) returns ( refinement_relation: RefinementRelation ) requires forall s :: lasf.init(s) ==> hasf.init(s) requires forall s :: lasf.state_ok(s) <==> hasf.state_ok(s) requires forall ls, ls', path, tid :: AtomicLiftPathFromAtomicPossible(lasf, ls, ls', path, tid) ==> exists hsteps :: AtomicLiftPathFromAtomicSuccessful(lasf, hasf, ls, ls', path, tid, hsteps) ensures SpecRefinesSpec(AtomicSpec(lasf), Armada_SpecFunctionsToSpec(hasf), refinement_relation) ensures refinement_relation == iset s | true :: RefinementPair(s, s) { refinement_relation := iset s | true :: RefinementPair(s, s); forall lb | BehaviorSatisfiesSpec(lb, AtomicSpec(lasf)) ensures BehaviorRefinesSpec(lb, Armada_SpecFunctionsToSpec(hasf), refinement_relation) { lemma_LiftBehaviorFromAtomic(lasf, hasf, lb); lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lb, refinement_relation); assert BehaviorRefinesBehavior(lb, lb, refinement_relation) && BehaviorSatisfiesSpec(lb, Armada_SpecFunctionsToSpec(hasf)); } } } ================================================ FILE: Armada/strategies/generic/LiftToAtomic.i.dfy ================================================ include "GenericArmadaAtomic.i.dfy" module LiftToAtomicModule { import opened util_collections_seqs_s import opened util_collections_seqs_i import opened util_option_s import opened GenericArmadaSpecModule import opened AnnotatedBehaviorModule import opened ArmadaCommonDefinitions import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened InvariantsModule import opened GenericArmadaLemmasModule import opened GenericArmadaAtomicModule ////////////////////////////////////////////// // LIFTING TO ATOMIC ////////////////////////////////////////////// predicate ThreadYieldingOrRecurrent( asf: Armada_SpecFunctions, is_recurrent_pc: PC->bool, s: State, tid: Armada_ThreadHandle ) { Armada_ThreadYielding(asf, s, tid) || is_recurrent_pc(asf.get_thread_pc(s, tid).v) } predicate LInitImpliesHInit( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions ) { forall s :: lasf.init(s) ==> hasf.init(s) } predicate StateOKsMatch( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions ) { forall s :: lasf.state_ok(s) == hasf.state_ok(s) } predicate NonyieldingPCsMatch( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions ) { forall pc :: lasf.is_pc_nonyielding(pc) <==> hasf.is_pc_nonyielding(pc) } predicate ThreadPCsMatch( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions ) { forall s, tid :: lasf.get_thread_pc(s, tid) == hasf.get_thread_pc(s, tid) } predicate TausLiftableConditions( lasf: Armada_SpecFunctions, s: State, s': State, step: OneStep, tid: Armada_ThreadHandle ) { && lasf.step_valid(s, step, tid) && s' == lasf.step_next(s, step, tid) && lasf.is_step_tau(step) } predicate TausLiftable( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions ) { forall s, s', lstep, tid {:trigger TausLiftableConditions(lasf, s, s', lstep, tid)} :: TausLiftableConditions(lasf, s, s', lstep, tid) ==> exists hpath :: && hasf.path_valid(s, hpath, tid) && hasf.path_type(hpath).AtomicPathType_Tau? && s' == hasf.path_next(s, hpath, tid) } predicate SequencesCompressibleConditions( asf: Armada_SpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, steps: seq, tid: Armada_ThreadHandle ) { && |steps| > 0 && !asf.is_step_tau(steps[0]) && asf.step_valid(s, steps[0], tid) && ThreadYieldingOrRecurrent(asf, is_recurrent_pc, s, tid) && ThreadYieldingOrRecurrent(asf, is_recurrent_pc, s', tid) && StepsStartNonyieldingNonrecurrent(asf, is_recurrent_pc, asf.step_next(s, steps[0], tid), s', steps[1..], tid) } predicate SequencesCompressibleConclusions( asf: AtomicSpecFunctions, s: State, s': State, tid: Armada_ThreadHandle, path: Path ) { && asf.path_valid(s, path, tid) && s' == asf.path_next(s, path, tid) && !asf.path_type(path).AtomicPathType_Tau? && AtomicPathTypeMatchesPCTypes(asf, s, path, tid) } predicate SequencesCompressible( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions, is_recurrent_pc: PC->bool ) { forall s, s', lsteps, tid {:trigger SequencesCompressibleConditions(lasf, is_recurrent_pc, s, s', lsteps, tid)} :: SequencesCompressibleConditions(lasf, is_recurrent_pc, s, s', lsteps, tid) ==> exists hpath :: SequencesCompressibleConclusions(hasf, s, s', tid, hpath) } predicate RequirementsForLiftingToAtomic( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions, is_recurrent_pc: PC->bool ) { && LInitImpliesHInit(lasf, hasf) && StateOKsMatch(lasf, hasf) && NonyieldingPCsMatch(lasf, hasf) && ThreadPCsMatch(lasf, hasf) && TausLiftable(lasf, hasf) && SequencesCompressible(lasf, hasf, is_recurrent_pc) } predicate StepsEndNonyieldingNonrecurrent( asf: Armada_SpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, steps: seq, tid: Armada_ThreadHandle ) { if |steps| == 0 then s' == s else var s_next := asf.step_next(s, steps[0], tid); && !Armada_ThreadYielding(asf, s_next, tid) && !is_recurrent_pc(asf.get_thread_pc(s_next, tid).v) && !asf.is_step_tau(steps[0]) && StepsEndNonyieldingNonrecurrent(asf, is_recurrent_pc, s_next, s', steps[1..], tid) } predicate StepsStartNonyieldingNonrecurrent( asf: Armada_SpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, steps: seq, tid: Armada_ThreadHandle ) { if |steps| == 0 then s' == s else var s_next := asf.step_next(s, steps[0], tid); && asf.step_valid(s, steps[0], tid) && !Armada_ThreadYielding(asf, s, tid) && !is_recurrent_pc(asf.get_thread_pc(s, tid).v) && !asf.is_step_tau(steps[0]) && StepsStartNonyieldingNonrecurrent(asf, is_recurrent_pc, s_next, s', steps[1..], tid) } lemma lemma_ExtendStepsEndNonyieldingNonrecurrent( asf: Armada_SpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, s'': State, steps: seq, step: OneStep, tid: Armada_ThreadHandle ) requires Armada_NextMultipleSteps(asf, s, s', steps, tid) requires StepsEndNonyieldingNonrecurrent(asf, is_recurrent_pc, s, s', steps, tid) requires !asf.is_step_tau(step) requires asf.step_valid(s', step, tid) requires s'' == asf.step_next(s', step, tid) requires !Armada_ThreadYielding(asf, s'', tid) requires !is_recurrent_pc(asf.get_thread_pc(s'', tid).v) ensures Armada_NextMultipleSteps(asf, s, s'', steps + [step], tid) ensures StepsEndNonyieldingNonrecurrent(asf, is_recurrent_pc, s, s'', steps + [step], tid); { if |steps| > 0 { var s_next := asf.step_next(s, steps[0], tid); lemma_ExtendStepsEndNonyieldingNonrecurrent(asf, is_recurrent_pc, s_next, s', s'', steps[1..], step, tid); var all_steps := steps + [step]; assert all_steps[0] == steps[0]; assert all_steps[1..] == steps[1..] + [step]; assert StepsEndNonyieldingNonrecurrent(asf, is_recurrent_pc, s_next, s'', all_steps[1..], tid); assert StepsEndNonyieldingNonrecurrent(asf, is_recurrent_pc, s, s'', all_steps, tid); } } lemma lemma_IfStepsEndNonyieldingNonrecurrentThenShiftedTheyStartNonyielding( asf: Armada_SpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, s'': State, steps: seq, step: OneStep, tid: Armada_ThreadHandle ) requires Armada_NextMultipleSteps(asf, s, s', steps, tid) requires StepsEndNonyieldingNonrecurrent(asf, is_recurrent_pc, s, s', steps, tid) requires !asf.is_step_tau(step) requires asf.step_valid(s', step, tid) requires s'' == asf.step_next(s', step, tid) ensures var new_steps := steps + [step]; StepsStartNonyieldingNonrecurrent(asf, is_recurrent_pc, asf.step_next(s, new_steps[0], tid), s'', new_steps[1..], tid) { if |steps| == 0 { return; } var s_next := asf.step_next(s, steps[0], tid); lemma_IfStepsEndNonyieldingNonrecurrentThenShiftedTheyStartNonyielding(asf, is_recurrent_pc, s_next, s', s'', steps[1..], step, tid); var new_steps := steps + [step]; assert new_steps[0] == steps[0]; assert new_steps[1..] == steps[1..] + [step]; } lemma lemma_ExtendAtomicValidPathSequence( asf: AtomicSpecFunctions, s: State, s': State, s'': State, rrs: seq, rr: Path, tid: Armada_ThreadHandle ) requires AtomicValidPathSequence(asf, s, rrs, tid) requires s' == AtomicGetStateAfterPaths(asf, s, rrs, tid) requires asf.path_valid(s', rr, tid) requires s'' == asf.path_next(s', rr, tid) requires asf.path_type(rr).AtomicPathType_RR? ensures AtomicValidPathSequence(asf, s, rrs + [rr], tid) ensures s'' == AtomicGetStateAfterPaths(asf, s, rrs + [rr], tid) { if |rrs| > 0 { var s_next := asf.path_next(s, rrs[0], tid); lemma_ExtendAtomicValidPathSequence(asf, s_next, s', s'', rrs[1..], rr, tid); assert (rrs + [rr])[0] == rrs[0]; assert (rrs + [rr])[1..] == rrs[1..] + [rr]; } } lemma lemma_LiftMultistepToAtomicGivenYRRRs( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, steps: seq, tid: Armada_ThreadHandle, pos_after_rrs: int, pos_mid: int, yr: Path, rrs: seq, state_after_yr: State, state_after_rrs: State, s_mid: State ) returns ( entry: AtomicTraceEntry ) requires RequirementsForLiftingToAtomic(lasf, hasf, is_recurrent_pc) requires 0 <= pos_after_rrs <= pos_mid < |steps| requires hasf.path_type(yr).AtomicPathType_YR? requires state_after_yr == hasf.path_next(s, yr, tid) requires state_after_rrs == AtomicGetStateAfterPaths(hasf, state_after_yr, rrs, tid) requires hasf.path_valid(s, yr, tid) requires AtomicValidPathSequence(hasf, state_after_yr, rrs, tid) requires Armada_NextMultipleSteps(lasf, state_after_rrs, s_mid, steps[pos_after_rrs..pos_mid], tid) requires Armada_NextMultipleSteps(lasf, s_mid, s', steps[pos_mid..], tid) requires StepsEndNonyieldingNonrecurrent(lasf, is_recurrent_pc, state_after_rrs, s_mid, steps[pos_after_rrs..pos_mid], tid) requires Armada_NextMultipleSteps(lasf, state_after_rrs, s', steps[pos_after_rrs..], tid) requires Armada_StepsStartNonyielding(lasf, s_mid, s', steps[pos_mid..], tid) requires Armada_NextMultipleSteps(lasf, s_mid, s', steps[pos_mid..], tid) requires Armada_StepsStartNonyielding(lasf, s_mid, s', steps[pos_mid..], tid) requires !Armada_ThreadYielding(lasf, state_after_rrs, tid) requires is_recurrent_pc(lasf.get_thread_pc(state_after_rrs, tid).v) requires Armada_ThreadYielding(lasf, s', tid) ensures AtomicNext(hasf, s, s', entry) decreases |steps| - pos_mid { var s_next := lasf.step_next(s_mid, steps[pos_mid], tid); if pos_mid == |steps|-1 { assert s_next == s'; lemma_IfStepsEndNonyieldingNonrecurrentThenShiftedTheyStartNonyielding(lasf, is_recurrent_pc, state_after_rrs, s_mid, s_next, steps[pos_after_rrs..pos_mid], steps[pos_mid], tid); assert steps[pos_after_rrs..pos_mid] + [steps[pos_mid]] == steps[pos_after_rrs..]; assert SequencesCompressibleConditions(lasf, is_recurrent_pc, state_after_rrs, s', steps[pos_after_rrs..], tid); var rx :| SequencesCompressibleConclusions(hasf, state_after_rrs, s', tid, rx); entry := AtomicTraceEntry_Recurrent(tid, yr, rrs, rx); assert AtomicValidRecursiveStep(hasf, s, tid, yr, rrs, rx); return; } assert !Armada_ThreadYielding(lasf, s_next, tid); assert steps[pos_after_rrs..pos_mid+1] == steps[pos_after_rrs..pos_mid] + [steps[pos_mid]]; var pc := lasf.get_thread_pc(s_next, tid).v; if !is_recurrent_pc(pc) { lemma_ExtendStepsEndNonyieldingNonrecurrent(lasf, is_recurrent_pc, state_after_rrs, s_mid, s_next, steps[pos_after_rrs..pos_mid], steps[pos_mid], tid); entry := lemma_LiftMultistepToAtomicGivenYRRRs(lasf, hasf, is_recurrent_pc, s, s', steps, tid, pos_after_rrs, pos_mid + 1, yr, rrs, state_after_yr, state_after_rrs, s_next); return; } lemma_ExtendArmadaNextMultipleSteps(lasf, state_after_rrs, s_mid, s_next, steps[pos_after_rrs..pos_mid], steps[pos_mid], tid); lemma_IfStepsEndNonyieldingNonrecurrentThenShiftedTheyStartNonyielding(lasf, is_recurrent_pc, state_after_rrs, s_mid, s_next, steps[pos_after_rrs..pos_mid], steps[pos_mid], tid); assert SequencesCompressibleConditions(lasf, is_recurrent_pc, state_after_rrs, s_next, steps[pos_after_rrs..pos_mid+1], tid); var rr :| SequencesCompressibleConclusions(hasf, state_after_rrs, s_next, tid, rr); lemma_ExtendAtomicValidPathSequence(hasf, state_after_yr, state_after_rrs, s_next, rrs, rr, tid); entry := lemma_LiftMultistepToAtomicGivenYRRRs(lasf, hasf, is_recurrent_pc, s, s', steps, tid, pos_mid + 1, pos_mid + 1, yr, rrs + [rr], state_after_yr, s_next, s_next); } lemma lemma_LiftMultistepToAtomicGivenInitialSteps( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, steps: seq, tid: Armada_ThreadHandle, pos: int, s_mid: State ) returns ( entry: AtomicTraceEntry ) requires RequirementsForLiftingToAtomic(lasf, hasf, is_recurrent_pc) requires 0 <= pos < |steps| requires Armada_NextMultipleSteps(lasf, s, s_mid, steps[..pos], tid) requires Armada_NextMultipleSteps(lasf, s_mid, s', steps[pos..], tid) requires !lasf.is_step_tau(steps[pos]) requires lasf.step_valid(s_mid, steps[pos], tid) requires StepsEndNonyieldingNonrecurrent(lasf, is_recurrent_pc, s, s_mid, steps[..pos], tid) requires Armada_StepsStartNonyielding(lasf, lasf.step_next(s_mid, steps[pos], tid), s', steps[pos+1..], tid) requires Armada_ThreadYielding(lasf, s, tid) requires Armada_ThreadYielding(lasf, s', tid) ensures AtomicNext(hasf, s, s', entry) decreases |steps| - pos { var s_next := lasf.step_next(s_mid, steps[pos], tid); if pos == |steps|-1 { assert s_next == s'; lemma_IfStepsEndNonyieldingNonrecurrentThenShiftedTheyStartNonyielding(lasf, is_recurrent_pc, s, s_mid, s_next, steps[..pos], steps[pos], tid); assert steps == steps[..pos] + [steps[pos]]; assert SequencesCompressibleConditions(lasf, is_recurrent_pc, s, s', steps, tid); var hpath :| SequencesCompressibleConclusions(hasf, s, s', tid, hpath); entry := AtomicTraceEntry_Normal(tid, hpath); return; } assert !Armada_ThreadYielding(lasf, s_next, tid); assert steps[..pos+1] == steps[..pos] + [steps[pos]]; var pc := lasf.get_thread_pc(s_next, tid).v; if !is_recurrent_pc(pc) { lemma_ExtendStepsEndNonyieldingNonrecurrent(lasf, is_recurrent_pc, s, s_mid, s_next, steps[..pos], steps[pos], tid); entry := lemma_LiftMultistepToAtomicGivenInitialSteps(lasf, hasf, is_recurrent_pc, s, s', steps, tid, pos + 1, s_next); return; } lemma_ExtendArmadaNextMultipleSteps(lasf, s, s_mid, s_next, steps[..pos], steps[pos], tid); lemma_IfStepsEndNonyieldingNonrecurrentThenShiftedTheyStartNonyielding(lasf, is_recurrent_pc, s, s_mid, s_next, steps[..pos], steps[pos], tid); assert steps[..pos] + [steps[pos]] == steps[..pos+1]; assert SequencesCompressibleConditions(lasf, is_recurrent_pc, s, s_next, steps[..pos+1], tid); var yr :| SequencesCompressibleConclusions(hasf, s, s_next, tid, yr); entry := lemma_LiftMultistepToAtomicGivenYRRRs(lasf, hasf, is_recurrent_pc, s, s', steps, tid, pos + 1, pos + 1, yr, [], s_next, s_next, s_next); } lemma lemma_LiftMultistepToAtomicRegular( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, steps: seq, tid: Armada_ThreadHandle ) returns ( entry: AtomicTraceEntry ) requires RequirementsForLiftingToAtomic(lasf, hasf, is_recurrent_pc) requires |steps| > 0 requires !lasf.is_step_tau(steps[0]) requires Armada_ThreadYielding(lasf, s, tid) requires Armada_ThreadYielding(lasf, s', tid) requires Armada_NextMultipleSteps(lasf, s, s', steps, tid) requires Armada_StepsStartNonyielding(lasf, lasf.step_next(s, steps[0], tid), s', steps[1..], tid) ensures AtomicNext(hasf, s, s', entry) { entry := lemma_LiftMultistepToAtomicGivenInitialSteps(lasf, hasf, is_recurrent_pc, s, s', steps, tid, 0, s); } lemma lemma_LiftMultistepToAtomic( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions, is_recurrent_pc: PC->bool, s: State, s': State, steps: seq, tid: Armada_ThreadHandle, tau: bool ) returns ( entry: AtomicTraceEntry ) requires RequirementsForLiftingToAtomic(lasf, hasf, is_recurrent_pc) requires Armada_NextMultistep(lasf, s, s', steps, tid, tau) ensures AtomicNext(hasf, s, s', entry) { if |steps| == 0 { entry := AtomicTraceEntry_Stutter(); } else if tau { assert Armada_NextMultipleSteps(lasf, lasf.step_next(s, steps[0], tid), s', steps[1..], tid); assert TausLiftableConditions(lasf, s, s', steps[0], tid); var hpath :| && hasf.path_valid(s, hpath, tid) && hasf.path_type(hpath).AtomicPathType_Tau? && s' == hasf.path_next(s, hpath, tid); entry := AtomicTraceEntry_Tau(tid, hpath); } else { entry := lemma_LiftMultistepToAtomicRegular(lasf, hasf, is_recurrent_pc, s, s', steps, tid); } } lemma lemma_LiftToAtomic( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions, is_recurrent_pc: PC->bool, b: seq ) requires RequirementsForLiftingToAtomic(lasf, hasf, is_recurrent_pc) requires BehaviorSatisfiesSpec(b, Armada_SpecFunctionsToSpec(lasf)) ensures BehaviorSatisfiesSpec(b, AtomicSpec(hasf)) decreases |b| { forall pos | 0 <= pos < |b|-1 ensures StatePair(b[pos], b[pos+1]) in AtomicSpec(hasf).next { var s := b[pos]; var s' := b[pos+1]; assert StatePair(s, s') in Armada_SpecFunctionsToSpec(lasf).next; var steps, tid, tau :| Armada_NextMultistep(lasf, s, s', steps, tid, tau); var entry := lemma_LiftMultistepToAtomic(lasf, hasf, is_recurrent_pc, s, s', steps, tid, tau); } } lemma lemma_SpecRefinesAtomicSpec( lasf: Armada_SpecFunctions, hasf: AtomicSpecFunctions, is_recurrent_pc: PC->bool ) returns ( refinement_relation: RefinementRelation ) requires RequirementsForLiftingToAtomic(lasf, hasf, is_recurrent_pc) ensures SpecRefinesSpec(Armada_SpecFunctionsToSpec(lasf), AtomicSpec(hasf), refinement_relation) ensures refinement_relation == iset s: State | true :: RefinementPair(s, s) { refinement_relation := iset s: State | true :: RefinementPair(s, s); forall lb | BehaviorSatisfiesSpec(lb, Armada_SpecFunctionsToSpec(lasf)) ensures exists hb :: BehaviorRefinesBehavior(lb, hb, refinement_relation) && BehaviorSatisfiesSpec(hb, AtomicSpec(hasf)) { lemma_LiftToAtomic(lasf, hasf, is_recurrent_pc, lb); lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lb, refinement_relation); assert BehaviorRefinesBehavior(lb, lb, refinement_relation); assert BehaviorSatisfiesSpec(lb, AtomicSpec(hasf)); } } } ================================================ FILE: Armada/strategies/invariants.i.dfy ================================================ include "refinement/AnnotatedBehavior.i.dfy" include "../spec/refinement.s.dfy" module InvariantsModule { import opened util_collections_seqs_s import opened AnnotatedBehaviorModule import opened GeneralRefinementModule datatype StateActorPair = StateActorPair(s:State, actor:Actor) //////////////////////////////////////// // INVARIANTS OF SPECS //////////////////////////////////////// function {:opaque} Reachables(spec:Spec) : iset { iset b | BehaviorSatisfiesSpec(b, spec) :: last(b) } lemma lemma_ReachablesPremiumProperties( spec:Spec ) ensures var rs := Reachables(spec); && (forall s {:trigger s in spec.init} {:trigger s in rs} :: s in spec.init ==> s in rs) && (forall s, s' {:trigger s in rs, StatePair(s, s') in spec.next} :: s in rs && StatePair(s, s') in spec.next ==> s' in rs) && (forall s' {:trigger s' in rs, s' in spec.init} :: s' in rs && s' !in spec.init ==> exists s :: s in rs && StatePair(s, s') in spec.next) && (forall b, s {:trigger BehaviorSatisfiesSpec(b, spec), s in b}{:trigger BehaviorSatisfiesSpec(b, spec),s in rs} :: BehaviorSatisfiesSpec(b, spec) && s in b ==> s in rs); { reveal Reachables(); var rs := Reachables(spec); forall s | s in spec.init ensures s in rs; { var b := [s]; assert BehaviorSatisfiesSpec(b, spec); } forall s, s' | s in rs && StatePair(s, s') in spec.next ensures s' in rs; { var b :| BehaviorSatisfiesSpec(b, spec) && s == last(b); var b' := b + [s']; assert BehaviorSatisfiesSpec(b', spec); } forall s' | s' in rs && s' !in spec.init ensures exists s :: s in rs && StatePair(s, s') in spec.next; { var b :| BehaviorSatisfiesSpec(b, spec) && s' == last(b); assert |b| > 1; var b' := all_but_last(b); assert BehaviorSatisfiesSpec(b', spec); var s := last(b'); var penult := |b|-2; assert s == b[penult]; assert s' == b[penult+1]; assert s in rs && StatePair(s, s') in spec.next; } forall b, s | BehaviorSatisfiesSpec(b, spec) && s in b ensures s in rs; { var i :| 0 <= i < |b| && s == b[i]; var b' := b[..i+1]; assert BehaviorSatisfiesSpec(b', spec); assert s == last(b'); } } function ReachablesPremium(spec:Spec) : (rs:iset) ensures rs == Reachables(spec) ensures (forall s {:trigger s in spec.init} {:trigger s in rs} :: s in spec.init ==> s in rs); ensures (forall s, s' {:trigger s in rs, StatePair(s, s') in spec.next} :: s in rs && StatePair(s, s') in spec.next ==> s' in rs); ensures (forall s' {:trigger s' in rs, s' in spec.init} :: s' in rs && s' !in spec.init ==> exists s :: s in rs && StatePair(s, s') in spec.next); ensures (forall b, s {:trigger BehaviorSatisfiesSpec(b, spec), s in b}{:trigger BehaviorSatisfiesSpec(b, spec), s in rs} :: BehaviorSatisfiesSpec(b, spec) && s in b ==> s in rs); { lemma_ReachablesPremiumProperties(spec); Reachables(spec) } predicate IsSpecInvariant( inv:iset, spec:Spec ) { Reachables(spec) <= inv } predicate ConditionsForSpecInvariance( inv:iset, spec:Spec ) { && (forall s :: s in spec.init ==> s in inv) && (forall s, s' :: s in inv && StatePair(s, s') in spec.next ==> s' in inv) } predicate ConditionsForSpecInvarianceDeduction( inv_unknown:iset, inv_known:iset, spec:Spec ) { && (forall s :: s in spec.init ==> s in inv_unknown) && (forall s, s' :: && s in inv_known && s in inv_unknown && StatePair(s, s') in spec.next ==> s' in inv_unknown) } predicate ConditionsForSpecInvarianceDeductionBeforeAndAfter( inv_unknown:iset, inv_known:iset, spec:Spec ) { && (forall s :: s in spec.init ==> s in inv_unknown) && (forall s, s' :: && s in inv_known && s' in inv_known && s in inv_unknown && StatePair(s, s') in spec.next ==> s' in inv_unknown) } function ExtendSpecWithInvariant(spec:Spec, inv:iset) : Spec { Spec(spec.init, iset s, s' | StatePair(s, s') in spec.next && s in inv :: StatePair(s, s')) } lemma lemma_EstablishSpecInvariantPure( inv:iset, spec:Spec ) requires ConditionsForSpecInvariance(inv, spec) ensures IsSpecInvariant(inv, spec) { var rs := Reachables(spec); reveal Reachables(); forall r | r in rs ensures r in inv; { var b :| BehaviorSatisfiesSpec(b, spec) && last(b) == r; assert b[0] in spec.init; var i := 0; while i < |b|-1 invariant 0 <= i < |b|; invariant b[i] in inv; { assert b[i] in inv; assert StatePair(b[i], b[i+1]) in spec.next; assert b[i+1] in inv; i := i + 1; } assert i == |b|-1; assert b[i] == r; } } lemma lemma_EstablishSpecInvariantUsingInvariant( inv_unknown:iset, inv_known:iset, spec:Spec ) requires IsSpecInvariant(inv_known, spec) requires ConditionsForSpecInvarianceDeduction(inv_unknown, inv_known, spec) ensures IsSpecInvariant(inv_unknown, spec) { var rs := Reachables(spec); reveal Reachables(); forall r | r in rs ensures r in inv_unknown; { var b :| BehaviorSatisfiesSpec(b, spec) && last(b) == r; var i := 0; while i < |b|-1 invariant 0 <= i < |b|; invariant b[i] in inv_unknown; { assert BehaviorSatisfiesSpec(b[..i+1], spec); assert StatePair(b[i], b[i+1]) in spec.next; i := i + 1; } assert i == |b|-1; assert b[i] == r; } } lemma lemma_EstablishSpecInvariantUsingInvariantBeforeAndAfter( inv_unknown:iset, inv_known:iset, spec:Spec ) requires IsSpecInvariant(inv_known, spec) requires ConditionsForSpecInvarianceDeductionBeforeAndAfter(inv_unknown, inv_known, spec) ensures IsSpecInvariant(inv_unknown, spec) { var rs := Reachables(spec); reveal Reachables(); forall r | r in rs ensures r in inv_unknown; { var b :| BehaviorSatisfiesSpec(b, spec) && last(b) == r; var i := 0; while i < |b|-1 invariant 0 <= i < |b|; invariant b[i] in inv_unknown; { assert BehaviorSatisfiesSpec(b[..i+1], spec); assert BehaviorSatisfiesSpec(b[..i+1+1], spec); assert StatePair(b[i], b[i+1]) in spec.next; i := i + 1; } assert i == |b|-1; assert b[i] == r; } } lemma lemma_SpecInvariantHoldsAtStart( s:State, spec:Spec, inv:iset ) requires s in spec.init requires IsSpecInvariant(inv, spec) ensures s in inv { var rs := ReachablesPremium(spec); } lemma lemma_SpecInvariantHoldsAtStep( b:seq, t:int, spec:Spec, inv:iset ) requires BehaviorSatisfiesSpec(b, spec) requires 0 <= t < |b| requires IsSpecInvariant(inv, spec) ensures b[t] in inv { reveal Reachables(); var b' := b[..t+1]; assert BehaviorSatisfiesSpec(b', spec); } lemma lemma_ReachablesSatisfyConditionsForSpecInvariance( spec:Spec ) ensures ConditionsForSpecInvariance(Reachables(spec), spec); { var rs := ReachablesPremium(spec); } lemma lemma_ExtendingSpecWithInvariantProducesEquivalentSpec( spec:Spec, inv:iset, b:seq ) requires IsSpecInvariant(inv, spec) ensures BehaviorSatisfiesSpec(b, spec) <==> BehaviorSatisfiesSpec(b, ExtendSpecWithInvariant(spec, inv)) { var espec := ExtendSpecWithInvariant(spec, inv); if BehaviorSatisfiesSpec(b, spec) { lemma_ReachablesPremiumProperties(spec); assert BehaviorSatisfiesSpec(b, espec); } if BehaviorSatisfiesSpec(b, espec) { forall i | 0 <= i < |b|-1 ensures StatePair(b[i], b[i+1]) in espec.next { } assert BehaviorSatisfiesSpec(b, spec); } } //////////////////////////////////////// // INVARIANTS OF ANNOTATED SPECS //////////////////////////////////////// function {:opaque} AnnotatedReachables(spec:AnnotatedBehaviorSpec) : iset { iset ab | AnnotatedBehaviorSatisfiesSpec(ab, spec) :: last(ab.states) } lemma lemma_AnnotatedReachablesPremiumProperties( spec:AnnotatedBehaviorSpec ) ensures var rs := AnnotatedReachables(spec); && (forall s {:trigger s in spec.init} {:trigger s in rs} :: s in spec.init ==> s in rs) && (forall s, s', step {:trigger s in rs, ActionTuple(s, s', step) in spec.next} :: s in rs && ActionTuple(s, s', step) in spec.next ==> s' in rs) && (forall s' {:trigger s' in rs, s' in spec.init} :: s' in rs && s' !in spec.init ==> exists s, step :: s in rs && ActionTuple(s, s', step) in spec.next) && (forall ab, s {:trigger AnnotatedBehaviorSatisfiesSpec(ab, spec), s in ab.states} {:trigger AnnotatedBehaviorSatisfiesSpec(ab, spec), s in rs} :: AnnotatedBehaviorSatisfiesSpec(ab, spec) && s in ab.states ==> s in rs); { reveal AnnotatedReachables(); var rs := AnnotatedReachables(spec); forall s | s in spec.init ensures s in rs; { var ab := AnnotatedBehavior([s], []); assert AnnotatedBehaviorSatisfiesSpec(ab, spec); } forall s, s', step | s in rs && ActionTuple(s, s', step) in spec.next ensures s' in rs; { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, spec) && s == last(ab.states); var ab' := AnnotatedBehavior(ab.states + [s'], ab.trace + [step]); assert AnnotatedBehaviorSatisfiesSpec(ab', spec); } forall s' | s' in rs && s' !in spec.init ensures exists s, step :: s in rs && ActionTuple(s, s', step) in spec.next; { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, spec) && s' == last(ab.states); assert |ab.states| > 1; var ab' := AnnotatedBehavior(all_but_last(ab.states), all_but_last(ab.trace)); assert AnnotatedBehaviorSatisfiesSpec(ab', spec); var s := last(ab'.states); var penult := |ab.states|-2; var step := ab.trace[penult]; assert s == ab.states[penult]; assert s' == ab.states[penult+1]; assert s in rs && ActionTuple(s, s', step) in spec.next; } forall ab, s | AnnotatedBehaviorSatisfiesSpec(ab, spec) && s in ab.states ensures s in rs; { var i :| 0 <= i < |ab.states| && s == ab.states[i]; var ab' := AnnotatedBehavior(ab.states[..i+1], ab.trace[..i]); assert AnnotatedBehaviorSatisfiesSpec(ab', spec); assert s == last(ab'.states); } } function AnnotatedReachablesPremium( spec:AnnotatedBehaviorSpec ) : (rs:iset) ensures rs == AnnotatedReachables(spec) ensures forall s {:trigger s in spec.init} {:trigger s in rs} :: s in spec.init ==> s in rs; ensures forall s, s', step {:trigger s in rs, ActionTuple(s, s', step) in spec.next} :: s in rs && ActionTuple(s, s', step) in spec.next ==> s' in rs; ensures forall s' {:trigger s' in rs, s' in spec.init} :: s' in rs && !(s' in spec.init) ==> exists s, step :: s in rs && ActionTuple(s, s', step) in spec.next; ensures forall ab, s {:trigger AnnotatedBehaviorSatisfiesSpec(ab, spec), s in ab.states} {:trigger AnnotatedBehaviorSatisfiesSpec(ab, spec), s in rs} :: AnnotatedBehaviorSatisfiesSpec(ab, spec) && s in ab.states ==> s in rs; { lemma_AnnotatedReachablesPremiumProperties(spec); AnnotatedReachables(spec) } lemma lemma_InitStateInAnnotatedReachables( s:State, spec:AnnotatedBehaviorSpec ) requires s in spec.init ensures s in AnnotatedReachables(spec) { assert AnnotatedReachables(spec) == AnnotatedReachablesPremium(spec); } lemma lemma_NextMaintainsAnnotatedReachables( s:State, s':State, step:Step, spec:AnnotatedBehaviorSpec ) requires s in AnnotatedReachables(spec) requires ActionTuple(s, s', step) in spec.next ensures s' in AnnotatedReachables(spec) { assert AnnotatedReachables(spec) == AnnotatedReachablesPremium(spec); } lemma lemma_StateNextSeqMaintainsAnnotatedReachables( states:seq, trace:seq, spec:AnnotatedBehaviorSpec ) requires StateNextSeq(states, trace, spec.next) requires states[0] in AnnotatedReachables(spec) ensures forall s :: s in states ==> s in AnnotatedReachables(spec) { var pos := 0; while pos < |states|-1 invariant 0 <= pos < |states| invariant forall i :: 0 <= i <= pos ==> states[i] in AnnotatedReachables(spec) { assert states[pos] in AnnotatedReachables(spec); lemma_NextMaintainsAnnotatedReachables(states[pos], states[pos+1], trace[pos], spec); pos := pos + 1; } } predicate IsInvariantOfSpec( inv:iset, spec:AnnotatedBehaviorSpec ) { AnnotatedReachables(spec) <= inv } predicate ConditionsForInvariance( inv:iset, spec:AnnotatedBehaviorSpec ) { && (forall s :: s in spec.init ==> s in inv) && (forall s, s', step :: s in inv && ActionTuple(s, s', step) in spec.next ==> s' in inv) } predicate ConditionsForInvarianceDeduction( inv_unknown:iset, inv_known:iset, spec:AnnotatedBehaviorSpec ) { && (forall s :: s in spec.init ==> s in inv_unknown) && (forall s, s', step :: && s in inv_known && s in inv_unknown && ActionTuple(s, s', step) in spec.next ==> s' in inv_unknown) } predicate ConditionsForInvarianceDeductionBeforeAndAfter( inv_unknown:iset, inv_known:iset, spec:AnnotatedBehaviorSpec ) { && (forall s :: s in spec.init ==> s in inv_unknown) && (forall s, s', step :: && s in inv_known && s' in inv_known && s in inv_unknown && ActionTuple(s, s', step) in spec.next ==> s' in inv_unknown) } function ExtendAnnotatedBehaviorSpecWithInvariant( spec:AnnotatedBehaviorSpec, inv:iset ) : AnnotatedBehaviorSpec { AnnotatedBehaviorSpec(spec.init, iset s, s', step | ActionTuple(s, s', step) in spec.next && s in inv :: ActionTuple(s, s', step)) } lemma lemma_EstablishInvariantPure( inv:iset, spec:AnnotatedBehaviorSpec ) requires ConditionsForInvariance(inv, spec) ensures IsInvariantOfSpec(inv, spec) { var rs := AnnotatedReachables(spec); reveal AnnotatedReachables(); forall r | r in rs ensures r in inv; { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, spec) && last(ab.states) == r; assert ab.states[0] in spec.init; var i := 0; while i < |ab.states|-1 invariant 0 <= i < |ab.states|; invariant ab.states[i] in inv; { assert ab.states[i] in inv; assert ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in spec.next; assert ab.states[i+1] in inv; i := i + 1; } assert i == |ab.states|-1; assert ab.states[i] == r; } } lemma lemma_EstablishInvariantUsingInvariant( inv_unknown:iset, inv_known:iset, spec:AnnotatedBehaviorSpec ) requires IsInvariantOfSpec(inv_known, spec) requires ConditionsForInvarianceDeduction(inv_unknown, inv_known, spec) ensures IsInvariantOfSpec(inv_unknown, spec) { var rs := AnnotatedReachables(spec); reveal AnnotatedReachables(); forall r | r in rs ensures r in inv_unknown; { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, spec) && last(ab.states) == r; var i := 0; while i < |ab.states|-1 invariant 0 <= i < |ab.states|; invariant ab.states[i] in inv_unknown; { assert AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(ab.states[..i+1], ab.trace[..i]), spec); assert ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in spec.next; i := i + 1; } assert i == |ab.states|-1; assert ab.states[i] == r; } } lemma lemma_EstablishInvariantUsingInvariantBeforeAndAfter( inv_unknown:iset, inv_known:iset, spec:AnnotatedBehaviorSpec ) requires IsInvariantOfSpec(inv_known, spec) requires ConditionsForInvarianceDeductionBeforeAndAfter(inv_unknown, inv_known, spec) ensures IsInvariantOfSpec(inv_unknown, spec) { var rs := AnnotatedReachables(spec); reveal AnnotatedReachables(); forall r | r in rs ensures r in inv_unknown; { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, spec) && last(ab.states) == r; var i := 0; while i < |ab.states|-1 invariant 0 <= i < |ab.states|; invariant ab.states[i] in inv_unknown; { assert AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(ab.states[..i+1], ab.trace[..i]), spec); assert AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(ab.states[..i+1+1], ab.trace[..i+1]), spec); assert ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in spec.next; i := i + 1; } assert i == |ab.states|-1; assert ab.states[i] == r; } } lemma lemma_InvariantHoldsAtStart( s:State, spec:AnnotatedBehaviorSpec, inv:iset ) requires s in spec.init requires IsInvariantOfSpec(inv, spec) ensures s in inv { var rs := AnnotatedReachablesPremium(spec); } lemma lemma_InvariantHoldsAtStep( ab:AnnotatedBehavior, t:int, spec:AnnotatedBehaviorSpec, inv:iset ) requires AnnotatedBehaviorSatisfiesSpec(ab, spec) requires 0 <= t < |ab.states| requires IsInvariantOfSpec(inv, spec) ensures ab.states[t] in inv { reveal AnnotatedReachables(); var ab' := AnnotatedBehavior(ab.states[..t+1], ab.trace[..t]); assert AnnotatedBehaviorSatisfiesSpec(ab', spec); } lemma lemma_AnnotatedReachablesSatisfyConditionsForInvariance( spec:AnnotatedBehaviorSpec ) ensures ConditionsForInvariance(AnnotatedReachables(spec), spec); { var rs := AnnotatedReachablesPremium(spec); } lemma lemma_ConditionsForInvarianceTransferAcrossStateNextSeq( states:seq, trace:seq, spec:AnnotatedBehaviorSpec, inv:iset ) requires ConditionsForInvariance(inv, spec) requires StateNextSeq(states, trace, spec.next) requires states[0] in inv ensures forall s :: s in states ==> s in inv { var j := 1; while j < |states| invariant 0 <= j <= |states|; invariant forall i :: 0 <= i < j ==> states[i] in inv; { var prev := j-1; assert ActionTuple(states[prev], states[prev+1], trace[prev]) in spec.next; j := j + 1; } } lemma lemma_ConjunctionOfInvariantsIsInvariant( spec:AnnotatedBehaviorSpec, invs:seqbool>, aggregate_inv:State->bool ) requires forall inv :: inv in invs ==> IsInvariantPredicateOfSpec(inv, spec) requires forall s :: (forall inv :: inv in invs ==> inv(s)) ==> aggregate_inv(s) ensures IsInvariantPredicateOfSpec(aggregate_inv, spec) { } lemma lemma_ExtendingAnnotatedSpecWithInvariantProducesEquivalentSpec( spec:AnnotatedBehaviorSpec, inv:iset, ab:AnnotatedBehavior ) requires IsInvariantOfSpec(inv, spec) ensures AnnotatedBehaviorSatisfiesSpec(ab, spec) <==> AnnotatedBehaviorSatisfiesSpec(ab, ExtendAnnotatedBehaviorSpecWithInvariant(spec, inv)) { var espec := ExtendAnnotatedBehaviorSpecWithInvariant(spec, inv); if AnnotatedBehaviorSatisfiesSpec(ab, spec) { lemma_AnnotatedReachablesPremiumProperties(spec); assert AnnotatedBehaviorSatisfiesSpec(ab, espec); } if AnnotatedBehaviorSatisfiesSpec(ab, espec) { forall i | 0 <= i < |ab.trace| ensures ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in espec.next { } assert AnnotatedBehaviorSatisfiesSpec(ab, spec); } } predicate IsInvariantPredicateOfSpec( inv:State->bool, spec:AnnotatedBehaviorSpec ) { forall s :: s in AnnotatedReachables(spec) ==> inv(s) } predicate ConditionsForInvariancePredicate( inv:State->bool, spec:AnnotatedBehaviorSpec ) { && (forall s :: s in spec.init ==> inv(s)) && (forall s, s', step :: inv(s) && ActionTuple(s, s', step) in spec.next ==> inv(s')) } predicate ConditionsForInvariancePredicateDeduction( inv_unknown:State->bool, inv_known:State->bool, spec:AnnotatedBehaviorSpec ) { && (forall s :: s in spec.init ==> inv_unknown(s)) && (forall s, s', step :: && inv_known(s) && inv_unknown(s) && ActionTuple(s, s', step) in spec.next ==> inv_unknown(s')) } predicate ConditionsForInvariancePredicateDeductionBeforeAndAfter( inv_unknown:State->bool, inv_known:State->bool, spec:AnnotatedBehaviorSpec ) { && (forall s :: s in spec.init ==> inv_unknown(s)) && (forall s, s', step :: && inv_known(s) && inv_known(s') && inv_unknown(s) && ActionTuple(s, s', step) in spec.next ==> inv_unknown(s')) } function ExtendAnnotatedBehaviorSpecWithInvariantPredicate( spec:AnnotatedBehaviorSpec, inv:State->bool ) : AnnotatedBehaviorSpec { AnnotatedBehaviorSpec(spec.init, iset s, s', step | ActionTuple(s, s', step) in spec.next && inv(s) :: ActionTuple(s, s', step)) } lemma lemma_EstablishInvariantPredicatePure( inv:State->bool, spec:AnnotatedBehaviorSpec ) requires ConditionsForInvariancePredicate(inv, spec) ensures IsInvariantPredicateOfSpec(inv, spec) { var rs := AnnotatedReachables(spec); reveal AnnotatedReachables(); forall r | r in rs ensures inv(r); { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, spec) && last(ab.states) == r; assert ab.states[0] in spec.init; var i := 0; while i < |ab.states|-1 invariant 0 <= i < |ab.states|; invariant inv(ab.states[i]); { assert inv(ab.states[i]); assert ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in spec.next; assert inv(ab.states[i+1]); i := i + 1; } assert i == |ab.states|-1; assert ab.states[i] == r; } } lemma lemma_EstablishInvariantPredicateUsingInvariantPredicate( inv_unknown:State->bool, inv_known:State->bool, spec:AnnotatedBehaviorSpec ) requires IsInvariantPredicateOfSpec(inv_known, spec) requires ConditionsForInvariancePredicateDeduction(inv_unknown, inv_known, spec) ensures IsInvariantPredicateOfSpec(inv_unknown, spec) { var rs := AnnotatedReachables(spec); reveal AnnotatedReachables(); forall r | r in rs ensures inv_unknown(r); { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, spec) && last(ab.states) == r; var i := 0; while i < |ab.states|-1 invariant 0 <= i < |ab.states| invariant inv_unknown(ab.states[i]) { lemma_InvariantPredicateHoldsAtStep(ab, i, spec, inv_known); assert ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in spec.next; assert inv_unknown(ab.states[i+1]); i := i + 1; } assert i == |ab.states|-1; assert ab.states[i] == r; } } lemma lemma_EstablishInvariantPredicateUsingInvariantPredicateBeforeAndAfter( inv_unknown:State->bool, inv_known:State->bool, spec:AnnotatedBehaviorSpec ) requires IsInvariantPredicateOfSpec(inv_known, spec) requires ConditionsForInvariancePredicateDeductionBeforeAndAfter(inv_unknown, inv_known, spec) ensures IsInvariantPredicateOfSpec(inv_unknown, spec) { var rs := AnnotatedReachables(spec); reveal AnnotatedReachables(); forall r | r in rs ensures inv_unknown(r); { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, spec) && last(ab.states) == r; var i := 0; while i < |ab.states|-1 invariant 0 <= i < |ab.states| invariant inv_unknown(ab.states[i]) { assert AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(ab.states[..i+1], ab.trace[..i]), spec); assert AnnotatedBehaviorSatisfiesSpec(AnnotatedBehavior(ab.states[..i+1+1], ab.trace[..i+1]), spec); assert ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in spec.next; i := i + 1; } assert i == |ab.states|-1; assert ab.states[i] == r; } } lemma lemma_InvariantPredicateHoldsAtStart( s:State, spec:AnnotatedBehaviorSpec, inv:State->bool ) requires s in spec.init requires IsInvariantPredicateOfSpec(inv, spec) ensures inv(s) { var rs := AnnotatedReachablesPremium(spec); } lemma lemma_InvariantPredicateHoldsAtStep( ab:AnnotatedBehavior, t:int, spec:AnnotatedBehaviorSpec, inv:State->bool ) requires AnnotatedBehaviorSatisfiesSpec(ab, spec) requires 0 <= t < |ab.states| requires IsInvariantPredicateOfSpec(inv, spec) ensures inv(ab.states[t]) { reveal AnnotatedReachables(); var ab' := AnnotatedBehavior(ab.states[..t+1], ab.trace[..t]); assert AnnotatedBehaviorSatisfiesSpec(ab', spec); } lemma lemma_ConditionsForInvariancePredicateTransferAcrossStateNextSeq( states:seq, trace:seq, spec:AnnotatedBehaviorSpec, inv:State->bool ) requires ConditionsForInvariancePredicate(inv, spec) requires StateNextSeq(states, trace, spec.next) requires inv(states[0]) ensures forall s :: s in states ==> inv(s) { var j := 1; while j < |states| invariant 0 <= j <= |states|; invariant forall i :: 0 <= i < j ==> inv(states[i]) { var prev := j-1; assert ActionTuple(states[prev], states[prev+1], trace[prev]) in spec.next; j := j + 1; } } lemma lemma_ExtendingAnnotatedSpecWithInvariantPredicateProducesEquivalentSpec( spec:AnnotatedBehaviorSpec, inv:State->bool, ab:AnnotatedBehavior ) requires IsInvariantPredicateOfSpec(inv, spec) ensures AnnotatedBehaviorSatisfiesSpec(ab, spec) <==> AnnotatedBehaviorSatisfiesSpec(ab, ExtendAnnotatedBehaviorSpecWithInvariantPredicate(spec, inv)) { var espec := ExtendAnnotatedBehaviorSpecWithInvariantPredicate(spec, inv); if AnnotatedBehaviorSatisfiesSpec(ab, spec) { lemma_AnnotatedReachablesPremiumProperties(spec); assert AnnotatedBehaviorSatisfiesSpec(ab, espec); } if AnnotatedBehaviorSatisfiesSpec(ab, espec) { forall i | 0 <= i < |ab.trace| ensures ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in espec.next { } assert AnnotatedBehaviorSatisfiesSpec(ab, spec); } } lemma lemma_StateInAnnotatedBehaviorInAnnotatedReachables( spec:AnnotatedBehaviorSpec, ab:AnnotatedBehavior, pos:int ) requires AnnotatedBehaviorSatisfiesSpec(ab, spec) requires 0 <= pos < |ab.states| ensures ab.states[pos] in AnnotatedReachables(spec) { assert AnnotatedReachables(spec) == AnnotatedReachablesPremium(spec); lemma_StateNextSeqMaintainsAnnotatedReachables(ab.states, ab.trace, spec); assert ab.states[pos] in ab.states; } } ================================================ FILE: Armada/strategies/reduction/AtomicReduction.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file contains a lemma for performing refinement via a Cohen-Lamport reduction on an Armada // atomic behavior. Such a reduction takes a behavior satisfying a low-level spec, which allows // threads to sometimes be in phase 1 or 2 between multisteps, and returns a behavior satisfying a // higher-level specification that only allows those phases in the middle of multisteps. It does so // by lifting sequences of multisteps in the lower-level specification to single multisteps in the // higher-level specification. // // To use this lemma, first create a request r of type AtomicReductionRequest (defined in // AtomicReductionSpec.i.dfy). Then, prove that it's a valid request, i.e., that // ValidAtomicReductionRequest(r). // // This lemma makes use of a lemma in another file (lemma_PerformRefinementViaReduction, in // RefinementViaReduction.i.dfy). That lemma performs refinement via reduction for generic state // machines. // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "AtomicReductionSpec.i.dfy" include "AtomicReductionLemmas.i.dfy" include "RefinementViaReduction.i.dfy" module AtomicReductionModule { import opened util_collections_seqs_s import opened util_collections_seqs_i import opened util_option_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened AtomicReductionSpecModule import opened AtomicReductionLemmasModule import opened RefinementViaReductionSpecModule import opened RefinementViaReductionModule import opened GenericArmadaSpecModule import opened GenericArmadaAtomicModule import opened ArmadaCommonDefinitions lemma lemma_ReduceAtomicBehavior( arr:AtomicReductionRequest, lb:AnnotatedBehavior> ) returns ( hb:AnnotatedBehavior> ) requires ValidAtomicReductionRequest(arr) requires AnnotatedBehaviorSatisfiesSpec(lb, AtomicAnnotatedSpec(arr.l)) ensures BehaviorRefinesBehavior(lb.states, hb.states, arr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, AtomicAnnotatedSpec(arr.h)) { var mb1 := lemma_IfBehaviorSatisfiesGenericSpecThenItSatisfiesRefinementViaReductionLSpec(arr, lb); lemma_IfAtomicReductionRequestValidThenRefinementViaReductionRequestValid(arr); var mb2 := lemma_PerformRefinementViaReduction(GetRefinementViaReductionRequest(arr), mb1); var hstates := MapSeqToSeq(mb2.states, arr.lstate_to_hstate); var htrace := MapSeqToSeq(mb2.trace, x => ConvertMultistepToAtomicTraceEntry(arr, x)); hb := AnnotatedBehavior(hstates, htrace); var mh_map := ConvertMapToSeq(|hstates|, map i | 0 <= i < |hstates| :: RefinementRange(i, i)); var hspec := AtomicAnnotatedSpec(arr.h); assert BehaviorRefinesBehaviorUsingRefinementMap(mb2.states, hb.states, arr.relation, mh_map); forall i | 0 <= i < |htrace| ensures ActionTuple(hstates[i], hstates[i+1], htrace[i]) in hspec.next { lemma_GenericNextReducedBehaviorSatisfiesInv(arr, mb2, i); lemma_LHMaintainsNext(arr, mb2.states[i], mb2.states[i+1], mb2.trace[i], hstates[i], hstates[i+1], htrace[i]); } assert AnnotatedBehaviorSatisfiesSpec(hb, hspec); lemma_RefinementConvolutionPure(lb.states, mb2.states, hb.states, arr.self_relation, arr.relation, arr.relation); } lemma lemma_PerformAtomicReduction( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures SpecRefinesSpec(AtomicSpec(arr.l), AtomicSpec(arr.h), arr.relation) { var lspec := AtomicSpec(arr.l); var hspec := AtomicSpec(arr.h); forall lb | BehaviorSatisfiesSpec(lb, lspec) ensures BehaviorRefinesSpec(lb, hspec, arr.relation) { var alb := lemma_AtomicBehaviorToAnnotatedBehavior(arr.l, lb); var ahb := lemma_ReduceAtomicBehavior(arr, alb); lemma_AtomicAnnotatedBehaviorSatisfiesAtomicSpec(arr.h, ahb); assert BehaviorRefinesBehavior(lb, ahb.states, arr.relation) && BehaviorSatisfiesSpec(ahb.states, hspec); } } } ================================================ FILE: Armada/strategies/reduction/AtomicReductionLemmas.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file contains lemmas useful in effecting a refinement via reduction on an Armada atomic // behavior. They support lemma_PerformAtomicReduction (in AtomicReduction.i.dfy). // // The general strategy is to show that the Armada atomic state machine, despite all its complexity // having to do with things like tau operations and recurrent trace entries, nevertheless satisfies // the conditions required by the more generic lemma_PerformRefinementViaReduction (in // RefinementViaReduction.i.dfy). These conditions are satisfied by creating a // RefinementViaReductionRequest and proving that it satisfies ValidRefinementViaReductionRequest. // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "AtomicReductionSpec.i.dfy" include "RefinementViaReductionSpec.i.dfy" include "../../util/collections/seqs.i.dfy" include "../generic/GenericArmadaLemmas.i.dfy" module AtomicReductionLemmasModule { import opened util_collections_seqs_s import opened util_collections_seqs_i import opened util_option_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened AtomicReductionSpecModule import opened RefinementViaReductionSpecModule import opened GenericArmadaSpecModule import opened GenericArmadaLemmasModule import opened GenericArmadaAtomicModule import opened ArmadaCommonDefinitions ////////////////////////////////////////////////////////// // FUNCTIONS FOR BUILDING CRASHING REDUCTION REQUEST ////////////////////////////////////////////////////////// predicate IsNonyieldingOrInPhase( arr: AtomicReductionRequest, s: LState, tid: Armada_ThreadHandle ) { var pc := arr.l.get_thread_pc(s, tid); arr.l.state_ok(s) && pc.Some? && (arr.l.is_pc_nonyielding(pc.v) || arr.is_phase1(pc.v) || arr.is_phase2(pc.v)) } predicate AtomicValidPathSequenceOfAnyType( asf: AtomicSpecFunctions, s: State, paths: seq, tid: Armada_ThreadHandle ) { |paths| > 0 ==> && asf.path_valid(s, paths[0], tid) && AtomicValidPathSequenceOfAnyType(asf, asf.path_next(s, paths[0], tid), paths[1..], tid) } predicate AtomicNextMultiplePaths( asf: AtomicSpecFunctions, s: State, s': State, paths: seq, tid: Armada_ThreadHandle ) { && AtomicValidPathSequenceOfAnyType(asf, s, paths, tid) && s' == AtomicGetStateAfterPaths(asf, s, paths, tid) } function GetReducedAtomicSpecFunctions( arr:AtomicReductionRequest ) : AtomicSpecFunctions { AtomicSpecFunctions( arr.l.init, arr.l.path_valid, arr.l.path_next, lpath => arr.h.path_type(arr.lpath_to_hpath(lpath)), arr.l.state_ok, arr.l.get_thread_pc, lpc => arr.h.is_pc_nonyielding(arr.lpc_to_hpc(lpc)) ) } predicate GenericNextReduced( arr:AtomicReductionRequest, s:LState, s':LState, paths:seq, tid:Armada_ThreadHandle, tau:bool ) { && AtomicNextMultiplePaths(arr.l, s, s', paths, tid) && (forall path :: path in paths ==> arr.l.path_type(path).AtomicPathType_Tau? == tau) && if tau then |paths| <= 1 else |paths| > 0 ==> ( && !IsNonyieldingOrInPhase(arr, s, tid) && !IsNonyieldingOrInPhase(arr, s', tid) && (var states := AtomicGetStateSequence(arr.l, s, paths, tid); forall i :: 0 < i < |paths| ==> IsNonyieldingOrInPhase(arr, states[i], tid)) ) } predicate AtomicNextMultistep( asf:AtomicSpecFunctions, s:State, s':State, paths:seq, tid:Armada_ThreadHandle, tau:bool ) { && AtomicNextMultiplePaths(asf, s, s', paths, tid) && (forall path :: path in paths ==> asf.path_type(path).AtomicPathType_Tau? == tau) && if tau then |paths| <= 1 else |paths| > 0 ==> ( && AtomicThreadYielding(asf, s, tid) && AtomicThreadYielding(asf, s', tid) && (var states := AtomicGetStateSequence(asf, s, paths, tid); forall i :: 0 < i < |paths| ==> !AtomicThreadYielding(asf, states[i], tid)) ) } predicate RefinementViaReductionLSpecNext( arr:AtomicReductionRequest, s:LState, s':LState, multistep:Armada_Multistep ) { AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau) } function GetRefinementViaReductionLSpec( arr:AtomicReductionRequest ) : AnnotatedBehaviorSpec> { AnnotatedBehaviorSpec( iset s | arr.l.init(s), iset s, s', multistep | RefinementViaReductionLSpecNext(arr, s, s', multistep) :: ActionTuple(s, s', multistep)) } predicate RefinementViaReductionHSpecNext( arr:AtomicReductionRequest, s:LState, s':LState, multistep:Armada_Multistep ) { GenericNextReduced(arr, s, s', multistep.steps, multistep.tid, multistep.tau) } function GetRefinementViaReductionHSpec( arr:AtomicReductionRequest ) : AnnotatedBehaviorSpec> { AnnotatedBehaviorSpec( iset s | arr.l.init(s), iset s, s', multistep | RefinementViaReductionHSpecNext(arr, s, s', multistep) :: ActionTuple(s, s', multistep)) } function RefinementViaReductionIdmap( arr:AtomicReductionRequest, multistep:Armada_Multistep ) : Option { if multistep.tau then None else Some(multistep.tid) } function GetRefinementViaReductionIdmap( arr:AtomicReductionRequest ) : Armada_Multistep->Option { multistep => RefinementViaReductionIdmap(arr, multistep) } predicate RefinementViaReductionPhase1( arr:AtomicReductionRequest, s:LState, tid:Option ) { && tid.Some? && var pc := arr.l.get_thread_pc(s, tid.v); pc.Some? && arr.is_phase1(pc.v) } function GetRefinementViaReductionPhase1( arr:AtomicReductionRequest ) : (LState, Option)->bool { (s:LState, tid:Option) => RefinementViaReductionPhase1(arr, s, tid) } predicate RefinementViaReductionPhase2( arr:AtomicReductionRequest, s:LState, tid:Option ) { && tid.Some? && var pc := arr.l.get_thread_pc(s, tid.v); pc.Some? && arr.is_phase2(pc.v) } function GetRefinementViaReductionPhase2( arr:AtomicReductionRequest ) : (LState, Option)->bool { (s:LState, tid:Option) => RefinementViaReductionPhase2(arr, s, tid) } function GetRefinementViaReductionCrashedPred( arr:AtomicReductionRequest ) : LState->bool { (s:LState) => !arr.l.state_ok(s) } function GetRefinementViaReductionRequest( arr:AtomicReductionRequest ) : RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep> { RefinementViaReductionRequest(GetRefinementViaReductionLSpec(arr), GetRefinementViaReductionHSpec(arr), arr.self_relation, GetRefinementViaReductionIdmap(arr), GetRefinementViaReductionPhase1(arr), GetRefinementViaReductionPhase2(arr), GetRefinementViaReductionCrashedPred(arr)) } ////////////////////////////// // UTILITY PREDICATES ////////////////////////////// predicate IsPhase1( arr:AtomicReductionRequest, s:LState, tid:Armada_ThreadHandle ) { var pc := arr.l.get_thread_pc(s, tid); pc.Some? && arr.is_phase1(pc.v) } predicate IsPhase2( arr:AtomicReductionRequest, s:LState, tid:Armada_ThreadHandle ) { var pc := arr.l.get_thread_pc(s, tid); pc.Some? && arr.is_phase2(pc.v) } predicate IsNonyielding( arr:AtomicReductionRequest, s:LState, tid:Armada_ThreadHandle ) { var pc := arr.l.get_thread_pc(s, tid); pc.Some? && arr.l.is_pc_nonyielding(pc.v) } predicate PCTypesMatch( arr:AtomicReductionRequest, s:LState, s':LState, tid:Armada_ThreadHandle ) { && IsPhase1(arr, s', tid) == IsPhase1(arr, s, tid) && IsPhase2(arr, s', tid) == IsPhase2(arr, s, tid) && IsNonyielding(arr, s', tid) == IsNonyielding(arr, s, tid) } predicate OKAndPCTypesMatch( arr:AtomicReductionRequest, s:LState, s':LState, tid:Armada_ThreadHandle ) { && arr.l.state_ok(s') == arr.l.state_ok(s) && PCTypesMatch(arr, s, s', tid) } ////////////////////////////// // UTILITY LEMMAS ////////////////////////////// lemma lemma_SpecificSequenceCaseA(s1:seq, s2:seq, x:T, i:int) requires |s2| > 0 requires 0 <= i - |s1 + [x]| + 1 < |s2[1..]| ensures s2[1..][i - |s1 + [x]| + 1] == s2[i - |s1| + 1] { } lemma lemma_SpecificSequenceCaseB(s1:seq, x:T, i:int) requires |s1| > 0 requires i == |s1| - 1 ensures (s1 + [x])[i] == last(s1) { } lemma lemma_SpecificSequenceCaseC(s1:seq, s2:seq, s3:seq, i:int) requires |s1| > 0 requires |s3| > 0 requires |s1| <= i + 1 < |s1| + |s2| ensures s2[(i-|all_but_last(s1)|+1)-|[last(s3)]|] == s2[i-|s1|+1] { } lemma lemma_ConcatenationCommutesWithAllButLast(s1:seq, s2:seq) requires |s2| > 0 ensures s1 + all_but_last(s2) == all_but_last(s1 + s2) { } lemma lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath( asf: AtomicSpecFunctions, s: State, s': State, paths: seq, tid: Armada_ThreadHandle, states: seq, i: int ) requires AtomicValidPathSequenceOfAnyType(asf, s, paths, tid) requires 0 <= i < |paths| requires states == AtomicGetStateSequence(asf, s, paths, tid) ensures asf.path_valid(states[i], paths[i], tid) ensures states[i+1] == asf.path_next(states[i], paths[i], tid) { if i > 0 { var s_mid := asf.path_next(s, paths[0], tid); lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(asf, s_mid, s', paths[1..], tid, states[1..], i-1); } } lemma lemma_ExtendingStateSequenceWorks( asf:AtomicSpecFunctions, s:State, s':State, paths:seq, states:seq, tid:Armada_ThreadHandle, next_path:Path, next_state:State ) requires AtomicNextMultiplePaths(asf, s, s', paths, tid) requires states == AtomicGetStateSequence(asf, s, paths, tid) requires asf.path_valid(s', next_path, tid) requires next_state == asf.path_next(s', next_path, tid) ensures AtomicNextMultiplePaths(asf, s, next_state, paths + [next_path], tid) ensures states + [next_state] == AtomicGetStateSequence(asf, s, paths + [next_path], tid) decreases |paths| { if |paths| > 0 { var s_mid := asf.path_next(s, paths[0], tid); lemma_ExtendingStateSequenceWorks(asf, s_mid, s', paths[1..], states[1..], tid, next_path, next_state); assert paths + [next_path] == [paths[0]] + (paths[1..] + [next_path]); assert states + [next_state] == [states[0]] + (states[1..] + [next_state]); } } lemma lemma_AllButLastPreservesAtomicNextMultiplePaths( arr:AtomicReductionRequest, s:LState, s':LState, paths:seq, states:seq, tid:Armada_ThreadHandle ) requires ValidAtomicReductionRequest(arr) requires AtomicNextMultiplePaths(arr.l, s, s', paths, tid) requires states == AtomicGetStateSequence(arr.l, s, paths, tid) requires |paths| > 0 ensures all_but_last(states) == AtomicGetStateSequence(arr.l, s, all_but_last(paths), tid) ensures AtomicNextMultiplePaths(arr.l, s, states[|states|-2], all_but_last(paths), tid) { if |paths| > 1 { var s_mid := arr.l.path_next(s, paths[0], tid); lemma_AllButLastPreservesAtomicNextMultiplePaths(arr, s_mid, s', paths[1..], states[1..], tid); assert all_but_last(states[1..]) == AtomicGetStateSequence(arr.l, s_mid, all_but_last(paths[1..]), tid); assert AtomicNextMultiplePaths(arr.l, s_mid, states[1..][|states[1..]|-2], all_but_last(paths[1..]), tid); assert states[1..][|states[1..]|-2] == states[|states|-2]; var abl := [paths[0]] + all_but_last(paths[1..]); lemma_ConcatenationCommutesWithAllButLast([paths[0]], paths[1..]); lemma_SequenceIsCarPlusCdr(paths); assert abl == all_but_last(paths); calc { all_but_last(states); [states[0]] + all_but_last(states[1..]); [states[0]] + AtomicGetStateSequence(arr.l, s_mid, all_but_last(paths[1..]), tid); AtomicGetStateSequence(arr.l, s, [paths[0]] + all_but_last(paths[1..]), tid); AtomicGetStateSequence(arr.l, s, all_but_last(paths), tid); } } } lemma lemma_IfMultistepEndsInPhase1ThenEachPathDoes( arr:AtomicReductionRequest, s:LState, s':LState, multistep:Armada_Multistep, states:seq ) requires ValidAtomicReductionRequest(arr) requires !multistep.tau requires AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau) requires states == AtomicGetStateSequence(arr.l, s, multistep.steps, multistep.tid) requires IsPhase1(arr, s', multistep.tid) ensures forall s :: s in states ==> arr.l.get_thread_pc(s, multistep.tid).Some? ensures forall i :: 0 < i < |states| ==> arr.is_phase1(arr.l.get_thread_pc(states[i], multistep.tid).v) { var pos := |states|-1; lemma_AtomicNextLastElement(arr.l, s, s', multistep.steps, multistep.tid, states); while pos > 0 invariant 0 <= pos < |states| invariant arr.l.get_thread_pc(states[pos], multistep.tid).Some? invariant pos > 0 ==> arr.is_phase1(arr.l.get_thread_pc(states[pos], multistep.tid).v) invariant forall i :: pos <= i < |states| ==> arr.l.get_thread_pc(states[i], multistep.tid).Some? invariant forall i :: pos < i < |states| ==> arr.is_phase1(arr.l.get_thread_pc(states[i], multistep.tid).v) decreases pos { var prev_pos := pos-1; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, s, s', multistep.steps, multistep.tid, states, prev_pos); var pc := arr.l.get_thread_pc(states[prev_pos], multistep.tid); var pc' := arr.l.get_thread_pc(states[prev_pos+1], multistep.tid); assert pc'.Some? && arr.is_phase1(pc'.v); assert arr.l.path_valid(states[prev_pos], multistep.steps[prev_pos], multistep.tid); assert arr.l.state_ok(states[prev_pos]); assert states[prev_pos+1] == arr.l.path_next(states[prev_pos], multistep.steps[prev_pos], multistep.tid); assert pc.Some?; if prev_pos > 0 { assert arr.is_phase1(pc.v); assert IsPhase1(arr, states[prev_pos], multistep.tid); } pos := prev_pos; } } lemma lemma_IfMultistepStartsInPhase2ThenEachPathDoes( arr:AtomicReductionRequest, s:LState, s':LState, multistep:Armada_Multistep, states:seq ) requires ValidAtomicReductionRequest(arr) requires !multistep.tau requires AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau) requires states == AtomicGetStateSequence(arr.l, s, multistep.steps, multistep.tid) requires IsPhase2(arr, s, multistep.tid) ensures forall i :: 0 <= i < |states|-1 ==> IsPhase2(arr, states[i], multistep.tid) { if |states| == 1 { return; } var pos := 0; lemma_AtomicNextLastElement(arr.l, s, s', multistep.steps, multistep.tid, states); while pos < |states|-2 invariant 0 <= pos <= |states|-2 invariant arr.l.get_thread_pc(states[pos], multistep.tid).Some? invariant arr.is_phase2(arr.l.get_thread_pc(states[pos], multistep.tid).v) invariant forall i :: 0 <= i <= pos ==> arr.l.get_thread_pc(states[i], multistep.tid).Some? invariant forall i :: 0 <= i <= pos ==> arr.is_phase2(arr.l.get_thread_pc(states[i], multistep.tid).v) decreases |states|-pos { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, s, s', multistep.steps, multistep.tid, states, pos); var pc := arr.l.get_thread_pc(states[pos], multistep.tid); var pc' := arr.l.get_thread_pc(states[pos+1], multistep.tid); assert pc.Some? && arr.is_phase2(pc.v); assert arr.l.path_valid(states[pos], multistep.steps[pos], multistep.tid); assert states[pos+1] == arr.l.path_next(states[pos], multistep.steps[pos], multistep.tid); var next_pos := pos+1; assert 0 < next_pos < |multistep.steps|; assert arr.l.get_thread_pc(states[next_pos], multistep.tid).Some? && arr.l.is_pc_nonyielding(arr.l.get_thread_pc(states[next_pos], multistep.tid).v); assert pc'.Some?; assert arr.is_phase2(pc'.v); pos := pos + 1; } } lemma lemma_TakingAtomicMultistepKeepsThreadYielding( asf:AtomicSpecFunctions, s:State, s':State, multistep:Armada_Multistep, tid:Armada_ThreadHandle ) requires AtomicTauLeavesPCUnchanged(asf) requires AtomicThreadCantAffectOtherThreadPCExceptViaFork(asf) requires AtomicPathRequiresOK(asf) requires AtomicNextMultistep(asf, s, s', multistep.steps, multistep.tid, multistep.tau) requires asf.state_ok(s) ==> AtomicThreadYielding(asf, s, tid) ensures asf.state_ok(s') ==> AtomicThreadYielding(asf, s', tid) { var pc' := asf.get_thread_pc(s', tid); var states := AtomicGetStateSequence(asf, s, multistep.steps, multistep.tid); lemma_AtomicNextLastElement(asf, s, s', multistep.steps, multistep.tid, states); if multistep.tau { return; } if multistep.tid == tid { return; } var pos := 0; while pos < |multistep.steps| invariant 0 <= pos <= |multistep.steps| invariant asf.state_ok(states[pos]) ==> AtomicThreadYielding(asf, states[pos], tid) decreases |multistep.steps|-pos { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(asf, s, s', multistep.steps, multistep.tid, states, pos); pos := pos + 1; } } lemma lemma_StateAmongAnnotatedReachablesHasThreadYielding( arr:AtomicReductionRequest, s:LState, tid:Armada_ThreadHandle ) requires ValidAtomicReductionRequest(arr) requires s in AnnotatedReachables(GetRefinementViaReductionLSpec(arr)) ensures arr.l.state_ok(s) ==> AtomicThreadYielding(arr.l, s, tid) { var rspec := GetRefinementViaReductionLSpec(arr); reveal_AnnotatedReachables(); var ab :| AnnotatedBehaviorSatisfiesSpec(ab, rspec) && last(ab.states) == s; var pos := 0; while pos < |ab.states|-1 invariant 0 <= pos < |ab.states| invariant arr.l.state_ok(ab.states[pos]) ==> AtomicThreadYielding(arr.l, ab.states[pos], tid) decreases |ab.states|-pos { var subpos := 0; var s := ab.states[pos]; var s' := ab.states[pos+1]; var path := ab.trace[pos]; assert ActionTuple(ab.states[pos], ab.states[pos+1], ab.trace[pos]) in rspec.next; lemma_TakingAtomicMultistepKeepsThreadYielding(arr.l, ab.states[pos], ab.states[pos+1], ab.trace[pos], tid); pos := pos + 1; } } lemma lemma_AtomicNextMultiplePathsTransitive( arr:AtomicReductionRequest, s1:LState, s2:LState, s3:LState, paths1:seq, paths2:seq, tid:Armada_ThreadHandle ) requires AtomicNextMultiplePaths(arr.l, s1, s2, paths1, tid) requires AtomicNextMultiplePaths(arr.l, s2, s3, paths2, tid) ensures AtomicNextMultiplePaths(arr.l, s1, s3, paths1 + paths2, tid) { if |paths1| > 0 { var s_mid := arr.l.path_next(s1, paths1[0], tid); lemma_AtomicNextMultiplePathsTransitive(arr, s_mid, s2, s3, paths1[1..], paths2, tid); assert AtomicNextMultiplePaths(arr.l, s_mid, s3, paths1[1..] + paths2, tid); var paths3 := paths1 + paths2; assert paths3[0] == paths1[0]; assert paths3[1..] == paths1[1..] + paths2; } else { assert s1 == s2; assert paths1 + paths2 == paths2; } } lemma lemma_ExecutingPathDoesntChangeOtherActorPCType( arr:AtomicReductionRequest, s:LState, s':LState, path:LPath, tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) requires ValidAtomicReductionRequest(arr) requires arr.l.path_valid(s, path, tid) requires s' == arr.l.path_next(s, path, tid) requires tid != other_tid || arr.l.path_type(path).AtomicPathType_Tau? ensures PCTypesMatch(arr, s, s', other_tid) { } lemma lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType( arr:AtomicReductionRequest, s:LState, s':LState, paths:seq, tau:bool, tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) requires ValidAtomicReductionRequest(arr) requires AtomicNextMultiplePaths(arr.l, s, s', paths, tid) requires forall path :: path in paths ==> arr.l.path_type(path).AtomicPathType_Tau? == tau requires tau || tid != other_tid ensures PCTypesMatch(arr, s, s', other_tid) decreases |paths| { if |paths| > 0 { var s_mid := arr.l.path_next(s, paths[0], tid); lemma_ExecutingPathDoesntChangeOtherActorPCType(arr, s, s_mid, paths[0], tid, other_tid); lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType(arr, s_mid, s', paths[1..], tau, tid, other_tid); } } lemma lemma_ExecutingPathDoesntRemoveOtherActorPC( arr:AtomicReductionRequest, s:LState, s':LState, path:LPath, tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) requires ValidAtomicReductionRequest(arr) requires arr.l.path_valid(s, path, tid) requires s' == arr.l.path_next(s, path, tid) requires tid != other_tid || arr.l.path_type(path).AtomicPathType_Tau? ensures arr.l.get_thread_pc(s, other_tid).Some? ==> arr.l.get_thread_pc(s', other_tid).Some? { } lemma lemma_ExecutingPathSequenceDoesntRemoveOtherActorPC( arr:AtomicReductionRequest, s:LState, s':LState, paths:seq, tau:bool, tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) requires ValidAtomicReductionRequest(arr) requires AtomicNextMultiplePaths(arr.l, s, s', paths, tid) requires forall path :: path in paths ==> arr.l.path_type(path).AtomicPathType_Tau? == tau requires tau || tid != other_tid ensures arr.l.get_thread_pc(s, other_tid).Some? ==> arr.l.get_thread_pc(s', other_tid).Some? decreases |paths| { if |paths| > 0 { var s_mid := arr.l.path_next(s, paths[0], tid); lemma_ExecutingPathDoesntRemoveOtherActorPC(arr, s, s_mid, paths[0], tid, other_tid); lemma_ExecutingPathSequenceDoesntRemoveOtherActorPC(arr, s_mid, s', paths[1..], tau, tid, other_tid); } } lemma lemma_StateAmongAnnotatedReachablesSatisfiesInv( arr:AtomicReductionRequest, s:LState ) requires ValidAtomicReductionRequest(arr) requires s in AnnotatedReachables(GetRefinementViaReductionLSpec(arr)) ensures arr.inv(s) { var rspec := GetRefinementViaReductionLSpec(arr); reveal_AnnotatedReachables(); var ab :| AnnotatedBehaviorSatisfiesSpec(ab, rspec) && last(ab.states) == s; var pos := 0; while pos < |ab.states|-1 invariant 0 <= pos < |ab.states| invariant arr.inv(ab.states[pos]) decreases |ab.states|-pos { var subpos := 0; var s := ab.states[pos]; var s' := ab.states[pos+1]; var multistep := ab.trace[pos]; assert ActionTuple(ab.states[pos], ab.states[pos+1], ab.trace[pos]) in rspec.next; assert AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau); var states := AtomicGetStateSequence(arr.l, s, multistep.steps, multistep.tid); while subpos < |multistep.steps| invariant 0 <= subpos <= |multistep.steps| invariant arr.inv(states[subpos]) decreases |multistep.steps|-subpos { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, s, s', multistep.steps, multistep.tid, states, subpos); subpos := subpos + 1; } lemma_AtomicNextLastElement(arr.l, s, s', multistep.steps, multistep.tid, states); pos := pos+1; } } lemma lemma_AtomicInvariantHoldsAtIntermediateState( asf:AtomicSpecFunctions, inv:State->bool, s:State, s':State, paths:seq, tid:Armada_ThreadHandle, states:seq, pos:int ) requires AtomicPathPreservesInv(asf, inv) requires AtomicNextMultiplePaths(asf, s, s', paths, tid) requires states == AtomicGetStateSequence(asf, s, paths, tid) requires 0 <= pos < |states| requires inv(states[0]) ensures inv(states[pos]) decreases pos { if pos > 0 { var s_mid := asf.path_next(s, paths[0], tid); lemma_AtomicInvariantHoldsAtIntermediateState(asf, inv, s_mid, s', paths[1..], tid, states[1..], pos-1); } } ////////////////////////////// // LEMMA-SPECIFIC PREDICATES ////////////////////////////// predicate PerformRightMoveTrigger(i:int) { true } predicate PerformRightMoveThroughPredicate( arr:AtomicReductionRequest, other_tid:Armada_ThreadHandle, other_states_before:seq, other_states_after:seq, other_states':seq ) requires |other_states_before| > 0 requires |other_states_after| > 0 requires |other_states'| == |other_states_before| + |other_states_after| - 1 { && (forall i {:trigger PerformRightMoveTrigger(i)} :: PerformRightMoveTrigger(i) && 0 <= i < |other_states_before| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states_before[i], other_tid)) && (forall i {:trigger PerformRightMoveTrigger(i)} :: PerformRightMoveTrigger(i) && |other_states_before|-1 <= i < |other_states'| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states_after[i-|other_states_before|+1], other_tid)) } predicate PerformRightMoveRemainingPredicate( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, mover_states_before:seq, mover_states_after:seq, mover_states':seq ) requires |mover_states_before| > 0 requires |mover_states_after| > 0 requires |mover_states'| == |mover_states_before| + |mover_states_after| - 1 { && (forall i {:trigger PerformRightMoveTrigger(i)} :: PerformRightMoveTrigger(i) && 0 <= i < |mover_states_before| ==> OKAndPCTypesMatch(arr, mover_states'[i], mover_states_before[i], mover_tid)) && (forall i {:trigger PerformRightMoveTrigger(i)} :: PerformRightMoveTrigger(i) && |mover_states_before|-1 <= i < |mover_states'| ==> OKAndPCTypesMatch(arr, mover_states'[i], mover_states_after[i-|mover_states_before|+1], mover_tid)) } ////////////////////////////////////////// // LEMMAS SUPPORTING PERFORM-RIGHT-MOVE ////////////////////////////////////////// lemma lemma_PerformRightMoveSingle( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, s1:LState, s2:LState, s3:LState, mover_path:LPath, other_path:LPath ) returns ( s2':LState ) requires ValidAtomicReductionRequest(arr) requires other_tau || other_tid != mover_tid requires arr.inv(s1) requires arr.l.state_ok(s3) requires arr.l.path_type(other_path).AtomicPathType_Tau? == other_tau requires !arr.l.path_type(mover_path).AtomicPathType_Tau? requires arr.l.path_valid(s1, mover_path, mover_tid) requires s2 == arr.l.path_next(s1, mover_path, mover_tid) requires arr.l.path_valid(s2, other_path, other_tid) requires s3 == arr.l.path_next(s2, other_path, other_tid) requires arr.l.get_thread_pc(s1, mover_tid).Some? requires arr.l.get_thread_pc(s2, mover_tid).Some? requires arr.is_phase1(arr.l.get_thread_pc(s2, mover_tid).v) ensures arr.l.path_valid(s1, other_path, other_tid) ensures s2' == arr.l.path_next(s1, other_path, other_tid) ensures arr.l.path_valid(s2', mover_path, mover_tid) ensures s3 == arr.l.path_next(s2', mover_path, mover_tid) ensures OKAndPCTypesMatch(arr, s2', s1, mover_tid) { assert AtomicReductionSpecModule.RightMoversCommuteConditions(arr, s1, mover_path, other_path, mover_tid, other_tid); s2' := arr.l.path_next(s1, other_path, other_tid); } lemma lemma_PerformRightMoveThroughHelper( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_path:LPath, other_paths_before:seq, other_states_before:seq, other_paths_after:seq, other_states_after:seq, s2':LState, other_states':seq ) requires ValidAtomicReductionRequest(arr) requires other_tau || other_tid != mover_tid requires |other_states_before| > 0 requires arr.inv(other_states_before[0]) requires other_states_before == AtomicGetStateSequence(arr.l, other_states_before[0], other_paths_before, other_tid) requires forall path :: path in other_paths_before ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires AtomicNextMultiplePaths(arr.l, other_states_before[0], last(other_states_before), other_paths_before, other_tid) requires |other_states_after| > 0 requires arr.l.state_ok(last(other_states_after)) requires other_states_after == AtomicGetStateSequence(arr.l, other_states_after[0], other_paths_after, other_tid) requires forall path :: path in other_paths_after ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires AtomicNextMultiplePaths(arr.l, other_states_after[0], last(other_states_after), other_paths_after, other_tid) requires arr.l.path_valid(last(other_states_before), mover_path, mover_tid) requires other_states_after[0] == arr.l.path_next(last(other_states_before), mover_path, mover_tid) requires !arr.l.path_type(mover_path).AtomicPathType_Tau? requires arr.l.get_thread_pc(other_states_before[0], mover_tid).Some? requires arr.l.get_thread_pc(other_states_after[0], mover_tid).Some? requires arr.is_phase1(arr.l.get_thread_pc(other_states_after[0], mover_tid).v) requires |other_paths_after| > 0 requires |other_states_after| > 1 requires arr.l.path_valid(last(other_states_before), other_paths_after[0], other_tid) requires s2' == arr.l.path_next(last(other_states_before), other_paths_after[0], other_tid) requires arr.l.path_valid(s2', mover_path, mover_tid) requires other_states_after[1] == arr.l.path_next(s2', mover_path, mover_tid) requires OKAndPCTypesMatch(arr, s2', last(other_states_before), mover_tid) requires |other_states'| == |(other_states_before + [s2'])| + |other_states_after[1..]| - 1 requires other_states'[0] == (other_states_before + [s2'])[0] requires other_states' == AtomicGetStateSequence(arr.l, other_states'[0], (other_paths_before + [other_paths_after[0]]) + other_paths_after[1..], other_tid) requires AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), (other_paths_before + [other_paths_after[0]]) + other_paths_after[1..], other_tid) requires !other_tau ==> PerformRightMoveThroughPredicate(arr, other_tid, other_states_before + [s2'], other_states_after[1..], other_states') requires OKAndPCTypesMatch(arr, last(other_states'), last((other_states_before + [s2'])), mover_tid) requires arr.l.path_valid(last(other_states'), mover_path, mover_tid) requires last(other_states_after[1..]) == arr.l.path_next(last(other_states'), mover_path, mover_tid) ensures |other_states'| == |other_states_before| + |other_states_after| - 1 ensures other_states'[0] == other_states_before[0] ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths_before + other_paths_after, other_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths_before + other_paths_after, other_tid) ensures !other_tau ==> PerformRightMoveThroughPredicate(arr, other_tid, other_states_before, other_states_after, other_states') ensures OKAndPCTypesMatch(arr, last(other_states'), last(other_states_before), mover_tid) ensures arr.l.path_valid(last(other_states'), mover_path, mover_tid) ensures last(other_states_after) == arr.l.path_next(last(other_states'), mover_path, mover_tid) { calc { other_paths_before + other_paths_after; { lemma_SequenceIsCarPlusCdr(other_paths_after); } other_paths_before + ([other_paths_after[0]] + other_paths_after[1..]); { lemma_SequenceConcatenationAssociative(other_paths_before, [other_paths_after[0]], other_paths_after[1..]); } (other_paths_before + [other_paths_after[0]]) + other_paths_after[1..]; } if !other_tau { forall i | |other_states_before|-1 <= i < |other_states'| ensures OKAndPCTypesMatch(arr, other_states'[i], other_states_after[i-|other_states_before|+1], other_tid) { if i >= |other_states_before + [s2']|-1 { assert PerformRightMoveTrigger(i); assert OKAndPCTypesMatch(arr, other_states'[i], other_states_after[1..][i-|other_states_before + [s2']|+1], other_tid); calc { arr.l.get_thread_pc(other_states_after[1..][i-|other_states_before + [s2']|+1], other_tid); { lemma_SpecificSequenceCaseA(other_states_before, other_states_after, s2', i); } arr.l.get_thread_pc(other_states_after[i-|other_states_before|+1], other_tid); } assert OKAndPCTypesMatch(arr, other_states'[i], other_states_after[i-|other_states_before|+1], other_tid); } else { assert i == |other_states_before|-1; assert i < |other_states_before + [s2']|-1; assert PerformRightMoveTrigger(i); assert OKAndPCTypesMatch(arr, other_states'[i], (other_states_before + [s2'])[i], other_tid); calc { arr.l.get_thread_pc((other_states_before + [s2'])[i], other_tid); { lemma_SpecificSequenceCaseB(other_states_before, s2', i); } arr.l.get_thread_pc(last(other_states_before), other_tid); } lemma_ExecutingPathDoesntChangeOtherActorPCType(arr, last(other_states_before), other_states_after[0], mover_path, mover_tid, other_tid); calc { arr.l.get_thread_pc(other_states_after[0], other_tid); { assert i - |other_states_before| + 1 == 0; } arr.l.get_thread_pc(other_states_after[i-|other_states_before|+1], other_tid); } assert OKAndPCTypesMatch(arr, other_states'[i], other_states_after[i-|other_states_before|+1], other_tid); } } } } lemma lemma_PerformRightMoveThrough( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_path:LPath, other_paths_before:seq, other_states_before:seq, other_paths_after:seq, other_states_after:seq ) returns ( other_states':seq ) requires ValidAtomicReductionRequest(arr) requires other_tau || other_tid != mover_tid requires |other_states_before| > 0 requires arr.inv(other_states_before[0]) requires other_states_before == AtomicGetStateSequence(arr.l, other_states_before[0], other_paths_before, other_tid) requires forall path :: path in other_paths_before ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires AtomicNextMultiplePaths(arr.l, other_states_before[0], last(other_states_before), other_paths_before, other_tid) requires |other_states_after| > 0 requires arr.l.state_ok(last(other_states_after)) requires other_states_after == AtomicGetStateSequence(arr.l, other_states_after[0], other_paths_after, other_tid) requires forall path :: path in other_paths_after ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires AtomicNextMultiplePaths(arr.l, other_states_after[0], last(other_states_after), other_paths_after, other_tid) requires arr.l.path_valid(last(other_states_before), mover_path, mover_tid) requires other_states_after[0] == arr.l.path_next(last(other_states_before), mover_path, mover_tid) requires !arr.l.path_type(mover_path).AtomicPathType_Tau? requires arr.l.get_thread_pc(other_states_before[0], mover_tid).Some? requires arr.l.get_thread_pc(other_states_after[0], mover_tid).Some? requires arr.is_phase1(arr.l.get_thread_pc(other_states_after[0], mover_tid).v) ensures |other_states'| == |other_states_before| + |other_states_after| - 1 ensures other_states'[0] == other_states_before[0] ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths_before + other_paths_after, other_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths_before + other_paths_after, other_tid) ensures !other_tau ==> PerformRightMoveThroughPredicate(arr, other_tid, other_states_before, other_states_after, other_states') ensures OKAndPCTypesMatch(arr, last(other_states'), last(other_states_before), mover_tid) ensures arr.l.path_valid(last(other_states'), mover_path, mover_tid) ensures last(other_states_after) == arr.l.path_next(last(other_states'), mover_path, mover_tid) decreases |other_paths_after| { lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType(arr, other_states_before[0], last(other_states_before), other_paths_before, other_tau, other_tid, mover_tid); if |other_paths_after| == 0 { other_states' := other_states_before; assert other_paths_before + other_paths_after == other_paths_before; if !other_tau { forall i | |other_states_before|-1 <= i < |other_states'| ensures OKAndPCTypesMatch(arr, other_states'[i], other_states_after[i-|other_states_before|+1], other_tid) { assert i == |other_states_before|-1; assert other_states'[i] == last(other_states_before); assert other_states_after[i-|other_states_before|+1] == other_states_after[0]; lemma_ExecutingPathDoesntChangeOtherActorPCType(arr, last(other_states_before), other_states_after[0], mover_path, mover_tid, other_tid); } } } else { lemma_AtomicInvariantHoldsAtIntermediateState(arr.l, arr.inv, other_states_before[0], last(other_states_before), other_paths_before, other_tid, other_states_before, |other_states_before|-1); forall ensures arr.l.state_ok(other_states_after[1]) { if |other_paths_after| == 1 { assert other_states_after[1] == last(other_states_after); } else { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, other_states_after[0], last(other_states_after), other_paths_after, other_tid, other_states_after, 1); } } var s2' := lemma_PerformRightMoveSingle(arr, mover_tid, other_tid, other_tau, last(other_states_before), other_states_after[0], other_states_after[1], mover_path, other_paths_after[0]); lemma_ExtendingStateSequenceWorks(arr.l, other_states_before[0], last(other_states_before), other_paths_before, other_states_before, other_tid, other_paths_after[0], s2'); other_states' := lemma_PerformRightMoveThrough(arr, mover_tid, other_tid, other_tau, mover_path, other_paths_before + [other_paths_after[0]], other_states_before + [s2'], other_paths_after[1..], other_states_after[1..]); lemma_PerformRightMoveThroughHelper(arr, mover_tid, other_tid, other_tau, mover_path, other_paths_before, other_states_before, other_paths_after, other_states_after, s2', other_states'); } } lemma lemma_PerformRightMoveOne( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_path:LPath, initial_state:LState, other_paths:seq, other_states:seq ) returns ( other_states':seq ) requires ValidAtomicReductionRequest(arr) requires other_tau || other_tid != mover_tid requires arr.inv(initial_state) requires |other_states| > 0 requires arr.l.state_ok(last(other_states)) requires IsPhase1(arr, other_states[0], mover_tid) requires arr.l.path_valid(initial_state, mover_path, mover_tid) requires other_states[0] == arr.l.path_next(initial_state, mover_path, mover_tid) requires !arr.l.path_type(mover_path).AtomicPathType_Tau? requires other_states == AtomicGetStateSequence(arr.l, other_states[0], other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires AtomicNextMultiplePaths(arr.l, other_states[0], last(other_states), other_paths, other_tid) ensures |other_states'| == |other_states| ensures other_states'[0] == initial_state ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) ensures !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) ensures OKAndPCTypesMatch(arr, last(other_states'), initial_state, mover_tid) ensures arr.l.path_valid(last(other_states'), mover_path, mover_tid) ensures last(other_states) == arr.l.path_next(last(other_states'), mover_path, mover_tid) { other_states' := lemma_PerformRightMoveThrough(arr, mover_tid, other_tid, other_tau, mover_path, [], [initial_state], other_paths, other_states); assert [] + other_paths == other_paths; if !other_tau { forall i | 0 <= i < |other_states| ensures OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) { assert PerformRightMoveTrigger(i); } } } lemma lemma_PerformRightMoveRemainingHelper1( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_paths_before:seq, mover_states_before:seq, mover_paths_after:seq, mover_states_after:seq, other_paths:seq, other_states:seq, other_states_mid:seq, mover_states':seq, other_states':seq ) requires ValidAtomicReductionRequest(arr) requires other_tau || other_tid != mover_tid requires |mover_states_before| > 0 requires arr.inv(mover_states_before[0]) requires arr.l.get_thread_pc(mover_states_before[0], mover_tid).Some? requires mover_states_before == AtomicGetStateSequence(arr.l, mover_states_before[0], mover_paths_before, mover_tid) requires forall s :: s in mover_states_before ==> arr.l.get_thread_pc(s, mover_tid).Some? requires forall i :: 0 < i < |mover_states_before| ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_before[i], mover_tid).v) requires AtomicNextMultiplePaths(arr.l, mover_states_before[0], last(mover_states_before), mover_paths_before, mover_tid) requires forall path :: path in mover_paths_before ==> !arr.l.path_type(path).AtomicPathType_Tau? requires |mover_states_after| > 0 requires mover_states_after == AtomicGetStateSequence(arr.l, mover_states_after[0], mover_paths_after, mover_tid) requires forall s :: s in mover_states_after ==> arr.l.get_thread_pc(s, mover_tid).Some? requires forall i :: 0 < i < |mover_states_after| ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_after[i], mover_tid).v) requires |mover_paths_before| > 0 ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_after[0], mover_tid).v) requires AtomicNextMultiplePaths(arr.l, mover_states_after[0], last(mover_states_after), mover_paths_after, mover_tid) requires forall path :: path in mover_paths_after ==> !arr.l.path_type(path).AtomicPathType_Tau? requires |other_states| > 0 requires other_states[0] == last(mover_states_before) requires last(other_states) == mover_states_after[0] requires other_states == AtomicGetStateSequence(arr.l, other_states[0], other_paths, other_tid) requires AtomicNextMultiplePaths(arr.l, other_states[0], last(other_states), other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires |mover_paths_before| > 0 requires |other_states_mid| == |other_states| requires other_states_mid[0] == mover_states_before[|mover_paths_before|-1] requires other_states_mid == AtomicGetStateSequence(arr.l, other_states_mid[0], other_paths, other_tid) requires AtomicNextMultiplePaths(arr.l, other_states_mid[0], last(other_states_mid), other_paths, other_tid) requires !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states_mid[i], other_states[i], other_tid) requires |other_states'| == |other_states_mid| requires other_states'[0] == all_but_last(mover_states_before)[0] requires other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) requires AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) requires !other_tau ==> forall i :: 0 <= i < |other_states_mid| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states_mid[i], other_tid) requires |mover_states'| == |all_but_last(mover_states_before)| + |([last(other_states_mid)] + mover_states_after)| - 1 requires mover_states'[0] == last(other_states') requires last(mover_states') == last(([last(other_states_mid)] + mover_states_after)) requires mover_states' == AtomicGetStateSequence(arr.l, mover_states'[0], all_but_last(mover_paths_before) + ([last(mover_paths_before)] + mover_paths_after), mover_tid) requires AtomicNextMultiplePaths(arr.l, mover_states'[0], last(mover_states'), all_but_last(mover_paths_before) + ([last(mover_paths_before)] + mover_paths_after), mover_tid) ensures |other_states'| == |other_states| ensures other_states'[0] == mover_states_before[0] ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) ensures !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) ensures |mover_states'| == |mover_states_before| + |mover_states_after| - 1 ensures mover_states'[0] == last(other_states') ensures last(mover_states') == last(mover_states_after) ensures mover_states' == AtomicGetStateSequence(arr.l, mover_states'[0], mover_paths_before + mover_paths_after, mover_tid) ensures AtomicNextMultiplePaths(arr.l, mover_states'[0], last(mover_states'), mover_paths_before + mover_paths_after, mover_tid) { calc { all_but_last(mover_paths_before) + ([last(mover_paths_before)] + mover_paths_after); { lemma_SequenceConcatenationAssociative(all_but_last(mover_paths_before), [last(mover_paths_before)], mover_paths_after); } (all_but_last(mover_paths_before) + [last(mover_paths_before)]) + mover_paths_after; { lemma_AllButLastPlusLastIsSeq(mover_paths_before); } mover_paths_before + mover_paths_after; } lemma_LastOfConcatenationIsLastOfLatter([last(other_states_mid)], mover_states_after); } lemma lemma_PerformRightMoveRemainingHelper2( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_paths_before:seq, mover_states_before:seq, mover_paths_after:seq, mover_states_after:seq, other_paths:seq, other_states:seq, other_states_mid:seq, mover_states':seq, other_states':seq ) requires ValidAtomicReductionRequest(arr) requires other_tau || other_tid != mover_tid requires |mover_states_before| > 0 requires arr.inv(mover_states_before[0]) requires arr.l.get_thread_pc(mover_states_before[0], mover_tid).Some? requires mover_states_before == AtomicGetStateSequence(arr.l, mover_states_before[0], mover_paths_before, mover_tid) requires forall s :: s in mover_states_before ==> arr.l.get_thread_pc(s, mover_tid).Some? requires forall i :: 0 < i < |mover_states_before| ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_before[i], mover_tid).v) requires AtomicNextMultiplePaths(arr.l, mover_states_before[0], last(mover_states_before), mover_paths_before, mover_tid) requires forall path :: path in mover_paths_before ==> !arr.l.path_type(path).AtomicPathType_Tau? requires |mover_states_after| > 0 requires mover_states_after == AtomicGetStateSequence(arr.l, mover_states_after[0], mover_paths_after, mover_tid) requires forall s :: s in mover_states_after ==> arr.l.get_thread_pc(s, mover_tid).Some? requires forall i :: 0 < i < |mover_states_after| ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_after[i], mover_tid).v) requires |mover_paths_before| > 0 ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_after[0], mover_tid).v) requires AtomicNextMultiplePaths(arr.l, mover_states_after[0], last(mover_states_after), mover_paths_after, mover_tid) requires forall path :: path in mover_paths_after ==> !arr.l.path_type(path).AtomicPathType_Tau? requires |other_states| > 0 requires arr.l.state_ok(last(other_states)) requires other_states[0] == last(mover_states_before) requires last(other_states) == mover_states_after[0] requires other_states == AtomicGetStateSequence(arr.l, other_states[0], other_paths, other_tid) requires AtomicNextMultiplePaths(arr.l, other_states[0], last(other_states), other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires |mover_paths_before| > 0 requires |other_states_mid| == |other_states| requires other_states_mid[0] == mover_states_before[|mover_paths_before|-1] requires other_states_mid == AtomicGetStateSequence(arr.l, other_states_mid[0], other_paths, other_tid) requires AtomicNextMultiplePaths(arr.l, other_states_mid[0], last(other_states_mid), other_paths, other_tid) requires !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states_mid[i], other_states[i], other_tid) requires |other_states'| == |other_states_mid| requires other_states'[0] == all_but_last(mover_states_before)[0] requires other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) requires AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) requires !other_tau ==> forall i :: 0 <= i < |other_states_mid| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states_mid[i], other_tid) requires |mover_states'| == |all_but_last(mover_states_before)| + |([last(other_states_mid)] + mover_states_after)| - 1 requires mover_states'[0] == last(other_states') requires last(mover_states') == last(([last(other_states_mid)] + mover_states_after)) requires mover_states' == AtomicGetStateSequence(arr.l, mover_states'[0], all_but_last(mover_paths_before) + ([last(mover_paths_before)] + mover_paths_after), mover_tid) requires AtomicNextMultiplePaths(arr.l, mover_states'[0], last(mover_states'), all_but_last(mover_paths_before) + ([last(mover_paths_before)] + mover_paths_after), mover_tid) requires PerformRightMoveRemainingPredicate(arr, mover_tid, all_but_last(mover_states_before), [last(other_states_mid)] + mover_states_after, mover_states') ensures PerformRightMoveRemainingPredicate(arr, mover_tid, mover_states_before, mover_states_after, mover_states') { forall i | 0 <= i < |mover_states_before| ensures OKAndPCTypesMatch(arr, mover_states'[i], mover_states_before[i], mover_tid) { assert PerformRightMoveTrigger(i); if i < |all_but_last(mover_states_before)| { assert OKAndPCTypesMatch(arr, mover_states'[i], all_but_last(mover_states_before)[i], mover_tid); calc { arr.l.get_thread_pc(all_but_last(mover_states_before)[i], mover_tid); arr.l.get_thread_pc(mover_states_before[i], mover_tid); } } else { assert i == |all_but_last(mover_states_before)|; assert |all_but_last(mover_states_before)|-1 <= i < |mover_states'|; assert OKAndPCTypesMatch(arr, mover_states'[i], ([last(other_states_mid)] + mover_states_after)[i-|all_but_last(mover_states_before)|+1], mover_tid); calc { arr.l.get_thread_pc(([last(other_states_mid)] + mover_states_after)[i-|all_but_last(mover_states_before)|+1], mover_tid); arr.l.get_thread_pc(([last(other_states_mid)] + mover_states_after)[1], mover_tid); arr.l.get_thread_pc(mover_states_after[0], mover_tid); } lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType(arr, last(mover_states_before), mover_states_after[0], other_paths, other_tau, other_tid, mover_tid); assert last(mover_states_before) == mover_states_before[i]; } } forall i | |mover_states_before|-1 <= i < |mover_states'| ensures OKAndPCTypesMatch(arr, mover_states'[i], mover_states_after[i-|mover_states_before|+1], mover_tid) { assert PerformRightMoveTrigger(i); assert |all_but_last(mover_states_before)|-1 <= i < |mover_states'|; assert OKAndPCTypesMatch(arr, mover_states'[i], ([last(other_states_mid)] + mover_states_after)[i-|all_but_last(mover_states_before)|+1], mover_tid); calc { arr.l.get_thread_pc(([last(other_states_mid)] + mover_states_after)[i-|all_but_last(mover_states_before)|+1], mover_tid); { lemma_IndexIntoConcatenation([last(other_states_mid)], mover_states_after, i-|all_but_last(mover_states_before)|+1); } arr.l.get_thread_pc(mover_states_after[(i-|all_but_last(mover_states_before)|+1)-|[last(other_states_mid)]|], mover_tid); { lemma_SpecificSequenceCaseC(mover_states_before, mover_states_after, other_states_mid, i); } arr.l.get_thread_pc(mover_states_after[i-|mover_states_before|+1], mover_tid); } } } lemma lemma_PerformRightMoveRemaining( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_paths_before:seq, mover_states_before:seq, mover_paths_after:seq, mover_states_after:seq, other_paths:seq, other_states:seq ) returns ( mover_states':seq, other_states':seq ) requires ValidAtomicReductionRequest(arr) requires other_tau || other_tid != mover_tid requires |mover_states_before| > 0 requires arr.inv(mover_states_before[0]) requires arr.l.get_thread_pc(mover_states_before[0], mover_tid).Some? requires mover_states_before == AtomicGetStateSequence(arr.l, mover_states_before[0], mover_paths_before, mover_tid) requires forall s :: s in mover_states_before ==> arr.l.get_thread_pc(s, mover_tid).Some? requires forall i :: 0 < i < |mover_states_before| ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_before[i], mover_tid).v) requires AtomicNextMultiplePaths(arr.l, mover_states_before[0], last(mover_states_before), mover_paths_before, mover_tid) requires forall path :: path in mover_paths_before ==> !arr.l.path_type(path).AtomicPathType_Tau? requires |mover_states_after| > 0 requires mover_states_after == AtomicGetStateSequence(arr.l, mover_states_after[0], mover_paths_after, mover_tid) requires forall s :: s in mover_states_after ==> arr.l.get_thread_pc(s, mover_tid).Some? requires forall i :: 0 < i < |mover_states_after| ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_after[i], mover_tid).v) requires |mover_paths_before| > 0 ==> arr.is_phase1(arr.l.get_thread_pc(mover_states_after[0], mover_tid).v) requires AtomicNextMultiplePaths(arr.l, mover_states_after[0], last(mover_states_after), mover_paths_after, mover_tid) requires forall path :: path in mover_paths_after ==> !arr.l.path_type(path).AtomicPathType_Tau? requires |other_states| > 0 requires arr.l.state_ok(last(other_states)) requires other_states[0] == last(mover_states_before) requires last(other_states) == mover_states_after[0] requires other_states == AtomicGetStateSequence(arr.l, other_states[0], other_paths, other_tid) requires AtomicNextMultiplePaths(arr.l, other_states[0], last(other_states), other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau ensures |other_states'| == |other_states| ensures other_states'[0] == mover_states_before[0] ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) ensures !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) ensures |mover_states'| == |mover_states_before| + |mover_states_after| - 1 ensures mover_states'[0] == last(other_states') ensures last(mover_states') == last(mover_states_after) ensures mover_states' == AtomicGetStateSequence(arr.l, mover_states'[0], mover_paths_before + mover_paths_after, mover_tid) ensures AtomicNextMultiplePaths(arr.l, mover_states'[0], last(mover_states'), mover_paths_before + mover_paths_after, mover_tid) ensures PerformRightMoveRemainingPredicate(arr, mover_tid, mover_states_before, mover_states_after, mover_states') decreases |mover_paths_before| { lemma_ExecutingPathSequenceDoesntRemoveOtherActorPC(arr, other_states[0], last(other_states), other_paths, other_tau, other_tid, mover_tid); lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType(arr, other_states[0], last(other_states), other_paths, other_tau, other_tid, mover_tid); if |mover_paths_before| == 0 { mover_states' := mover_states_after; other_states' := other_states; assert mover_paths_before + mover_paths_after == mover_paths_after; assert PerformRightMoveRemainingPredicate(arr, mover_tid, mover_states_before, mover_states_after, mover_states'); } else { lemma_AtomicInvariantHoldsAtIntermediateState(arr.l, arr.inv, mover_states_before[0], last(mover_states_before), mover_paths_before, mover_tid, mover_states_before, |mover_paths_before|-1); assert arr.inv(mover_states_before[|mover_paths_before|-1]); lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, mover_states_before[0], last(mover_states_before), mover_paths_before, mover_tid, mover_states_before, |mover_paths_before|-1); assert arr.l.path_valid(mover_states_before[|mover_paths_before|-1], mover_paths_before[|mover_paths_before|-1], mover_tid); assert arr.l.path_valid(mover_states_before[|mover_paths_before|-1], last(mover_paths_before), mover_tid); var other_states_mid := lemma_PerformRightMoveOne(arr, mover_tid, other_tid, other_tau, last(mover_paths_before), mover_states_before[|mover_paths_before|-1], other_paths, other_states); lemma_AllButLastPreservesAtomicNextMultiplePaths(arr, mover_states_before[0], last(mover_states_before), mover_paths_before, mover_states_before, mover_tid); calc { other_states_mid[0]; mover_states_before[|mover_paths_before|-1]; last(all_but_last(mover_states_before)); } var mover_states_after' := [last(other_states_mid)] + mover_states_after; var mover_paths_after' := [last(mover_paths_before)] + mover_paths_after; assert mover_states_after'[0] == last(other_states_mid); assert mover_states_after'[1..] == mover_states_after; assert mover_paths_after'[0] == last(mover_paths_before); assert mover_paths_after'[1..] == mover_paths_after; assert AtomicNextMultiplePaths(arr.l, mover_states_after'[0], last(mover_states_after'), mover_paths_after', mover_tid); mover_states', other_states' := lemma_PerformRightMoveRemaining(arr, mover_tid, other_tid, other_tau, all_but_last(mover_paths_before), all_but_last(mover_states_before), [last(mover_paths_before)] + mover_paths_after, [last(other_states_mid)] + mover_states_after, other_paths, other_states_mid); assert PerformRightMoveRemainingPredicate(arr, mover_tid, all_but_last(mover_states_before), [last(other_states_mid)] + mover_states_after, mover_states'); lemma_PerformRightMoveRemainingHelper1(arr, mover_tid, other_tid, other_tau, mover_paths_before, mover_states_before, mover_paths_after, mover_states_after, other_paths, other_states, other_states_mid, mover_states', other_states'); lemma_PerformRightMoveRemainingHelper2(arr, mover_tid, other_tid, other_tau, mover_paths_before, mover_states_before, mover_paths_after, mover_states_after, other_paths, other_states, other_states_mid, mover_states', other_states'); assert PerformRightMoveRemainingPredicate(arr, mover_tid, mover_states_before, mover_states_after, mover_states'); } } lemma lemma_PerformRightMoveAll( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_paths:seq, mover_states:seq, other_paths:seq, other_states:seq ) returns ( mover_states':seq, other_states':seq ) requires ValidAtomicReductionRequest(arr) requires other_tau || other_tid != mover_tid requires |mover_states| > 0 requires arr.inv(mover_states[0]) requires arr.l.get_thread_pc(mover_states[0], mover_tid).Some? requires mover_states == AtomicGetStateSequence(arr.l, mover_states[0], mover_paths, mover_tid) requires forall s :: s in mover_states ==> arr.l.get_thread_pc(s, mover_tid).Some? requires forall i :: 0 < i < |mover_states| ==> arr.is_phase1(arr.l.get_thread_pc(mover_states[i], mover_tid).v) requires AtomicNextMultiplePaths(arr.l, mover_states[0], last(mover_states), mover_paths, mover_tid) requires forall path :: path in mover_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? requires |other_states| > 0 requires arr.l.state_ok(last(other_states)) requires other_states[0] == last(mover_states) requires other_states == AtomicGetStateSequence(arr.l, other_states[0], other_paths, other_tid) requires AtomicNextMultiplePaths(arr.l, other_states[0], last(other_states), other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau ensures |other_states'| == |other_states| ensures other_states'[0] == mover_states[0] ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) ensures !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) ensures |mover_states'| == |mover_states| ensures mover_states'[0] == last(other_states') ensures last(mover_states') == last(other_states) ensures mover_states' == AtomicGetStateSequence(arr.l, mover_states'[0], mover_paths, mover_tid) ensures AtomicNextMultiplePaths(arr.l, mover_states'[0], last(mover_states'), mover_paths, mover_tid) ensures forall i :: 0 <= i < |mover_states| ==> OKAndPCTypesMatch(arr, mover_states'[i], mover_states[i], mover_tid) { lemma_ExecutingPathSequenceDoesntRemoveOtherActorPC(arr, other_states[0], last(other_states), other_paths, other_tau, other_tid, mover_tid); lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType(arr, other_states[0], last(other_states), other_paths, other_tau, other_tid, mover_tid); mover_states', other_states' := lemma_PerformRightMoveRemaining(arr, mover_tid, other_tid, other_tau, mover_paths, mover_states, [], [last(other_states)], other_paths, other_states); assert mover_paths + [] == mover_paths; if !other_tau { forall i | 0 <= i < |other_states| ensures OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) { assert PerformRightMoveTrigger(i); } } forall i | 0 <= i < |mover_states| ensures OKAndPCTypesMatch(arr, mover_states'[i], mover_states[i], mover_tid) { assert PerformRightMoveTrigger(i); } } lemma lemma_PerformRightMove( arr:AtomicReductionRequest, s1:LState, s2:LState, s3:LState, multistep1:Armada_Multistep, multistep2:Armada_Multistep ) returns ( s2':LState ) requires ValidAtomicReductionRequest(arr) requires s1 in AnnotatedReachables(GetRefinementViaReductionLSpec(arr)) requires arr.l.state_ok(s3) requires AtomicNextMultistep(arr.l, s1, s2, multistep1.steps, multistep1.tid, multistep1.tau) requires AtomicNextMultistep(arr.l, s2, s3, multistep2.steps, multistep2.tid, multistep2.tau) requires !multistep1.tau requires multistep2.tau || multistep2.tid != multistep1.tid requires IsPhase1(arr, s2, multistep1.tid) ensures AtomicNextMultistep(arr.l, s1, s2', multistep2.steps, multistep2.tid, multistep2.tau) ensures AtomicNextMultistep(arr.l, s2', s3, multistep1.steps, multistep1.tid, multistep1.tau) { lemma_StateAmongAnnotatedReachablesSatisfiesInv(arr, s1); assert arr.inv(s1); var mover_states := AtomicGetStateSequence(arr.l, s1, multistep1.steps, multistep1.tid); lemma_IfMultistepEndsInPhase1ThenEachPathDoes(arr, s1, s2, multistep1, mover_states); var other_states := AtomicGetStateSequence(arr.l, s2, multistep2.steps, multistep2.tid); lemma_AtomicNextLastElement(arr.l, s1, s2, multistep1.steps, multistep1.tid, mover_states); lemma_AtomicNextLastElement(arr.l, s2, s3, multistep2.steps, multistep2.tid, other_states); var mover_states', other_states' := lemma_PerformRightMoveAll(arr, multistep1.tid, multistep2.tid, multistep2.tau, multistep1.steps, mover_states, multistep2.steps, other_states); s2' := last(other_states'); lemma_AtomicNextLastElement(arr.l, s2', s3, multistep1.steps, multistep1.tid, mover_states'); } ////////////////////////////////////////// // LEFT-MOVER ENABLEMENT LEMMAS ////////////////////////////////////////// lemma lemma_GenerateLeftMoverSequenceOfSubpaths( arr:AtomicReductionRequest, s:LState, tid:Armada_ThreadHandle ) returns ( states:seq, trace:seq ) requires ValidAtomicReductionRequest(arr) requires arr.inv(s) requires arr.l.state_ok(s) requires IsPhase2(arr, s, tid) requires AtomicThreadYielding(arr.l, s, tid) ensures states == AtomicGetStateSequence(arr.l, s, trace, tid) ensures AtomicNextMultiplePaths(arr.l, s, last(states), trace, tid) ensures states[0] == s ensures arr.l.state_ok(last(states)) ==> !IsPhase2(arr, last(states), tid) && AtomicThreadYielding(arr.l, last(states), tid) ensures forall i :: 0 <= i < |states|-1 ==> IsPhase2(arr, states[i], tid) ensures forall path :: path in trace ==> !arr.l.path_type(path).AtomicPathType_Tau? { states := [s]; trace := []; while arr.l.state_ok(last(states)) && IsPhase2(arr, last(states), tid) invariant states == AtomicGetStateSequence(arr.l, s, trace, tid) invariant AtomicNextMultiplePaths(arr.l, s, last(states), trace, tid) invariant states[0] == s invariant arr.inv(last(states)) invariant arr.l.state_ok(last(states)) && !AtomicThreadYielding(arr.l, last(states), tid) ==> IsPhase2(arr, last(states), tid) invariant forall i :: 0 <= i < |states|-1 ==> IsPhase2(arr, states[i], tid) invariant forall path :: path in trace ==> !arr.l.path_type(path).AtomicPathType_Tau? decreases arr.left_mover_generation_progress(last(states), tid) { var states_current := states; forall i | 0 <= i < |states_current| ensures IsPhase2(arr, states_current[i], tid) { if i < |states|-1 { assert IsPhase2(arr, states[i], tid); } else { assert states_current[i] == last(states); } } var s_current := last(states); var path := arr.generate_left_mover(s_current, tid); assert AtomicReductionSpecModule.LeftMoversAlwaysEnabledConditions(arr, s_current, tid); assert arr.l.path_valid(s_current, path, tid) && !arr.l.path_type(path).AtomicPathType_Tau?; var s_next := arr.l.path_next(s_current, path, tid); assert 0 <= arr.left_mover_generation_progress(s_next, tid) < arr.left_mover_generation_progress(s_current, tid); lemma_ExtendingStateSequenceWorks(arr.l, s, s_current, trace, states, tid, path, s_next); trace := trace + [path]; states := states + [s_next]; forall i | 0 <= i < |states|-1 ensures IsPhase2(arr, states[i], tid) { assert states[i] == states_current[i]; } } } lemma lemma_GenerateLeftMoverSequence( arr:AtomicReductionRequest, s:LState, tid:Armada_ThreadHandle ) returns ( states:seq, trace:seq> ) requires ValidAtomicReductionRequest(arr) requires arr.inv(s) requires arr.l.state_ok(s) requires IsPhase2(arr, s, tid) requires AtomicThreadYielding(arr.l, s, tid) ensures StateNextSeq(states, trace, GetRefinementViaReductionLSpec(arr).next) ensures states[0] == s ensures arr.l.state_ok(last(states)) ==> !IsPhase2(arr, last(states), tid) && AtomicThreadYielding(arr.l, last(states), tid) ensures forall i :: 0 <= i < |states|-1 ==> IsPhase2(arr, states[i], tid) ensures forall multistep :: multistep in trace ==> !multistep.tau && multistep.tid == tid { var rspec := GetRefinementViaReductionLSpec(arr); var single_states, single_trace := lemma_GenerateLeftMoverSequenceOfSubpaths(arr, s, tid); states := [s]; trace := []; var partial_paths := []; var partial_states := [s]; var pos := 0; assert !arr.l.state_ok(single_states[pos]) || AtomicThreadYielding(arr.l, single_states[pos], tid) ==> |partial_paths| == 0; while pos < |single_trace| invariant 0 <= pos <= |single_trace| invariant |states| > 0 invariant states[0] == s invariant StateNextSeq(states, trace, GetRefinementViaReductionLSpec(arr).next) invariant if pos < |single_trace| then (forall state :: state in states ==> IsPhase2(arr, state, tid)) else (forall i :: 0 <= i < |states|-1 ==> IsPhase2(arr, states[i], tid)) invariant forall multistep :: multistep in trace ==> !multistep.tau && multistep.tid == tid invariant !arr.l.state_ok(last(states)) || AtomicThreadYielding(arr.l, last(states), tid) invariant !arr.l.state_ok(single_states[pos]) || AtomicThreadYielding(arr.l, single_states[pos], tid) ==> |partial_paths| == 0 invariant AtomicNextMultiplePaths(arr.l, last(states), single_states[pos], partial_paths, tid) invariant partial_states == AtomicGetStateSequence(arr.l, last(states), partial_paths, tid) invariant forall i :: 0 < i < |partial_states| ==> !AtomicThreadYielding(arr.l, partial_states[i], tid) invariant forall path :: path in partial_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? decreases |single_trace| - pos { var s_current := single_states[pos]; var s_next := single_states[pos+1]; var next_path := single_trace[pos]; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, single_states[0], last(single_states), single_trace, tid, single_states, pos); assert arr.l.path_valid(s_current, next_path, tid); assert s_next == arr.l.path_next(s_current, next_path, tid); lemma_ExtendingStateSequenceWorks(arr.l, last(states), s_current, partial_paths, partial_states, tid, next_path, s_next); var upper := |partial_states|; assert forall i :: 0 < i < upper ==> !AtomicThreadYielding(arr.l, partial_states[i], tid); partial_paths := partial_paths + [next_path]; partial_states := partial_states + [s_next]; assert upper == |partial_paths|; assert forall i :: 0 < i < |partial_paths| ==> !AtomicThreadYielding(arr.l, partial_states[i], tid); if !arr.l.state_ok(s_next) || AtomicThreadYielding(arr.l, s_next, tid) { var multistep := Armada_Multistep(partial_paths, tid, false); assert AtomicNextMultistep(arr.l, last(states), s_next, multistep.steps, multistep.tid, multistep.tau); assert ActionTuple(last(states), s_next, multistep) in rspec.next; var states_next := states + [s_next]; var trace_next := trace + [multistep]; forall i | 0 <= i < |trace_next| ensures ActionTuple(states_next[i], states_next[i+1], trace_next[i]) in rspec.next { if i < |trace| { assert ActionTuple(states[i], states[i+1], trace[i]) in rspec.next; assert states_next[i] == states[i]; assert states_next[i+1] == states[i+1]; assert trace_next[i] == trace[i]; } else { assert i == |trace| == |states|-1; assert states_next[i] == states[i] == last(states); assert states_next[i+1] == s_next; assert trace_next[i] == multistep; } } states := states_next; trace := trace_next; partial_paths := []; partial_states := [s_next]; assert StateNextSeq(states, trace, rspec.next); } pos := pos + 1; assert AtomicNextMultiplePaths(arr.l, last(states), single_states[pos], partial_paths, tid); } assert |partial_paths| == 0; assert pos == |single_trace|; assert last(states) == single_states[pos] == last(single_states); } lemma lemma_LeftMoversAlwaysEnabled( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures RefinementViaReductionSpecModule.LeftMoversAlwaysEnabled(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall s, actor | && s in AnnotatedReachables(rr.lspec) && rr.phase2(s, actor) && !rr.crashed(s) ensures exists states, trace :: && StateNextSeq(states, trace, rr.lspec.next) && states[0] == s && (!arr.l.state_ok(last(states)) || !rr.phase2(last(states), actor)) && (forall i :: 0 <= i < |states|-1 ==> rr.phase2(states[i], actor)) && (forall path :: path in trace ==> rr.idmap(path) == actor) { lemma_StateAmongAnnotatedReachablesSatisfiesInv(arr, s); assert actor.Some?; lemma_StateAmongAnnotatedReachablesHasThreadYielding(arr, s, actor.v); var states, trace := lemma_GenerateLeftMoverSequence(arr, s, actor.v); } } //////////////////////////////////////////// // LEMMAS ABOUT LIFTING ACTION SEQUENCES //////////////////////////////////////////// function CombineMultisteps( arr:AtomicReductionRequest, trace:seq> ) : seq { if |trace| == 0 then [] else trace[0].steps + CombineMultisteps(arr, trace[1..]) } lemma lemma_CombineMultistepsEffectOnGetStateSequence( arr:AtomicReductionRequest, states:seq, trace:seq>, tid:Armada_ThreadHandle ) requires ValidAtomicReductionRequest(arr) requires |trace| > 0 requires |states| == |trace| + 1 requires forall i :: 0 <= i < |trace| ==> AtomicNextMultiplePaths(arr.l, states[i], states[i+1], trace[i].steps, tid) ensures AtomicGetStateSequence(arr.l, states[0], CombineMultisteps(arr, trace), tid) == AtomicGetStateSequence(arr.l, states[0], trace[0].steps, tid) + AtomicGetStateSequence(arr.l, states[1], CombineMultisteps(arr, trace[1..]), tid)[1..] decreases |trace[0].steps| { var pos := 0; assert AtomicNextMultiplePaths(arr.l, states[pos], states[pos+1], trace[pos].steps, tid); var multistep := trace[0]; if |multistep.steps| == 0 { calc { AtomicGetStateSequence(arr.l, states[0], trace[0].steps, tid) + AtomicGetStateSequence(arr.l, states[1], CombineMultisteps(arr, trace[1..]), tid)[1..]; { assert trace[0].steps == []; assert AtomicGetStateSequence(arr.l, states[0], trace[0].steps, tid) == [states[0]]; } [states[0]] + AtomicGetStateSequence(arr.l, states[1], CombineMultisteps(arr, trace[1..]), tid)[1..]; { assert states[1] == states[0]; } [states[0]] + AtomicGetStateSequence(arr.l, states[0], CombineMultisteps(arr, trace[1..]), tid)[1..]; { assert AtomicGetStateSequence(arr.l, states[0], CombineMultisteps(arr, trace[1..]), tid)[0] == states[0]; } AtomicGetStateSequence(arr.l, states[0], CombineMultisteps(arr, trace[1..]), tid); { assert CombineMultisteps(arr, trace) == CombineMultisteps(arr, trace[1..]); } AtomicGetStateSequence(arr.l, states[0], CombineMultisteps(arr, trace), tid); } } else { var s_mid := arr.l.path_next(states[0], multistep.steps[0], tid); var new_trace0 := multistep.(steps := multistep.steps[1..]); var states' := states[0 := s_mid]; var trace' := trace[0 := new_trace0]; forall i {:trigger ActionTuple(states'[i], states'[i+1], trace'[i]) in GetRefinementViaReductionLSpec(arr).next} | 0 <= i < |trace'| ensures AtomicNextMultiplePaths(arr.l, states'[i], states'[i+1], trace'[i].steps, tid); { assert AtomicNextMultiplePaths(arr.l, states[i], states[i+1], trace[i].steps, tid); if i == 0 { assert states'[i] == s_mid; assert states'[i+1] == states[1]; assert trace'[i] == new_trace0; } else { assert states'[i] == states[i]; assert states'[i+1] == states[i+1]; assert trace'[i] == trace[i]; } } lemma_CombineMultistepsEffectOnGetStateSequence(arr, states', trace', tid); assert AtomicGetStateSequence(arr.l, states'[0], CombineMultisteps(arr, trace'), tid) == AtomicGetStateSequence(arr.l, states'[0], trace'[0].steps, tid) + AtomicGetStateSequence(arr.l, states'[1], CombineMultisteps(arr, trace'[1..]), tid)[1..]; calc { CombineMultisteps(arr, trace'); trace'[0].steps + CombineMultisteps(arr, trace'[1..]); new_trace0.steps + CombineMultisteps(arr, trace'[1..]); multistep.steps[1..] + CombineMultisteps(arr, trace[1..]); { lemma_DroppingHeadOfConcatenation(multistep.steps, CombineMultisteps(arr, trace[1..])); } (multistep.steps + CombineMultisteps(arr, trace[1..]))[1..]; CombineMultisteps(arr, trace)[1..]; } calc { AtomicGetStateSequence(arr.l, states[0], CombineMultisteps(arr, trace), tid); [states[0]] + AtomicGetStateSequence(arr.l, s_mid, CombineMultisteps(arr, trace)[1..], tid); [states[0]] + AtomicGetStateSequence(arr.l, states'[0], CombineMultisteps(arr, trace)[1..], tid); { assert CombineMultisteps(arr, trace') == CombineMultisteps(arr, trace)[1..]; } [states[0]] + AtomicGetStateSequence(arr.l, states'[0], CombineMultisteps(arr, trace'), tid); { assert AtomicGetStateSequence(arr.l, states'[0], CombineMultisteps(arr, trace'), tid) == AtomicGetStateSequence(arr.l, states'[0], trace'[0].steps, tid) + AtomicGetStateSequence(arr.l, states'[1], CombineMultisteps(arr, trace'[1..]), tid)[1..]; } [states[0]] + (AtomicGetStateSequence(arr.l, states'[0], trace'[0].steps, tid) + AtomicGetStateSequence(arr.l, states'[1], CombineMultisteps(arr, trace'[1..]), tid)[1..]); { lemma_SequenceConcatenationAssociative( [states[0]], AtomicGetStateSequence(arr.l, states'[0], trace'[0].steps, tid), AtomicGetStateSequence(arr.l, states'[1], CombineMultisteps(arr, trace'[1..]), tid)[1..]); } [states[0]] + AtomicGetStateSequence(arr.l, states'[0], trace'[0].steps, tid) + AtomicGetStateSequence(arr.l, states'[1], CombineMultisteps(arr, trace'[1..]), tid)[1..]; [states[0]] + AtomicGetStateSequence(arr.l, states'[0], trace'[0].steps, tid) + AtomicGetStateSequence(arr.l, states[1], CombineMultisteps(arr, trace'[1..]), tid)[1..]; [states[0]] + AtomicGetStateSequence(arr.l, states'[0], trace'[0].steps, tid) + AtomicGetStateSequence(arr.l, states[1], CombineMultisteps(arr, trace[1..]), tid)[1..]; AtomicGetStateSequence(arr.l, states[0], trace[0].steps, tid) + AtomicGetStateSequence(arr.l, states[1], CombineMultisteps(arr, trace[1..]), tid)[1..]; } } } lemma lemma_CombineMultistepsCreatesValidHPathHelper( arr:AtomicReductionRequest, s:LState, s':LState, multistep:Armada_Multistep, states:seq, i:int ) requires states == AtomicGetStateSequence(arr.l, s, multistep.steps, multistep.tid) requires !multistep.tau requires AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau) requires 0 < i < |states| requires i == |states|-1 ==> arr.l.state_ok(s') requires i == |states|-1 ==> IsPhase1(arr, s', multistep.tid) || IsPhase2(arr, s', multistep.tid) ensures IsNonyieldingOrInPhase(arr, states[i], multistep.tid) { var pc := arr.l.get_thread_pc(states[i], multistep.tid); if i < |states|-1 { assert pc.Some? && arr.l.is_pc_nonyielding(pc.v); } else { assert i == |states|-1; assert states[i] == last(states); lemma_AtomicNextLastElement(arr.l, s, s', multistep.steps, multistep.tid, states); assert last(states) == s'; assert IsPhase1(arr, states[i], multistep.tid) || IsPhase2(arr, states[i], multistep.tid); assert pc.Some? && (arr.is_phase1(pc.v) || arr.is_phase2(pc.v)); } assert IsNonyieldingOrInPhase(arr, states[i], multistep.tid); } lemma lemma_CombineMultistepsCreatesValidHPath( arr:AtomicReductionRequest, states:seq, trace:seq>, tid:Armada_ThreadHandle, combined:seq, all_states:seq ) requires ValidAtomicReductionRequest(arr) requires StateNextSeq(states, trace, GetRefinementViaReductionLSpec(arr).next) requires forall multistep :: multistep in trace ==> !multistep.tau && multistep.tid == tid requires combined == CombineMultisteps(arr, trace) requires forall i :: 0 < i < |trace| ==> arr.l.state_ok(states[i]) requires forall i :: 0 < i < |trace| ==> IsPhase1(arr, states[i], tid) || IsPhase2(arr, states[i], tid) requires all_states == AtomicGetStateSequence(arr.l, states[0], combined, tid) ensures forall i :: 0 < i < |combined| ==> IsNonyieldingOrInPhase(arr, all_states[i], tid) { var rspec := GetRefinementViaReductionLSpec(arr); if |trace| > 0 { forall i | 0 <= i < |trace| ensures AtomicNextMultiplePaths(arr.l, states[i], states[i+1], trace[i].steps, tid) { assert ActionTuple(states[i], states[i+1], trace[i]) in rspec.next; } lemma_CombineMultistepsEffectOnGetStateSequence(arr, states, trace, tid); var states' := states[1..]; var trace' := trace[1..]; var combined' := CombineMultisteps(arr, trace'); var all_states' := AtomicGetStateSequence(arr.l, states'[0], combined', tid); forall i {:trigger ActionTuple(states'[i], states'[i+1], trace'[i]) in rspec.next} | 0 <= i < |trace'| ensures ActionTuple(states'[i], states'[i+1], trace'[i]) in rspec.next { var next := i+1; assert ActionTuple(states[next], states[next+1], trace[next]) in rspec.next; } lemma_CombineMultistepsCreatesValidHPath(arr, states[1..], trace[1..], tid, combined', all_states'); var multistep := trace[0]; assert multistep.tid == tid; var first_states := AtomicGetStateSequence(arr.l, states[0], multistep.steps, tid); assert all_states == first_states + all_states'[1..]; forall i | 0 < i < |combined| ensures IsNonyieldingOrInPhase(arr, all_states[i], tid) { if i < |first_states| { var zero := 0; assert ActionTuple(states[zero], states[zero+1], trace[zero]) in rspec.next; assert AtomicNextMultistep(arr.l, states[zero], states[zero+1], multistep.steps, multistep.tid, multistep.tau); var one := zero+1; lemma_AtomicNextLastElement(arr.l, states[zero], states[zero+1], multistep.steps, multistep.tid, first_states); assert all_states[i] == first_states[i]; if i == |first_states|-1 { assert |combined| > |combined'|; assert |states| > 2; assert 0 < one < |trace|; assert arr.l.state_ok(states[one]); assert IsPhase1(arr, states[one], tid) || IsPhase2(arr, states[one], tid); } lemma_CombineMultistepsCreatesValidHPathHelper(arr, states[zero], states[one], multistep, first_states, i); assert IsNonyieldingOrInPhase(arr, all_states[i], tid); } else { calc { all_states[i]; (first_states + all_states'[1..])[i]; { lemma_IndexIntoConcatenation(first_states, all_states'[1..], i); } all_states'[1..][i - |first_states|]; { lemma_IndexIntoDrop(all_states', 1, i - |first_states|); } all_states'[1 + i - |first_states|]; } var pos := 1 + i - |first_states|; assert 0 < pos < |combined'|; assert IsNonyieldingOrInPhase(arr, all_states'[pos], tid); assert IsNonyieldingOrInPhase(arr, all_states[i], tid); } } } } lemma lemma_CombineMultistepsPreservesStateNextSeq( arr:AtomicReductionRequest, states:seq, trace:seq>, tid:Armada_ThreadHandle, combined:seq ) requires ValidAtomicReductionRequest(arr) requires StateNextSeq(states, trace, GetRefinementViaReductionLSpec(arr).next) requires forall multistep :: multistep in trace ==> !multistep.tau && multistep.tid == tid requires combined == CombineMultisteps(arr, trace) ensures AtomicNextMultiplePaths(arr.l, states[0], last(states), combined, tid) ensures forall path :: path in combined ==> !arr.l.path_type(path).AtomicPathType_Tau? { var rspec := GetRefinementViaReductionLSpec(arr); if |trace| > 0 { var pos := 0; assert ActionTuple(states[pos], states[pos+1], trace[pos]) in rspec.next; var multistep := trace[0]; assert AtomicNextMultistep(arr.l, states[pos], states[pos+1], multistep.steps, multistep.tid, multistep.tau); var states' := states[1..]; var trace' := trace[1..]; forall i {:trigger ActionTuple(states'[i], states'[i+1], trace'[i]) in rspec.next} | 0 <= i < |trace'| ensures ActionTuple(states'[i], states'[i+1], trace'[i]) in rspec.next { var next := i+1; assert ActionTuple(states[next], states[next+1], trace[next]) in rspec.next; assert states'[i] == states[next]; assert states'[i+1] == states[next+1]; assert trace'[i] == trace[next]; } var combined' := CombineMultisteps(arr, trace'); lemma_CombineMultistepsPreservesStateNextSeq(arr, states', trace', tid, combined'); lemma_AtomicNextMultiplePathsTransitive(arr, states[0], states[1], last(states), multistep.steps, combined', tid); } } lemma lemma_LiftActionSequenceCaseMultiplePaths( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, states:seq, trace:seq>, actor:Option ) returns ( hmultistep:Armada_Multistep ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires StateNextSeq(states, trace, GetRefinementViaReductionLSpec(arr).next) requires forall multistep :: multistep in trace ==> rr.idmap(multistep) == actor requires !rr.phase1(states[0], actor) requires !rr.phase2(states[0], actor) requires var s := last(states); !rr.crashed(s) ==> !rr.phase1(s, actor) && !rr.phase2(s, actor) requires forall i :: 0 < i < |trace| ==> !rr.crashed(states[i]) requires forall i :: 0 < i < |trace| ==> var s := states[i]; rr.phase1(s, actor) || rr.phase2(s, actor) requires |trace| > 1 requires actor.Some? ensures ActionTuple(states[0], last(states), hmultistep) in rr.hspec.next { var rspec := GetRefinementViaReductionLSpec(arr); var pos := 0; assert ActionTuple(states[pos], states[pos+1], trace[pos]) in rspec.next; assert rr.idmap(trace[pos]) == actor; assert !IsNonyieldingOrInPhase(arr, states[0], actor.v); pos := |trace|-1; assert ActionTuple(states[pos], states[pos+1], trace[pos]) in rspec.next; assert rr.idmap(trace[pos]) == actor; assert !IsNonyieldingOrInPhase(arr, last(states), actor.v); var paths := CombineMultisteps(arr, trace); hmultistep := Armada_Multistep(paths, actor.v, false); lemma_CombineMultistepsPreservesStateNextSeq(arr, states, trace, actor.v, paths); var all_states := AtomicGetStateSequence(arr.l, states[0], paths, actor.v); forall i | 1 <= i < |states|-1 ensures IsPhase1(arr, states[i], actor.v) || IsPhase2(arr, states[i], actor.v) { assert rr.phase1(states[i], actor) || rr.phase2(states[i], actor); } lemma_CombineMultistepsCreatesValidHPath(arr, states, trace, actor.v, paths, all_states); assert GenericNextReduced(arr, states[0], last(states), paths, actor.v, false); } lemma lemma_LiftActionSequenceCasePath( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, states:seq, trace:seq>, actor:Option ) returns ( hmultistep:Armada_Multistep ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires StateNextSeq(states, trace, GetRefinementViaReductionLSpec(arr).next) requires forall multistep :: multistep in trace ==> rr.idmap(multistep) == actor requires !rr.phase1(states[0], actor) requires !rr.phase2(states[0], actor) requires var s := last(states); !rr.crashed(s) ==> !rr.phase1(s, actor) && !rr.phase2(s, actor) requires forall i :: 1 <= i < |states|-1 ==> rr.phase1(states[i], actor) || rr.phase2(states[i], actor) requires |trace| == 1 ensures ActionTuple(states[0], last(states), hmultistep) in rr.hspec.next { var s := states[0]; var s' := last(states); hmultistep := trace[0]; var pos := 0; assert ActionTuple(states[pos], states[pos+1], trace[pos]) in GetRefinementViaReductionLSpec(arr).next; assert AtomicNextMultistep(arr.l, states[0], states[0+1], hmultistep.steps, hmultistep.tid, hmultistep.tau); assert rr.idmap(trace[0]) == actor; assert !hmultistep.tau ==> actor == Some(hmultistep.tid); assert GenericNextReduced(arr, s, s', hmultistep.steps, hmultistep.tid, hmultistep.tau); } lemma lemma_LiftActionSequenceCaseTau( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, states:seq, trace:seq>, actor:Option ) returns ( hmultistep:Armada_Multistep ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires StateNextSeq(states, trace, GetRefinementViaReductionLSpec(arr).next) requires forall multistep :: multistep in trace ==> rr.idmap(multistep) == actor requires !rr.phase1(states[0], actor) requires !rr.phase2(states[0], actor) requires var s := last(states); !rr.crashed(s) ==> !rr.phase1(s, actor) && !rr.phase2(s, actor) requires forall i :: 1 <= i < |states|-1 ==> rr.phase1(states[i], actor) || rr.phase2(states[i], actor) requires |trace| > 1 requires actor.None? ensures ActionTuple(states[0], last(states), hmultistep) in rr.hspec.next { var pos := 1; assert 1 <= pos < |states|-1; assert rr.phase1(states[pos], actor) || rr.phase2(states[pos], actor); assert false; } lemma lemma_LiftActionSequence( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, states:seq, trace:seq>, actor:Option ) returns ( hmultistep:Armada_Multistep ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires StateNextSeq(states, trace, GetRefinementViaReductionLSpec(arr).next) requires forall multistep :: multistep in trace ==> rr.idmap(multistep) == actor requires |states| > 1 requires !rr.phase1(states[0], actor) requires !rr.phase2(states[0], actor) requires var s := last(states); !rr.crashed(s) ==> !rr.phase1(s, actor) && !rr.phase2(s, actor) requires forall i :: 0 < i < |trace| ==> !rr.crashed(states[i]) requires forall i :: 0 < i < |trace| ==> rr.phase1(states[i], actor) || rr.phase2(states[i], actor) ensures ActionTuple(states[0], last(states), hmultistep) in rr.hspec.next { if |trace| == 1 { hmultistep := lemma_LiftActionSequenceCasePath(arr, rr, states, trace, actor); } else if actor.None? { hmultistep := lemma_LiftActionSequenceCaseTau(arr, rr, states, trace, actor); } else { hmultistep := lemma_LiftActionSequenceCaseMultiplePaths(arr, rr, states, trace, actor); } } lemma lemma_ActionSequencesLiftable( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures ActionSequencesLiftable(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall states, trace, actor {:trigger RefinementViaReductionSpecModule.ActionSequencesLiftableConditions(rr, states, trace, actor)} | RefinementViaReductionSpecModule.ActionSequencesLiftableConditions(rr, states, trace, actor) ensures exists hmultistep :: ActionTuple(states[0], last(states), hmultistep) in rr.hspec.next { var hmultistep := lemma_LiftActionSequence(arr, rr, states, trace, actor); } } ////////////////////////////////////////////////////////////////////////// // LEMMAS PROVING VALIDITY OF GENERATED CRASHING REDUCTION REQUEST ////////////////////////////////////////////////////////////////////////// lemma lemma_IfAtomicReductionRequestValidThenCrashingCantGoDirectlyFromPhase2ToPhase1( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures CantGoDirectlyFromPhase2ToPhase1(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall s, s', multistep | ActionTuple(s, s', multistep) in rr.lspec.next && rr.phase2(s, rr.idmap(multistep)) ensures !rr.phase1(s', rr.idmap(multistep)) { assert AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau); var tid := multistep.tid; if |multistep.steps| > 0 { var states := AtomicGetStateSequence(arr.l, s, multistep.steps, multistep.tid); var pos := 0; while pos < |multistep.steps|-1 invariant 0 <= pos < |multistep.steps| invariant IsPhase2(arr, states[pos], tid) decreases |multistep.steps| - pos { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, s, s', multistep.steps, multistep.tid, states, pos); assert arr.l.path_valid(states[pos], multistep.steps[pos], multistep.tid); assert states[pos+1] == arr.l.path_next(states[pos], multistep.steps[pos], multistep.tid); var next_pos := pos+1; var pc := arr.l.get_thread_pc(states[next_pos], tid).v; assert arr.l.is_pc_nonyielding(pc); pos := pos+1; } lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, s, s', multistep.steps, multistep.tid, states, pos); lemma_AtomicNextLastElement(arr.l, s, s', multistep.steps, multistep.tid, states); } } } lemma lemma_IfAtomicReductionRequestValidThenCrashingPhaseUnaffectedByOtherActorsHelper( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures var rr := GetRefinementViaReductionRequest(arr); forall s, s', multistep, actor :: ActionTuple(s, s', multistep) in rr.lspec.next && rr.idmap(multistep) != actor ==> && (rr.phase1(s, actor) <==> rr.phase1(s', actor)) && (rr.phase2(s, actor) <==> rr.phase2(s', actor)) { var rr := GetRefinementViaReductionRequest(arr); forall s, s', multistep, actor | ActionTuple(s, s', multistep) in rr.lspec.next && rr.idmap(multistep) != actor ensures rr.phase1(s, actor) <==> rr.phase1(s', actor) ensures rr.phase2(s, actor) <==> rr.phase2(s', actor) { if actor.Some? { assert AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau); var tid := actor.v; var states := AtomicGetStateSequence(arr.l, s, multistep.steps, multistep.tid); var pc := arr.l.get_thread_pc(s, tid); var phase1 := pc.Some? && arr.is_phase1(pc.v); var phase2 := pc.Some? && arr.is_phase2(pc.v); var pos := 0; while pos < |multistep.steps| invariant 0 <= pos <= |multistep.steps| invariant phase1 <==> IsPhase1(arr, states[pos], tid) invariant phase2 <==> IsPhase2(arr, states[pos], tid) decreases |multistep.steps| - pos { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, s, s', multistep.steps, multistep.tid, states, pos); assert arr.l.path_valid(states[pos], multistep.steps[pos], multistep.tid); assert states[pos+1] == arr.l.path_next(states[pos], multistep.steps[pos], multistep.tid); var pc1 := arr.l.get_thread_pc(states[pos], tid); var pc2 := arr.l.get_thread_pc(states[pos+1], tid); assert pc1 != pc2 ==> pc1.None? && !arr.is_phase1(pc2.v) && !arr.is_phase2(pc2.v); var next_pos := pos+1; pos := pos+1; } lemma_AtomicNextLastElement(arr.l, s, s', multistep.steps, multistep.tid, states); } } } lemma lemma_IfAtomicReductionRequestValidThenCrashingPhaseUnaffectedByOtherActors( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures RefinementViaReductionSpecModule.PhaseUnaffectedByOtherActors(GetRefinementViaReductionRequest(arr)) { lemma_IfAtomicReductionRequestValidThenCrashingPhaseUnaffectedByOtherActorsHelper(arr); var rr := GetRefinementViaReductionRequest(arr); forall s, s', multistep, actor | ActionTuple(s, s', multistep) in rr.lspec.next && rr.idmap(multistep) != actor ensures rr.phase1(s, actor) <==> rr.phase1(s', actor) { assert && (rr.phase1(s, actor) <==> rr.phase1(s', actor)) && (rr.phase2(s, actor) <==> rr.phase2(s', actor)); } forall s, s', multistep, actor | ActionTuple(s, s', multistep) in rr.lspec.next && rr.idmap(multistep) != actor ensures rr.phase2(s, actor) <==> rr.phase2(s', actor) { assert && (rr.phase1(s, actor) <==> rr.phase1(s', actor)) && (rr.phase2(s, actor) <==> rr.phase2(s', actor)); } } lemma lemma_PostCrashStepsStutter( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures PostCrashStepsStutter(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall s, s', multistep | rr.crashed(s) && ActionTuple(s, s', multistep) in GetRefinementViaReductionLSpec(arr).next ensures s' == s { assert AtomicNextMultiplePaths(arr.l, s, s', multistep.steps, multistep.tid); if |multistep.steps| > 0 { assert arr.l.path_valid(s, multistep.steps[0], multistep.tid); assert !arr.l.state_ok(s); assert !arr.l.path_valid(s, multistep.steps[0], multistep.tid); assert false; } else { assert s' == s; } } } lemma lemma_RightMoversPreserveStateRefinement( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures RefinementViaReductionSpecModule.RightMoversPreserveStateRefinement(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall s, s', multistep | && ActionTuple(s, s', multistep) in rr.lspec.next && rr.phase1(s', rr.idmap(multistep)) && arr.l.state_ok(s') ensures RefinementPair(s', s) in rr.relation { assert !multistep.tau; assert AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau); var tid := multistep.tid; var states := AtomicGetStateSequence(arr.l, s, multistep.steps, multistep.tid); lemma_AtomicNextLastElement(arr.l, s, s', multistep.steps, multistep.tid, states); var pos := |multistep.steps|; while pos > 0 invariant 0 <= pos <= |multistep.steps| invariant pos > 0 ==> IsPhase1(arr, states[pos], tid) invariant arr.l.state_ok(states[pos]) invariant RefinementPair(s', states[pos]) in rr.relation decreases pos { var prev_pos := pos-1; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, s, s', multistep.steps, multistep.tid, states, prev_pos); assert arr.l.path_valid(states[prev_pos], multistep.steps[prev_pos], multistep.tid); assert states[prev_pos+1] == arr.l.path_next(states[prev_pos], multistep.steps[prev_pos], multistep.tid); assert RefinementPair(states[prev_pos+1], states[prev_pos]) in arr.self_relation; assert RefinementPair(s', states[pos]) in arr.self_relation; assert RefinementPair(s', states[prev_pos]) in arr.self_relation; if prev_pos > 0 { var pc := arr.l.get_thread_pc(states[prev_pos], tid); assert pc.Some? && arr.l.is_pc_nonyielding(pc.v); assert !arr.is_phase2(pc.v); assert arr.is_phase1(pc.v); } pos := pos-1; } } } lemma lemma_LeftMoversPreserveStateRefinement( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures RefinementViaReductionSpecModule.LeftMoversPreserveStateRefinement(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall s, s', multistep | && ActionTuple(s, s', multistep) in rr.lspec.next && rr.phase2(s, rr.idmap(multistep)) ensures RefinementPair(s, s') in rr.relation { assert !multistep.tau; assert AtomicNextMultistep(arr.l, s, s', multistep.steps, multistep.tid, multistep.tau); var tid := multistep.tid; var states := AtomicGetStateSequence(arr.l, s, multistep.steps, multistep.tid); var pos := 0; while pos < |multistep.steps| invariant 0 <= pos <= |multistep.steps| invariant pos < |multistep.steps| ==> IsPhase2(arr, states[pos], tid) invariant RefinementPair(s, states[pos]) in rr.relation decreases |multistep.steps| - pos { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, s, s', multistep.steps, multistep.tid, states, pos); assert arr.l.path_valid(states[pos], multistep.steps[pos], multistep.tid); assert states[pos+1] == arr.l.path_next(states[pos], multistep.steps[pos], multistep.tid); assert RefinementPair(states[pos], states[pos+1]) in arr.self_relation; pos := pos+1; } lemma_AtomicNextLastElement(arr.l, s, s', multistep.steps, multistep.tid, states); } } lemma lemma_MoveLeftMoverLeftOneAsSinglePath( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, initial_state:LState, state_after_other_path:LState, state_after_both_paths:LState, mover_path:LPath, other_path:LPath ) returns ( state_after_mover_path:LState ) requires ValidAtomicReductionRequest(arr) requires arr.inv(initial_state) requires arr.l.path_valid(initial_state, other_path, other_tid) requires state_after_other_path == arr.l.path_next(initial_state, other_path, other_tid) requires arr.l.path_type(other_path).AtomicPathType_Tau? == other_tau requires arr.l.path_valid(state_after_other_path, mover_path, mover_tid) requires state_after_both_paths == arr.l.path_next(state_after_other_path, mover_path, mover_tid) requires !arr.l.path_type(mover_path).AtomicPathType_Tau? requires other_tau || other_tid != mover_tid requires arr.l.state_ok(state_after_both_paths) requires IsPhase2(arr, state_after_other_path, mover_tid) ensures arr.l.path_valid(initial_state, mover_path, mover_tid) ensures state_after_mover_path == arr.l.path_next(initial_state, mover_path, mover_tid) ensures arr.l.path_valid(state_after_mover_path, other_path, other_tid) ensures state_after_both_paths == arr.l.path_next(state_after_mover_path, other_path, other_tid) ensures OKAndPCTypesMatch(arr, state_after_mover_path, state_after_both_paths, mover_tid) ensures !other_tau ==> OKAndPCTypesMatch(arr, state_after_other_path, state_after_both_paths, other_tid) { state_after_mover_path := arr.l.path_next(initial_state, mover_path, mover_tid); assert AtomicReductionSpecModule.LeftMoversCommuteConditions(arr, initial_state, other_path, mover_path, mover_tid, other_tid); if !other_tau { lemma_ExecutingPathDoesntChangeOtherActorPCType(arr, state_after_other_path, state_after_both_paths, mover_path, mover_tid, other_tid); } lemma_ExecutingPathDoesntChangeOtherActorPCType(arr, state_after_mover_path, state_after_both_paths, other_path, other_tid, mover_tid); } lemma {:timeLimitMultiplier 2} lemma_MoveLeftMoverLeftAsSinglePaths( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, post_mover_state:LState, mover_path:LPath, other_states:seq, other_paths:seq ) returns ( other_states':seq ) requires ValidAtomicReductionRequest(arr) requires |other_states| > 0 requires arr.inv(other_states[0]) requires AtomicNextMultiplePaths(arr.l, other_states[0], last(other_states), other_paths, other_tid) requires other_states == AtomicGetStateSequence(arr.l, other_states[0], other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires arr.l.path_valid(last(other_states), mover_path, mover_tid) requires post_mover_state == arr.l.path_next(last(other_states), mover_path, mover_tid) requires !arr.l.path_type(mover_path).AtomicPathType_Tau? requires other_tau || other_tid != mover_tid requires arr.l.state_ok(post_mover_state) requires IsPhase2(arr, last(other_states), mover_tid) ensures |other_states'| == |other_states| ensures last(other_states') == post_mover_state ensures arr.l.path_valid(other_states[0], mover_path, mover_tid) ensures other_states'[0] == arr.l.path_next(other_states[0], mover_path, mover_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) ensures OKAndPCTypesMatch(arr, other_states[0], last(other_states), mover_tid) ensures OKAndPCTypesMatch(arr, other_states'[0], post_mover_state, mover_tid) ensures !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) decreases |other_states| { if |other_paths| == 0 { other_states' := [post_mover_state]; return; } var pos := |other_states|-2; lemma_AtomicInvariantHoldsAtIntermediateState(arr.l, arr.inv, other_states[0], last(other_states), other_paths, other_tid, other_states, pos); lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, other_states[0], last(other_states), other_paths, other_tid, other_states, pos); var state_after_mover_path := lemma_MoveLeftMoverLeftOneAsSinglePath( arr, mover_tid, other_tid, other_tau, other_states[pos], other_states[pos+1], post_mover_state, mover_path, other_paths[pos]); lemma_AllButLastPreservesAtomicNextMultiplePaths(arr, other_states[0], last(other_states), other_paths, other_states, other_tid); assert last(all_but_last(other_states)) == other_states[pos]; var other_states_next := lemma_MoveLeftMoverLeftAsSinglePaths( arr, mover_tid, other_tid, other_tau, state_after_mover_path, mover_path, all_but_last(other_states), all_but_last(other_paths)); other_states' := other_states_next + [post_mover_state]; lemma_ExtendingStateSequenceWorks(arr.l, other_states_next[0], last(other_states_next), all_but_last(other_paths), other_states_next, other_tid, last(other_paths), post_mover_state); lemma_AllButLastPlusLastIsSeq(other_paths); if !other_tau { forall i | 0 <= i < |other_states| ensures OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) { if i < |other_states_next| { assert other_states'[i] == other_states_next[i]; assert OKAndPCTypesMatch(arr, other_states_next[i], other_states[i], other_tid); } else { assert |other_states_next| == |all_but_last(other_states)| == |other_states|-1 == i; assert other_states'[i] == post_mover_state; assert other_states[i] == last(other_states); lemma_ExecutingPathDoesntChangeOtherActorPCType(arr, last(other_states), post_mover_state, mover_path, mover_tid, other_tid); assert OKAndPCTypesMatch(arr, post_mover_state, last(other_states), other_tid); } } } } lemma lemma_MoveLeftMoversLeftAsSinglePaths( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_states:seq, mover_paths:seq, other_states:seq, other_paths:seq ) returns ( mover_states':seq, other_states':seq ) requires ValidAtomicReductionRequest(arr) requires |other_states| > 0 requires arr.inv(other_states[0]) requires AtomicNextMultiplePaths(arr.l, other_states[0], last(other_states), other_paths, other_tid) requires other_states == AtomicGetStateSequence(arr.l, other_states[0], other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires |mover_states| > 1 requires last(other_states) == mover_states[0] requires AtomicNextMultiplePaths(arr.l, mover_states[0], last(mover_states), mover_paths, mover_tid) requires mover_states == AtomicGetStateSequence(arr.l, mover_states[0], mover_paths, mover_tid) requires forall path :: path in mover_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? requires forall i :: 0 <= i < |mover_states|-1 ==> IsPhase2(arr, mover_states[i], mover_tid) requires other_tau || other_tid != mover_tid requires arr.l.state_ok(last(mover_states)) ensures |mover_states'| == |mover_states| ensures |other_states'| == |other_states| ensures mover_states'[0] == other_states[0] ensures last(mover_states') == other_states'[0] ensures last(other_states') == last(mover_states) ensures AtomicNextMultiplePaths(arr.l, mover_states'[0], last(mover_states'), mover_paths, mover_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) ensures mover_states' == AtomicGetStateSequence(arr.l, mover_states'[0], mover_paths, mover_tid) ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) ensures forall i :: 0 <= i < |mover_states| ==> OKAndPCTypesMatch(arr, mover_states'[i], mover_states[i], mover_tid) ensures !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) decreases |mover_states| { assert |mover_paths| > 0; var pos := 0; assert mover_states[pos+1] == arr.l.path_next(mover_states[pos], mover_paths[pos], mover_tid); forall ensures arr.l.state_ok(mover_states[pos+1]) { if |mover_paths| == 1 { assert mover_states[pos+1] == last(mover_states); } else { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, mover_states[0], last(mover_states), mover_paths, mover_tid, mover_states, pos+1); } } var other_states_mid := lemma_MoveLeftMoverLeftAsSinglePaths( arr, mover_tid, other_tid, other_tau, mover_states[pos+1], mover_paths[pos], other_states, other_paths); if |mover_paths| == 1 { mover_states' := [other_states[0], other_states_mid[0]]; other_states' := other_states_mid; } else { var mover_states_next; lemma_LastOfDropIsLast(mover_states, 1); mover_states_next, other_states' := lemma_MoveLeftMoversLeftAsSinglePaths( arr, mover_tid, other_tid, other_tau, mover_states[1..], mover_paths[1..], other_states_mid, other_paths); mover_states' := [other_states[0]] + mover_states_next; lemma_LastOfConcatenationIsLastOfLatter([other_states[0]], mover_states_next); calc { last(other_states'); last(mover_states[1..]); last(mover_states); } forall i | 0 <= i < |mover_states| ensures OKAndPCTypesMatch(arr, mover_states'[i], mover_states[i], mover_tid) { if i > 0 { var j := i-1; assert OKAndPCTypesMatch(arr, mover_states_next[j], mover_states[1..][j], mover_tid); assert mover_states'[i] == mover_states_next[j]; assert mover_states[i] == mover_states[1..][j]; } else { assert mover_states'[i] == other_states[0]; assert mover_states[i] == mover_states[0]; } } if !other_tau { forall i | 0 <= i < |other_states| ensures OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) { assert OKAndPCTypesMatch(arr, other_states'[i], other_states_mid[i], other_tid); assert OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid); } } } } lemma lemma_PerformLeftMoveAll( arr:AtomicReductionRequest, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle, other_tau:bool, mover_paths:seq, mover_states:seq, other_paths:seq, other_states:seq ) returns ( mover_states':seq, other_states':seq ) requires ValidAtomicReductionRequest(arr) requires |other_states| > 0 requires arr.inv(other_states[0]) requires AtomicNextMultiplePaths(arr.l, other_states[0], last(other_states), other_paths, other_tid) requires other_states == AtomicGetStateSequence(arr.l, other_states[0], other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires |mover_states| > 0 requires last(other_states) == mover_states[0] requires AtomicNextMultiplePaths(arr.l, mover_states[0], last(mover_states), mover_paths, mover_tid) requires mover_states == AtomicGetStateSequence(arr.l, mover_states[0], mover_paths, mover_tid) requires forall path :: path in mover_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? requires forall i :: 0 <= i < |mover_states|-1 ==> IsPhase2(arr, mover_states[i], mover_tid) requires other_tau || other_tid != mover_tid requires arr.l.state_ok(last(mover_states)) ensures |mover_states'| == |mover_states| ensures |other_states'| == |other_states| ensures mover_states'[0] == other_states[0] ensures last(mover_states') == other_states'[0] ensures last(other_states') == last(mover_states) ensures AtomicNextMultiplePaths(arr.l, mover_states'[0], last(mover_states'), mover_paths, mover_tid) ensures AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), other_paths, other_tid) ensures mover_states' == AtomicGetStateSequence(arr.l, mover_states'[0], mover_paths, mover_tid) ensures other_states' == AtomicGetStateSequence(arr.l, other_states'[0], other_paths, other_tid) ensures forall i :: 0 <= i < |mover_states| ==> OKAndPCTypesMatch(arr, mover_states'[i], mover_states[i], mover_tid) ensures !other_tau ==> forall i :: 0 <= i < |other_states| ==> OKAndPCTypesMatch(arr, other_states'[i], other_states[i], other_tid) { if |mover_states| == 1 { lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType(arr, other_states[0], last(other_states), other_paths, other_tau, other_tid, mover_tid); mover_states' := [other_states[0]]; other_states' := other_states; } else { mover_states', other_states' := lemma_MoveLeftMoversLeftAsSinglePaths (arr, mover_tid, other_tid, other_tau, mover_states, mover_paths, other_states, other_paths); } } lemma lemma_PerformLeftMove( arr:AtomicReductionRequest, s1:LState, s2:LState, s3:LState, multistep1:Armada_Multistep, multistep2:Armada_Multistep ) returns ( s2':LState ) requires ValidAtomicReductionRequest(arr) requires s1 in AnnotatedReachables(GetRefinementViaReductionLSpec(arr)) requires arr.l.state_ok(s3) requires AtomicNextMultistep(arr.l, s1, s2, multistep1.steps, multistep1.tid, multistep1.tau) requires AtomicNextMultistep(arr.l, s2, s3, multistep2.steps, multistep2.tid, multistep2.tau) requires !multistep2.tau requires multistep1.tau || multistep1.tid != multistep2.tid requires IsPhase2(arr, s2, multistep2.tid) ensures AtomicNextMultistep(arr.l, s1, s2', multistep2.steps, multistep2.tid, multistep2.tau) ensures AtomicNextMultistep(arr.l, s2', s3, multistep1.steps, multistep1.tid, multistep1.tau) { lemma_StateAmongAnnotatedReachablesSatisfiesInv(arr, s1); assert arr.inv(s1); var mover_states := AtomicGetStateSequence(arr.l, s2, multistep2.steps, multistep2.tid); lemma_IfMultistepStartsInPhase2ThenEachPathDoes(arr, s2, s3, multistep2, mover_states); var other_states := AtomicGetStateSequence(arr.l, s1, multistep1.steps, multistep1.tid); lemma_AtomicNextLastElement(arr.l, s1, s2, multistep1.steps, multistep1.tid, other_states); lemma_AtomicNextLastElement(arr.l, s2, s3, multistep2.steps, multistep2.tid, mover_states); var mover_states', other_states' := lemma_PerformLeftMoveAll(arr, multistep2.tid, multistep1.tid, multistep1.tau, multistep2.steps, mover_states, multistep1.steps, other_states); s2' := last(mover_states'); lemma_AtomicNextLastElement(arr.l, s2', s3, multistep1.steps, multistep1.tid, other_states'); } lemma lemma_RightMoversCommute( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures RefinementViaReductionSpecModule.RightMoversCommute(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); var idmap := rr.idmap; var phase1 := rr.phase1; var phase2 := rr.phase2; forall initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep {:trigger RefinementViaReductionSpecModule.RightMoversCommuteConditions(rr, initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep)} | RefinementViaReductionSpecModule.RightMoversCommuteConditions(rr, initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep) ensures exists new_middle_state, other_multistep', right_mover' :: && ActionTuple(initial_state, new_middle_state, other_multistep') in rr.lspec.next && ActionTuple(new_middle_state, state_after_both_paths, right_mover') in rr.lspec.next && rr.idmap(other_multistep') == rr.idmap(other_multistep) && rr.idmap(right_mover') == rr.idmap(right_mover) { var new_middle_state:LState; new_middle_state := lemma_PerformRightMove(arr, initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep); assert ActionTuple(initial_state, new_middle_state, other_multistep) in rr.lspec.next; assert ActionTuple(new_middle_state, state_after_both_paths, right_mover) in rr.lspec.next; } } lemma lemma_LeftMoversCommute( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures RefinementViaReductionSpecModule.LeftMoversCommute(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); var idmap := rr.idmap; var phase1 := rr.phase1; var phase2 := rr.phase2; forall initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover {:trigger RefinementViaReductionSpecModule.LeftMoversCommuteConditions(rr, initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover)} | RefinementViaReductionSpecModule.LeftMoversCommuteConditions(rr, initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover) ensures exists new_middle_state, left_mover', other_multistep' :: && ActionTuple(initial_state, new_middle_state, left_mover') in rr.lspec.next && ActionTuple(new_middle_state, state_after_both_paths, other_multistep') in rr.lspec.next && rr.idmap(left_mover') == rr.idmap(left_mover) && rr.idmap(other_multistep') == rr.idmap(other_multistep) { var new_middle_state:LState; new_middle_state := lemma_PerformLeftMove(arr, initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover); assert ActionTuple(initial_state, new_middle_state, left_mover) in rr.lspec.next; assert ActionTuple(new_middle_state, state_after_both_paths, other_multistep) in rr.lspec.next; } } ////////////////////////////////////// // RIGHT MOVER CRASH PRESERVATION ////////////////////////////////////// lemma lemma_DemonstrateRightMoverCrashPreservationOneRightMoverPathOtherPathSequence( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_right_mover:LState, state_after_both_paths:LState, right_mover:LPath, right_mover_tid:Armada_ThreadHandle, other_multistep_paths:seq, other_multistep_tid:Armada_ThreadHandle, other_multistep_tau:bool, other_multistep_states:seq ) returns ( state_after_other_multistep':LState, other_multistep_states':seq ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires arr.l.path_valid(initial_state, right_mover, right_mover_tid) requires state_after_right_mover == arr.l.path_next(initial_state, right_mover, right_mover_tid) requires !arr.l.path_type(right_mover).AtomicPathType_Tau? requires AtomicNextMultiplePaths(arr.l, state_after_right_mover, state_after_both_paths, other_multistep_paths, other_multistep_tid) requires forall path :: path in other_multistep_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_multistep_tau requires other_multistep_states == AtomicGetStateSequence(arr.l, state_after_right_mover, other_multistep_paths, other_multistep_tid) requires !rr.crashed(initial_state) requires !rr.crashed(state_after_right_mover) requires rr.crashed(state_after_both_paths) requires IsPhase1(arr, state_after_right_mover, right_mover_tid) requires other_multistep_tau || other_multistep_tid != right_mover_tid ensures AtomicNextMultiplePaths(arr.l, initial_state, state_after_other_multistep', other_multistep_paths, other_multistep_tid) ensures other_multistep_states' == AtomicGetStateSequence(arr.l, initial_state, other_multistep_paths, other_multistep_tid) ensures |other_multistep_states'| == |other_multistep_states| ensures !other_multistep_tau ==> forall i :: 0 <= i < |other_multistep_states|-1 ==> OKAndPCTypesMatch(arr, other_multistep_states'[i], other_multistep_states[i], other_multistep_tid) ensures rr.crashed(state_after_other_multistep') ensures RefinementPair(state_after_both_paths, state_after_other_multistep') in rr.relation { assert |other_multistep_paths| > 0; assert arr.l.path_valid(state_after_right_mover, other_multistep_paths[0], other_multistep_tid); assert other_multistep_states[1] == arr.l.path_next(state_after_right_mover, other_multistep_paths[0], other_multistep_tid); if !other_multistep_tau { lemma_ExecutingPathDoesntChangeOtherActorPCType(arr, initial_state, state_after_right_mover, right_mover, right_mover_tid, other_multistep_tid); } if |other_multistep_paths| == 1 { other_multistep_states' := AtomicGetStateSequence(arr.l, initial_state, other_multistep_paths, other_multistep_tid); state_after_other_multistep' := arr.l.path_next(initial_state, other_multistep_paths[0], other_multistep_tid); lemma_AtomicNextLastElement(arr.l, state_after_right_mover, state_after_both_paths, other_multistep_paths, other_multistep_tid, other_multistep_states); assert AtomicReductionSpecModule.RightMoverCrashPreservationConditions(arr, initial_state, right_mover, other_multistep_paths[0], right_mover_tid, other_multistep_tid); return; } lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, state_after_right_mover, state_after_both_paths, other_multistep_paths, other_multistep_tid, other_multistep_states, 1); assert AtomicReductionSpecModule.RightMoversCommuteConditions(arr, initial_state, right_mover, other_multistep_paths[0], right_mover_tid, other_multistep_tid); assert arr.l.path_valid(initial_state, other_multistep_paths[0], other_multistep_tid); var s2' := arr.l.path_next(initial_state, other_multistep_paths[0], other_multistep_tid); assert arr.l.path_valid(s2', right_mover, right_mover_tid); assert other_multistep_states[1] == arr.l.path_next(s2', right_mover, right_mover_tid); var other_multistep_states_mid; state_after_other_multistep', other_multistep_states_mid := lemma_DemonstrateRightMoverCrashPreservationOneRightMoverPathOtherPathSequence( arr, rr, s2', other_multistep_states[1], state_after_both_paths, right_mover, right_mover_tid, other_multistep_paths[1..], other_multistep_tid, other_multistep_tau, other_multistep_states[1..]); other_multistep_states' := [initial_state] + other_multistep_states_mid; } lemma lemma_DemonstrateRightMoverCrashPreservationOneRightMoverOtherArmada_Multistep( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_right_mover:LState, state_after_both_paths:LState, other_multistep:Armada_Multistep, right_mover:LPath, right_mover_tid:Armada_ThreadHandle ) returns ( state_after_other_multistep':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires arr.l.path_valid(initial_state, right_mover, right_mover_tid) requires state_after_right_mover == arr.l.path_next(initial_state, right_mover, right_mover_tid) requires !arr.l.path_type(right_mover).AtomicPathType_Tau? requires AtomicNextMultistep(arr.l, state_after_right_mover, state_after_both_paths, other_multistep.steps, other_multistep.tid, other_multistep.tau) requires !rr.crashed(initial_state) requires !rr.crashed(state_after_right_mover) requires rr.crashed(state_after_both_paths) requires IsPhase1(arr, state_after_right_mover, right_mover_tid) requires other_multistep.tau || other_multistep.tid != right_mover_tid ensures AtomicNextMultistep(arr.l, initial_state, state_after_other_multistep', other_multistep.steps, other_multistep.tid, other_multistep.tau) ensures rr.crashed(state_after_other_multistep') ensures RefinementPair(state_after_both_paths, state_after_other_multistep') in rr.relation { var other_multistep_states := AtomicGetStateSequence(arr.l, state_after_right_mover, other_multistep.steps, other_multistep.tid); var other_multistep_states'; state_after_other_multistep', other_multistep_states' := lemma_DemonstrateRightMoverCrashPreservationOneRightMoverPathOtherPathSequence( arr, rr, initial_state, state_after_right_mover, state_after_both_paths, right_mover, right_mover_tid, other_multistep.steps, other_multistep.tid, other_multistep.tau, other_multistep_states); } lemma lemma_DemonstrateRightMoverCrashPreservationOneRightMoverOtherMultistep( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_right_mover:LState, state_after_both_paths:LState, other_multistep:Armada_Multistep, right_mover:LPath, right_mover_tid:Armada_ThreadHandle ) returns ( state_after_other_multistep':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires arr.l.path_valid(initial_state, right_mover, right_mover_tid) requires state_after_right_mover == arr.l.path_next(initial_state, right_mover, right_mover_tid) requires !arr.l.path_type(right_mover).AtomicPathType_Tau? requires ActionTuple(state_after_right_mover, state_after_both_paths, other_multistep) in rr.lspec.next requires !rr.crashed(initial_state) requires !rr.crashed(state_after_right_mover) requires rr.crashed(state_after_both_paths) requires IsPhase1(arr, state_after_right_mover, right_mover_tid) requires rr.idmap(other_multistep) != Some(right_mover_tid) ensures ActionTuple(initial_state, state_after_other_multistep', other_multistep) in rr.lspec.next ensures rr.crashed(state_after_other_multistep') ensures RefinementPair(state_after_both_paths, state_after_other_multistep') in rr.relation { state_after_other_multistep' := lemma_DemonstrateRightMoverCrashPreservationOneRightMoverOtherArmada_Multistep( arr, rr, initial_state, state_after_right_mover, state_after_both_paths, other_multistep, right_mover, right_mover_tid); } lemma lemma_DemonstrateRightMoverCrashPreservationRightMoverPathSequenceOtherMultistep( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_right_mover:LState, state_after_both_paths:LState, other_multistep:Armada_Multistep, right_mover_paths:seq, right_mover_tid:Armada_ThreadHandle, right_mover_states:seq ) returns ( state_after_other_multistep':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires AtomicNextMultiplePaths(arr.l, initial_state, state_after_right_mover, right_mover_paths, right_mover_tid) requires forall path :: path in right_mover_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? requires ActionTuple(state_after_right_mover, state_after_both_paths, other_multistep) in rr.lspec.next requires !rr.crashed(initial_state) requires !rr.crashed(state_after_right_mover) requires rr.crashed(state_after_both_paths) requires IsPhase1(arr, state_after_right_mover, right_mover_tid) requires right_mover_states == AtomicGetStateSequence(arr.l, initial_state, right_mover_paths, right_mover_tid) requires forall i :: 0 < i < |right_mover_states| ==> IsPhase1(arr, right_mover_states[i], right_mover_tid) requires rr.idmap(other_multistep) != Some(right_mover_tid) ensures ActionTuple(initial_state, state_after_other_multistep', other_multistep) in rr.lspec.next ensures rr.crashed(state_after_other_multistep') ensures RefinementPair(state_after_both_paths, state_after_other_multistep') in rr.relation decreases |right_mover_paths| { if |right_mover_paths| == 0 { state_after_other_multistep' := state_after_both_paths; return; } var pos := |right_mover_states|-2; var penultimate_state := right_mover_states[pos]; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, initial_state, state_after_right_mover, right_mover_paths, right_mover_tid, right_mover_states, pos); lemma_AtomicNextLastElement(arr.l, initial_state, state_after_right_mover, right_mover_paths, right_mover_tid, right_mover_states); lemma_AtomicInvariantHoldsAtIntermediateState(arr.l, arr.inv, initial_state, state_after_right_mover, right_mover_paths, right_mover_tid, right_mover_states, pos); var state_after_other_multistep_mid := lemma_DemonstrateRightMoverCrashPreservationOneRightMoverOtherMultistep( arr, rr, penultimate_state, state_after_right_mover, state_after_both_paths, other_multistep, last(right_mover_paths), right_mover_tid); if |right_mover_paths| == 1 { state_after_other_multistep' := state_after_other_multistep_mid; return; } lemma_AllButLastPreservesAtomicNextMultiplePaths(arr, initial_state, state_after_right_mover, right_mover_paths, right_mover_states, right_mover_tid); assert 0 < pos < |right_mover_states|; assert IsPhase1(arr, penultimate_state, right_mover_tid); state_after_other_multistep' := lemma_DemonstrateRightMoverCrashPreservationRightMoverPathSequenceOtherMultistep( arr, rr, initial_state, penultimate_state, state_after_other_multistep_mid, other_multistep, all_but_last(right_mover_paths), right_mover_tid, all_but_last(right_mover_states)); } lemma lemma_DemonstrateRightMoverCrashPreservationRightMoverArmada_MultistepOtherMultistep( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_right_mover:LState, state_after_both_paths:LState, right_mover:Armada_Multistep, other_multistep:Armada_Multistep ) returns ( state_after_other_multistep':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires AtomicNextMultistep(arr.l, initial_state, state_after_right_mover, right_mover.steps, right_mover.tid, right_mover.tau) requires ActionTuple(state_after_right_mover, state_after_both_paths, other_multistep) in rr.lspec.next requires !rr.crashed(initial_state) requires !rr.crashed(state_after_right_mover) requires rr.crashed(state_after_both_paths) requires IsPhase1(arr, state_after_right_mover, right_mover.tid) requires !right_mover.tau requires rr.idmap(other_multistep) != Some(right_mover.tid) ensures ActionTuple(initial_state, state_after_other_multistep', other_multistep) in rr.lspec.next ensures rr.crashed(state_after_other_multistep') ensures RefinementPair(state_after_both_paths, state_after_other_multistep') in rr.relation { var right_mover_states := AtomicGetStateSequence(arr.l, initial_state, right_mover.steps, right_mover.tid); lemma_IfMultistepEndsInPhase1ThenEachPathDoes(arr, initial_state, state_after_right_mover, right_mover, right_mover_states); state_after_other_multistep' := lemma_DemonstrateRightMoverCrashPreservationRightMoverPathSequenceOtherMultistep( arr, rr, initial_state, state_after_right_mover, state_after_both_paths, other_multistep, right_mover.steps, right_mover.tid, right_mover_states); } lemma lemma_DemonstrateRightMoverCrashPreservation( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_right_mover:LState, state_after_both_paths:LState, right_mover:Armada_Multistep, other_multistep:Armada_Multistep ) returns ( other_multistep':Armada_Multistep, state_after_other_multistep':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in rr.lspec.next requires ActionTuple(state_after_right_mover, state_after_both_paths, other_multistep) in rr.lspec.next requires !rr.crashed(initial_state) requires !rr.crashed(state_after_right_mover) requires rr.crashed(state_after_both_paths) requires rr.phase1(state_after_right_mover, rr.idmap(right_mover)) requires rr.idmap(right_mover) != rr.idmap(other_multistep) ensures rr.idmap(other_multistep') == rr.idmap(other_multistep) ensures ActionTuple(initial_state, state_after_other_multistep', other_multistep') in rr.lspec.next ensures rr.crashed(state_after_other_multistep') ensures RefinementPair(state_after_both_paths, state_after_other_multistep') in rr.relation { lemma_StateAmongAnnotatedReachablesSatisfiesInv(arr, initial_state); other_multistep' := other_multistep; state_after_other_multistep' := lemma_DemonstrateRightMoverCrashPreservationRightMoverArmada_MultistepOtherMultistep( arr, rr, initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep); } lemma lemma_RightMoverCrashPreservation( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures RefinementViaReductionSpecModule.RightMoverCrashPreservation(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep {:trigger RefinementViaReductionSpecModule.RightMoverCrashPreservationConditions(rr, initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep)} | RefinementViaReductionSpecModule.RightMoverCrashPreservationConditions(rr, initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep) && initial_state in AnnotatedReachables(rr.lspec) && ActionTuple(initial_state, state_after_right_mover, right_mover) in rr.lspec.next && ActionTuple(state_after_right_mover, state_after_both_paths, other_multistep) in rr.lspec.next && !rr.crashed(initial_state) && !rr.crashed(state_after_right_mover) && rr.crashed(state_after_both_paths) && rr.phase1(state_after_right_mover, rr.idmap(right_mover)) && rr.idmap(right_mover) != rr.idmap(other_multistep) ensures exists other_multistep', state_after_other_multistep' :: && rr.idmap(other_multistep') == rr.idmap(other_multistep) && ActionTuple(initial_state, state_after_other_multistep', other_multistep') in rr.lspec.next && rr.crashed(state_after_other_multistep') && RefinementPair(state_after_both_paths, state_after_other_multistep') in rr.relation { var other_multistep', state_after_other_multistep' := lemma_DemonstrateRightMoverCrashPreservation(arr, rr, initial_state, state_after_right_mover, state_after_both_paths, right_mover, other_multistep); } } ////////////////////////////////////// // LEFT MOVERS ENABLED BEFORE CRASH ////////////////////////////////////// lemma lemma_CombineLeftMoverSubpathsIntoPathsOneIteration( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, tid:Armada_ThreadHandle, states:seq, paths:seq, pos:int, multistates:seq, multipaths:seq>, partial_paths:seq, partial_multistates:seq ) returns ( pos':int, multistates':seq, multipaths':seq>, partial_paths':seq, partial_multistates':seq ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires |states| > 0 requires AtomicNextMultiplePaths(arr.l, states[0], last(states), paths, tid) requires states == AtomicGetStateSequence(arr.l, states[0], paths, tid) requires arr.inv(states[0]) requires IsPhase2(arr, states[0], tid) requires AtomicThreadYielding(arr.l, states[0], tid) requires forall i :: 0 <= i < |states|-1 ==> IsPhase2(arr, states[i], tid) requires !IsPhase2(arr, last(states), tid) requires AtomicThreadYielding(arr.l, last(states), tid) requires forall path :: path in paths ==> !arr.l.path_type(path).AtomicPathType_Tau? requires 0 <= pos < |paths| requires |multistates| > 0 requires multistates[0] == states[0] requires StateNextSeq(multistates, multipaths, rr.lspec.next) requires forall multipath :: multipath in multipaths ==> !multipath.tau && multipath.tid == tid requires AtomicThreadYielding(arr.l, last(multistates), tid) requires AtomicThreadYielding(arr.l, states[pos], tid) ==> |partial_paths| == 0 requires AtomicNextMultiplePaths(arr.l, last(multistates), states[pos], partial_paths, tid) requires partial_multistates == AtomicGetStateSequence(arr.l, last(multistates), partial_paths, tid) requires forall i :: 0 < i < |partial_multistates| ==> !AtomicThreadYielding(arr.l, partial_multistates[i], tid) requires forall path :: path in partial_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? requires if pos < |paths| then ( forall s :: s in multistates ==> IsPhase2(arr, s, tid) ) else ( forall i :: 0 <= i < |multistates|-1 ==> IsPhase2(arr, multistates[i], tid) ) ensures pos' == pos + 1 ensures |multistates'| > 0 ensures multistates'[0] == states[0] ensures StateNextSeq(multistates', multipaths', rr.lspec.next) ensures forall multipath :: multipath in multipaths' ==> !multipath.tau && multipath.tid == tid ensures AtomicThreadYielding(arr.l, last(multistates'), tid) ensures AtomicThreadYielding(arr.l, states[pos'], tid) ==> |partial_paths'| == 0 ensures AtomicNextMultiplePaths(arr.l, last(multistates'), states[pos'], partial_paths', tid) ensures partial_multistates' == AtomicGetStateSequence(arr.l, last(multistates'), partial_paths', tid) ensures forall i :: 0 < i < |partial_multistates'| ==> !AtomicThreadYielding(arr.l, partial_multistates'[i], tid) ensures forall path :: path in partial_paths' ==> !arr.l.path_type(path).AtomicPathType_Tau? ensures if pos' < |paths| then ( forall s :: s in multistates' ==> IsPhase2(arr, s, tid) ) else ( forall i :: 0 <= i < |multistates'|-1 ==> IsPhase2(arr, multistates'[i], tid) ) { var s_current := states[pos]; var s_next := states[pos+1]; var next_path := paths[pos]; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, states[0], last(states), paths, tid, states, pos); assert arr.l.path_valid(s_current, next_path, tid); assert s_next == arr.l.path_next(s_current, next_path, tid); var pc' := arr.l.get_thread_pc(s_next, tid); lemma_ExtendingStateSequenceWorks(arr.l, last(multistates), s_current, partial_paths, partial_multistates, tid, next_path, s_next); var upper := |partial_multistates|; assert forall i :: 0 < i < upper ==> !AtomicThreadYielding(arr.l, partial_multistates[i], tid); partial_paths' := partial_paths + [next_path]; partial_multistates' := partial_multistates + [s_next]; assert upper == |partial_paths'|; assert forall i :: 0 < i < |partial_paths'| ==> !AtomicThreadYielding(arr.l, partial_multistates'[i], tid); if AtomicThreadYielding(arr.l, s_next, tid) { var multipath := Armada_Multistep(partial_paths', tid, false); assert AtomicNextMultistep(arr.l, last(multistates), s_next, multipath.steps, multipath.tid, multipath.tau); assert ActionTuple(last(multistates), s_next, multipath) in rr.lspec.next; multistates' := multistates + [s_next]; multipaths' := multipaths + [multipath]; forall i | 0 <= i < |multipaths'| ensures ActionTuple(multistates'[i], multistates'[i+1], multipaths'[i]) in rr.lspec.next { if i < |multipaths| { assert ActionTuple(multistates[i], multistates[i+1], multipaths[i]) in rr.lspec.next; assert multistates'[i] == multistates[i]; assert multistates'[i+1] == multistates[i+1]; assert multipaths'[i] == multipaths[i]; } else { assert i == |multipaths| == |multistates|-1; assert multistates'[i] == multistates[i] == last(multistates); assert multistates'[i+1] == s_next; assert multipaths'[i] == multipath; } } assert StateNextSeq(multistates', multipaths', rr.lspec.next); partial_paths' := []; partial_multistates' := [s_next]; } else { multistates' := multistates; multipaths' := multipaths; } pos' := pos + 1; assert AtomicNextMultiplePaths(arr.l, last(multistates'), states[pos'], partial_paths', tid); } lemma lemma_CombineLeftMoverSubpathsIntoPaths( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, tid:Armada_ThreadHandle, states:seq, paths:seq ) returns ( multistates:seq, multipaths:seq> ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires |states| > 0 requires AtomicNextMultiplePaths(arr.l, states[0], last(states), paths, tid) requires states == AtomicGetStateSequence(arr.l, states[0], paths, tid) requires arr.inv(states[0]) requires AtomicThreadYielding(arr.l, states[0], tid) requires IsPhase2(arr, states[0], tid) requires forall i :: 0 <= i < |states|-1 ==> IsPhase2(arr, states[i], tid) requires !IsPhase2(arr, last(states), tid) requires AtomicThreadYielding(arr.l, last(states), tid) requires forall path :: path in paths ==> !arr.l.path_type(path).AtomicPathType_Tau? ensures StateNextSeq(multistates, multipaths, rr.lspec.next) ensures |multistates| > 0 ensures multistates[0] == states[0] ensures last(multistates) == last(states) ensures forall i :: 0 <= i < |multistates|-1 ==> IsPhase2(arr, multistates[i], tid) ensures forall multipath :: multipath in multipaths ==> !multipath.tau && multipath.tid == tid { multistates := [states[0]]; multipaths := []; var partial_paths := []; var partial_multistates := [states[0]]; var pos := 0; assert AtomicThreadYielding(arr.l, states[pos], tid) ==> |partial_paths| == 0; while pos < |paths| invariant 0 <= pos <= |paths| invariant |multistates| > 0 invariant multistates[0] == states[0] invariant StateNextSeq(multistates, multipaths, rr.lspec.next) invariant forall multipath :: multipath in multipaths ==> !multipath.tau && multipath.tid == tid invariant AtomicThreadYielding(arr.l, last(multistates), tid) invariant AtomicThreadYielding(arr.l, states[pos], tid) ==> |partial_paths| == 0 invariant AtomicNextMultiplePaths(arr.l, last(multistates), states[pos], partial_paths, tid) invariant partial_multistates == AtomicGetStateSequence(arr.l, last(multistates), partial_paths, tid) invariant forall i :: 0 < i < |partial_multistates| ==> !AtomicThreadYielding(arr.l, partial_multistates[i], tid) invariant forall path :: path in partial_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? invariant if pos < |paths| then ( forall s :: s in multistates ==> IsPhase2(arr, s, tid) ) else ( forall i :: 0 <= i < |multistates|-1 ==> IsPhase2(arr, multistates[i], tid) ) decreases |paths| - pos { pos, multistates, multipaths, partial_paths, partial_multistates := lemma_CombineLeftMoverSubpathsIntoPathsOneIteration( arr, rr, tid, states, paths, pos, multistates, multipaths, partial_paths, partial_multistates); } } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashPathSequencePart2( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, post_crash_state:LState, crash_path_paths:seq, crash_path_tid:Armada_ThreadHandle, crash_path_tau:bool, crash_path_states:seq, left_mover_tid:Armada_ThreadHandle, left_mover_states_mid:seq, left_mover_paths_mid:seq, post_crash_state':LState ) returns ( left_mover_states:seq, left_mover_paths:seq>, crash_path_states':seq ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires AtomicNextMultiplePaths(arr.l, initial_state, post_crash_state, crash_path_paths, crash_path_tid) requires forall path :: path in crash_path_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == crash_path_tau requires crash_path_states == AtomicGetStateSequence(arr.l, initial_state, crash_path_paths, crash_path_tid) requires !rr.crashed(initial_state) requires rr.crashed(post_crash_state) requires crash_path_tau || crash_path_tid != left_mover_tid requires IsPhase2(arr, initial_state, left_mover_tid) requires AtomicThreadYielding(arr.l, initial_state, left_mover_tid) requires |left_mover_states_mid| > 1 requires AtomicNextMultiplePaths(arr.l, crash_path_states[|crash_path_states|-2], last(left_mover_states_mid), left_mover_paths_mid, left_mover_tid) requires left_mover_states_mid == AtomicGetStateSequence(arr.l, crash_path_states[|crash_path_states|-2], left_mover_paths_mid, left_mover_tid) requires arr.l.state_ok(last(left_mover_states_mid)) requires !IsPhase2(arr, last(left_mover_states_mid), left_mover_tid) requires AtomicThreadYielding(arr.l, last(left_mover_states_mid), left_mover_tid) requires forall i :: 0 <= i < |left_mover_states_mid|-1 ==> IsPhase2(arr, left_mover_states_mid[i], left_mover_tid) requires forall path :: path in left_mover_paths_mid ==> !arr.l.path_type(path).AtomicPathType_Tau? requires arr.l.path_valid(last(left_mover_states_mid), last(crash_path_paths), crash_path_tid) requires post_crash_state' == arr.l.path_next(last(left_mover_states_mid), last(crash_path_paths), crash_path_tid); requires rr.crashed(post_crash_state') requires RefinementPair(post_crash_state, post_crash_state') in rr.relation ensures StateNextSeq(left_mover_states, left_mover_paths, rr.lspec.next) ensures left_mover_states[0] == initial_state ensures arr.l.state_ok(last(left_mover_states)) ensures !IsPhase2(arr, last(left_mover_states), left_mover_tid) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> IsPhase2(arr, left_mover_states[i], left_mover_tid) ensures forall path :: path in left_mover_paths ==> rr.idmap(path) == Some(left_mover_tid) ensures AtomicNextMultiplePaths(arr.l, last(left_mover_states), post_crash_state', crash_path_paths, crash_path_tid) ensures crash_path_states' == AtomicGetStateSequence(arr.l, last(left_mover_states), crash_path_paths, crash_path_tid) ensures |crash_path_states'| == |crash_path_states| ensures !crash_path_tau ==> forall i :: 0 <= i < |crash_path_states|-1 ==> OKAndPCTypesMatch(arr, crash_path_states'[i], crash_path_states[i], crash_path_tid) { var pos := |crash_path_states|-2; var penultimate_state := crash_path_states[pos]; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, initial_state, post_crash_state, crash_path_paths, crash_path_tid, crash_path_states, pos); lemma_AtomicNextLastElement(arr.l, initial_state, post_crash_state, crash_path_paths, crash_path_tid, crash_path_states); lemma_AtomicInvariantHoldsAtIntermediateState(arr.l, arr.inv, initial_state, post_crash_state, crash_path_paths, crash_path_tid, crash_path_states, pos); lemma_AllButLastPreservesAtomicNextMultiplePaths(arr, initial_state, post_crash_state, crash_path_paths, crash_path_states, crash_path_tid); var mover_states', other_states' := lemma_MoveLeftMoversLeftAsSinglePaths( arr, left_mover_tid, crash_path_tid, crash_path_tau, left_mover_states_mid, left_mover_paths_mid, all_but_last(crash_path_states), all_but_last(crash_path_paths)); left_mover_states, left_mover_paths := lemma_CombineLeftMoverSubpathsIntoPaths(arr, rr, left_mover_tid, mover_states', left_mover_paths_mid); assert last(left_mover_states) == last(mover_states') == other_states'[0]; assert AtomicNextMultiplePaths(arr.l, other_states'[0], last(other_states'), all_but_last(crash_path_paths), crash_path_tid); assert last(other_states') == last(left_mover_states_mid); lemma_ExtendingStateSequenceWorks(arr.l, last(left_mover_states), last(left_mover_states_mid), all_but_last(crash_path_paths), other_states', crash_path_tid, last(crash_path_paths), post_crash_state'); lemma_AllButLastPlusLastIsSeq(crash_path_paths); assert AtomicNextMultiplePaths(arr.l, last(left_mover_states), post_crash_state', crash_path_paths, crash_path_tid); crash_path_states' := other_states' + [post_crash_state']; if !crash_path_tau { forall i | 0 <= i < |crash_path_states|-1 ensures OKAndPCTypesMatch(arr, crash_path_states'[i], crash_path_states[i], crash_path_tid) { assert crash_path_states'[i] == other_states'[i]; assert all_but_last(crash_path_states)[i] == crash_path_states[i]; } } } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashPath( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, post_crash_state:LState, crash_path:LPath, crash_path_tid:Armada_ThreadHandle, crash_path_tau:bool, left_mover_tid:Armada_ThreadHandle ) returns ( left_mover_states:seq, left_mover_paths:seq, post_crash_state':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires arr.l.path_valid(initial_state, crash_path, crash_path_tid) requires post_crash_state == arr.l.path_next(initial_state, crash_path, crash_path_tid) requires arr.l.path_type(crash_path).AtomicPathType_Tau? == crash_path_tau requires !rr.crashed(initial_state) requires rr.crashed(post_crash_state) requires crash_path_tau || crash_path_tid != left_mover_tid requires IsPhase2(arr, initial_state, left_mover_tid) ensures |left_mover_states| > 0 ensures AtomicNextMultiplePaths(arr.l, initial_state, last(left_mover_states), left_mover_paths, left_mover_tid) ensures left_mover_states == AtomicGetStateSequence(arr.l, initial_state, left_mover_paths, left_mover_tid) ensures arr.l.state_ok(last(left_mover_states)) ensures !IsPhase2(arr, last(left_mover_states), left_mover_tid) ensures AtomicThreadYielding(arr.l, last(left_mover_states), left_mover_tid) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> IsPhase2(arr, left_mover_states[i], left_mover_tid) ensures |left_mover_paths| > 0 ensures forall path :: path in left_mover_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? ensures arr.l.path_valid(last(left_mover_states), crash_path, crash_path_tid) ensures post_crash_state' == arr.l.path_next(last(left_mover_states), crash_path, crash_path_tid) ensures !crash_path_tau ==> OKAndPCTypesMatch(arr, last(left_mover_states), initial_state, crash_path_tid) ensures rr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in rr.relation decreases arr.left_mover_generation_progress(initial_state, left_mover_tid) { assert AtomicReductionSpecModule.LeftMoversAlwaysEnabledConditions(arr, initial_state, left_mover_tid); var left_mover_path := arr.generate_left_mover(initial_state, left_mover_tid); var state_after_left_mover := arr.l.path_next(initial_state, left_mover_path, left_mover_tid); assert && arr.l.path_valid(initial_state, left_mover_path, left_mover_tid) && !arr.l.path_type(left_mover_path).AtomicPathType_Tau? && 0 <= arr.left_mover_generation_progress(state_after_left_mover, left_mover_tid) < arr.left_mover_generation_progress(initial_state, left_mover_tid); assert AtomicReductionSpecModule.LeftMoverCrashPreservationConditions(arr, initial_state, left_mover_path, crash_path, left_mover_tid, crash_path_tid); var state_after_both_paths := arr.l.path_next(state_after_left_mover, crash_path, crash_path_tid); assert && arr.l.path_valid(state_after_left_mover, crash_path, crash_path_tid) && !arr.l.state_ok(state_after_both_paths) && RefinementPair(post_crash_state, state_after_both_paths) in arr.self_relation; if !IsPhase2(arr, state_after_left_mover, left_mover_tid) { assert AtomicThreadYielding(arr.l, state_after_left_mover, left_mover_tid); left_mover_states := [initial_state, state_after_left_mover]; left_mover_paths := [left_mover_path]; post_crash_state' := state_after_both_paths; } else { var left_mover_states_next, left_mover_paths_next; left_mover_states_next, left_mover_paths_next, post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashPath( arr, rr, state_after_left_mover, state_after_both_paths, crash_path, crash_path_tid, crash_path_tau, left_mover_tid); left_mover_states := [initial_state] + left_mover_states_next; left_mover_paths := [left_mover_path] + left_mover_paths_next; lemma_LastOfConcatenationIsLastOfLatter([initial_state], left_mover_states_next); } } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashPathSequence ( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, post_crash_state:LState, crash_path_paths:seq, crash_path_tid:Armada_ThreadHandle, crash_path_tau:bool, crash_path_states:seq, left_mover_tid:Armada_ThreadHandle ) returns ( left_mover_states:seq, left_mover_paths:seq>, crash_path_states':seq, post_crash_state':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires AtomicNextMultiplePaths(arr.l, initial_state, post_crash_state, crash_path_paths, crash_path_tid) requires forall path :: path in crash_path_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == crash_path_tau requires crash_path_states == AtomicGetStateSequence(arr.l, initial_state, crash_path_paths, crash_path_tid) requires !rr.crashed(initial_state) requires rr.crashed(post_crash_state) requires crash_path_tau || crash_path_tid != left_mover_tid requires IsPhase2(arr, initial_state, left_mover_tid) requires AtomicThreadYielding(arr.l, initial_state, left_mover_tid) ensures StateNextSeq(left_mover_states, left_mover_paths, rr.lspec.next) ensures left_mover_states[0] == initial_state ensures arr.l.state_ok(last(left_mover_states)) ensures !IsPhase2(arr, last(left_mover_states), left_mover_tid) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> IsPhase2(arr, left_mover_states[i], left_mover_tid) ensures forall path :: path in left_mover_paths ==> rr.idmap(path) == Some(left_mover_tid) ensures AtomicNextMultiplePaths(arr.l, last(left_mover_states), post_crash_state', crash_path_paths, crash_path_tid) ensures crash_path_states' == AtomicGetStateSequence(arr.l, last(left_mover_states), crash_path_paths, crash_path_tid) ensures |crash_path_states'| == |crash_path_states| ensures !crash_path_tau ==> forall i :: 0 <= i < |crash_path_states|-1 ==> OKAndPCTypesMatch(arr, crash_path_states'[i], crash_path_states[i], crash_path_tid) ensures rr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in rr.relation { assert |crash_path_paths| > 0; var pos := |crash_path_states|-2; var penultimate_state := crash_path_states[pos]; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, initial_state, post_crash_state, crash_path_paths, crash_path_tid, crash_path_states, pos); lemma_AtomicNextLastElement(arr.l, initial_state, post_crash_state, crash_path_paths, crash_path_tid, crash_path_states); lemma_AtomicInvariantHoldsAtIntermediateState(arr.l, arr.inv, initial_state, post_crash_state, crash_path_paths, crash_path_tid, crash_path_states, pos); lemma_AllButLastPreservesAtomicNextMultiplePaths(arr, initial_state, post_crash_state, crash_path_paths, crash_path_states, crash_path_tid); lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType(arr, initial_state, penultimate_state, all_but_last(crash_path_paths), crash_path_tau, crash_path_tid, left_mover_tid); var left_mover_states_mid, left_mover_paths_mid; left_mover_states_mid, left_mover_paths_mid, post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashPath( arr, rr, penultimate_state, post_crash_state, last(crash_path_paths), crash_path_tid, crash_path_tau, left_mover_tid); left_mover_states, left_mover_paths, crash_path_states' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashPathSequencePart2( arr, rr, initial_state, post_crash_state, crash_path_paths, crash_path_tid, crash_path_tau, crash_path_states, left_mover_tid, left_mover_states_mid, left_mover_paths_mid, post_crash_state'); } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashArmada_Multistep ( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, post_crash_state:LState, crash_multistep:Armada_Multistep, left_mover_tid:Armada_ThreadHandle ) returns ( left_mover_states:seq, left_mover_multisteps:seq>, post_crash_state':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires AtomicNextMultistep(arr.l, initial_state, post_crash_state, crash_multistep.steps, crash_multistep.tid, crash_multistep.tau) requires !rr.crashed(initial_state) requires rr.crashed(post_crash_state) requires crash_multistep.tau || crash_multistep.tid != left_mover_tid requires IsPhase2(arr, initial_state, left_mover_tid) requires AtomicThreadYielding(arr.l, initial_state, left_mover_tid) ensures StateNextSeq(left_mover_states, left_mover_multisteps, rr.lspec.next) ensures left_mover_states[0] == initial_state ensures arr.l.state_ok(last(left_mover_states)) ensures !IsPhase2(arr, last(left_mover_states), left_mover_tid) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> IsPhase2(arr, left_mover_states[i], left_mover_tid) ensures forall path :: path in left_mover_multisteps ==> rr.idmap(path) == Some(left_mover_tid) ensures AtomicNextMultistep(arr.l, last(left_mover_states), post_crash_state', crash_multistep.steps, crash_multistep.tid, crash_multistep.tau) ensures rr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in rr.relation { var crash_multistep_states := AtomicGetStateSequence(arr.l, initial_state, crash_multistep.steps, crash_multistep.tid); var crash_multistep_states'; left_mover_states, left_mover_multisteps, crash_multistep_states', post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashPathSequence( arr, rr, initial_state, post_crash_state, crash_multistep.steps, crash_multistep.tid, crash_multistep.tau, crash_multistep_states, left_mover_tid); } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrash( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, post_crash_state:LState, crash_multistep:Armada_Multistep, left_mover_tid:Armada_ThreadHandle ) returns ( left_mover_states:seq, left_mover_multisteps:seq>, post_crash_state':LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, post_crash_state, crash_multistep) in rr.lspec.next requires !rr.crashed(initial_state) requires rr.crashed(post_crash_state) requires rr.idmap(crash_multistep) != Some(left_mover_tid) requires IsPhase2(arr, initial_state, left_mover_tid) ensures StateNextSeq(left_mover_states, left_mover_multisteps, rr.lspec.next) ensures left_mover_states[0] == initial_state ensures arr.l.state_ok(last(left_mover_states)) ensures !IsPhase2(arr, last(left_mover_states), left_mover_tid) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> IsPhase2(arr, left_mover_states[i], left_mover_tid) ensures forall path :: path in left_mover_multisteps ==> rr.idmap(path) == Some(left_mover_tid) ensures ActionTuple(last(left_mover_states), post_crash_state', crash_multistep) in rr.lspec.next ensures rr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in rr.relation { lemma_StateAmongAnnotatedReachablesSatisfiesInv(arr, initial_state); lemma_StateAmongAnnotatedReachablesHasThreadYielding(arr, initial_state, left_mover_tid); left_mover_states, left_mover_multisteps, post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCrashArmada_Multistep( arr, rr, initial_state, post_crash_state, crash_multistep, left_mover_tid); assert AtomicNextMultistep(arr.l, last(left_mover_states), post_crash_state', crash_multistep.steps, crash_multistep.tid, crash_multistep.tau); assert ActionTuple(last(left_mover_states), post_crash_state', crash_multistep) in rr.lspec.next; } lemma lemma_LeftMoversEnabledBeforeCrash( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures LeftMoversEnabledBeforeCrash(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall initial_state, post_crash_state, crash_multistep, actor {:trigger RefinementViaReductionSpecModule.LeftMoversEnabledBeforeCrashConditions(rr, initial_state, post_crash_state, crash_multistep, actor)} | RefinementViaReductionSpecModule.LeftMoversEnabledBeforeCrashConditions(rr, initial_state, post_crash_state, crash_multistep, actor) ensures exists states, trace, crash_multistep', post_crash_state' :: && StateNextSeq(states, trace, rr.lspec.next) && states[0] == initial_state && !rr.crashed(last(states)) && !rr.phase2(last(states), actor) && (forall i :: 0 <= i < |states|-1 ==> rr.phase2(states[i], actor)) && (forall path :: path in trace ==> rr.idmap(path) == actor) && ActionTuple(last(states), post_crash_state', crash_multistep') in rr.lspec.next && rr.idmap(crash_multistep') == rr.idmap(crash_multistep) && rr.crashed(post_crash_state') && RefinementPair(post_crash_state, post_crash_state') in rr.relation { var states, trace, post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrash(arr, rr, initial_state, post_crash_state, crash_multistep, actor.v); } } ////////////////////////////////////////// // LEFT MOVER SELF CRASH PRESERVATION ////////////////////////////////////////// lemma lemma_DemonstrateLeftMoverSinglePathSelfCrashPreservationAcrossSinglePath ( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_other_path:LState, state_after_both_paths:LState, other_path:LPath, mover_path:LPath, mover_tid:Armada_ThreadHandle, other_tau:bool, other_tid:Armada_ThreadHandle ) returns ( state_after_left_mover:LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires arr.l.path_valid(initial_state, other_path, other_tid) requires state_after_other_path == arr.l.path_next(initial_state, other_path, other_tid) requires arr.l.path_type(other_path).AtomicPathType_Tau? == other_tau requires arr.l.path_valid(state_after_other_path, mover_path, mover_tid) requires state_after_both_paths == arr.l.path_next(state_after_other_path, mover_path, mover_tid) requires !arr.l.path_type(mover_path).AtomicPathType_Tau? requires !rr.crashed(initial_state) requires !rr.crashed(state_after_other_path) requires rr.crashed(state_after_both_paths) requires IsPhase2(arr, state_after_other_path, mover_tid) requires other_tau || other_tid != mover_tid ensures arr.l.path_valid(initial_state, mover_path, mover_tid) ensures state_after_left_mover == arr.l.path_next(initial_state, mover_path, mover_tid) ensures rr.crashed(state_after_left_mover) ensures RefinementPair(state_after_both_paths, state_after_left_mover) in rr.relation { assert AtomicReductionSpecModule.LeftMoverSelfCrashPreservationConditions(arr, initial_state, mover_path, other_path, mover_tid, other_tid); state_after_left_mover := arr.l.path_next(initial_state, mover_path, mover_tid); } lemma lemma_DemonstrateLeftMoverSinglePathSelfCrashPreservationAcrossPathSequence ( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_other_path:LState, state_after_both_paths:LState, other_paths:seq, mover_path:LPath, mover_tid:Armada_ThreadHandle, other_tau:bool, other_tid:Armada_ThreadHandle ) returns ( state_after_left_mover:LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires AtomicNextMultiplePaths(arr.l, initial_state, state_after_other_path, other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires arr.l.path_valid(state_after_other_path, mover_path, mover_tid) requires state_after_both_paths == arr.l.path_next(state_after_other_path, mover_path, mover_tid) requires !arr.l.path_type(mover_path).AtomicPathType_Tau? requires !rr.crashed(initial_state) requires !rr.crashed(state_after_other_path) requires rr.crashed(state_after_both_paths) requires IsPhase2(arr, state_after_other_path, mover_tid) requires other_tau || other_tid != mover_tid ensures arr.l.path_valid(initial_state, mover_path, mover_tid) ensures state_after_left_mover == arr.l.path_next(initial_state, mover_path, mover_tid) ensures rr.crashed(state_after_left_mover) ensures RefinementPair(state_after_both_paths, state_after_left_mover) in rr.relation { if |other_paths| == 0 { state_after_left_mover := state_after_both_paths; return; } var other_states := AtomicGetStateSequence(arr.l, initial_state, other_paths, other_tid); var pos := |other_states|-2; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, initial_state, state_after_other_path, other_paths, other_tid, other_states, pos); lemma_AtomicNextLastElement(arr.l, initial_state, state_after_other_path, other_paths, other_tid, other_states); lemma_AllButLastPreservesAtomicNextMultiplePaths(arr, initial_state, state_after_other_path, other_paths, other_states, other_tid); lemma_AtomicInvariantHoldsAtIntermediateState(arr.l, arr.inv, initial_state, state_after_other_path, other_paths, other_tid, other_states, pos); var state_after_left_mover_mid := lemma_DemonstrateLeftMoverSinglePathSelfCrashPreservationAcrossSinglePath( arr, rr, other_states[pos], other_states[pos+1], state_after_both_paths, last(other_paths), mover_path, mover_tid, other_tau, other_tid); state_after_left_mover := lemma_DemonstrateLeftMoverSinglePathSelfCrashPreservationAcrossPathSequence( arr, rr, initial_state, other_states[pos], state_after_left_mover_mid, all_but_last(other_paths), mover_path, mover_tid, other_tau, other_tid); } lemma lemma_DemonstrateLeftMoverSelfCrashPreservationPathSequence( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_other_path:LState, state_after_both_paths:LState, other_paths:seq, mover_paths:seq, mover_states:seq, mover_tid:Armada_ThreadHandle, other_tau:bool, other_tid:Armada_ThreadHandle ) returns ( state_after_left_mover:LState, mover_states':seq ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires AtomicNextMultiplePaths(arr.l, initial_state, state_after_other_path, other_paths, other_tid) requires forall path :: path in other_paths ==> arr.l.path_type(path).AtomicPathType_Tau? == other_tau requires AtomicNextMultiplePaths(arr.l, state_after_other_path, state_after_both_paths, mover_paths, mover_tid) requires mover_states == AtomicGetStateSequence(arr.l, state_after_other_path, mover_paths, mover_tid) requires forall path :: path in mover_paths ==> !arr.l.path_type(path).AtomicPathType_Tau? requires forall i :: 0 <= i < |mover_states|-1 ==> IsPhase2(arr, mover_states[i], mover_tid) requires !rr.crashed(initial_state) requires !rr.crashed(state_after_other_path) requires rr.crashed(state_after_both_paths) requires IsPhase2(arr, state_after_other_path, mover_tid) requires other_tau || other_tid != mover_tid ensures AtomicNextMultiplePaths(arr.l, initial_state, state_after_left_mover, mover_paths, mover_tid) ensures mover_states' == AtomicGetStateSequence(arr.l, initial_state, mover_paths, mover_tid) ensures |mover_states'| == |mover_states| ensures forall i :: 0 <= i < |mover_states|-1 ==> OKAndPCTypesMatch(arr, mover_states[i], mover_states'[i], mover_tid) ensures rr.crashed(state_after_left_mover) ensures RefinementPair(state_after_both_paths, state_after_left_mover) in rr.relation { assert state_after_both_paths != state_after_other_path; assert |mover_paths| > 0; var pos := |mover_states|-2; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, state_after_other_path, state_after_both_paths, mover_paths, mover_tid, mover_states, pos); assert !rr.crashed(mover_states[pos]); var other_states := AtomicGetStateSequence(arr.l, initial_state, other_paths, other_tid); lemma_AtomicNextLastElement(arr.l, initial_state, state_after_other_path, other_paths, other_tid, other_states); lemma_AtomicNextLastElement(arr.l, state_after_other_path, state_after_both_paths, mover_paths, mover_tid, mover_states); lemma_AllButLastPreservesAtomicNextMultiplePaths(arr, state_after_other_path, state_after_both_paths, mover_paths, mover_states, mover_tid); var mover_states_mid, other_states_mid; if |mover_paths| == 1 { mover_states_mid := [initial_state]; other_states_mid := other_states; forall i | 0 <= i < |all_but_last(mover_states)| ensures OKAndPCTypesMatch(arr, mover_states_mid[i], all_but_last(mover_states)[i], mover_tid) { assert mover_states_mid[i] == initial_state; assert all_but_last(mover_states)[i] == state_after_other_path; lemma_ExecutingPathSequenceDoesntChangeOtherActorPCType(arr, initial_state, state_after_other_path, other_paths, other_tau, other_tid, mover_tid); } } else { mover_states_mid, other_states_mid := lemma_MoveLeftMoversLeftAsSinglePaths(arr, mover_tid, other_tid, other_tau, all_but_last(mover_states), all_but_last(mover_paths), other_states, other_paths); } assert forall i :: 0 <= i < |all_but_last(mover_states)| ==> OKAndPCTypesMatch(arr, mover_states_mid[i], all_but_last(mover_states)[i], mover_tid); lemma_AtomicInvariantHoldsAtIntermediateState(arr.l, arr.inv, initial_state, last(mover_states_mid), all_but_last(mover_paths), mover_tid, mover_states_mid, |mover_states_mid|-1); state_after_left_mover := lemma_DemonstrateLeftMoverSinglePathSelfCrashPreservationAcrossPathSequence( arr, rr, last(mover_states_mid), last(other_states_mid), last(mover_states), other_paths, last(mover_paths), mover_tid, other_tau, other_tid); mover_states' := mover_states_mid + [state_after_left_mover]; lemma_ExtendingStateSequenceWorks(arr.l, initial_state, last(mover_states_mid), all_but_last(mover_paths), mover_states_mid, mover_tid, last(mover_paths), state_after_left_mover); lemma_AllButLastPlusLastIsSeq(mover_paths); forall i | 0 <= i < |mover_states|-1 ensures OKAndPCTypesMatch(arr, mover_states[i], mover_states'[i], mover_tid) { assert mover_states'[i] == mover_states_mid[i]; assert 0 <= i < |all_but_last(mover_states)|; assert OKAndPCTypesMatch(arr, mover_states_mid[i], all_but_last(mover_states)[i], mover_tid); assert all_but_last(mover_states)[i] == mover_states[i]; } } lemma lemma_DemonstrateLeftMoverSelfCrashPreservationArmadaMultistep( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_other_multistep:LState, state_after_both_paths:LState, other_multistep:Armada_Multistep, mover_multistep:Armada_Multistep, mover_tid:Armada_ThreadHandle ) returns ( state_after_left_mover:LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires arr.inv(initial_state) requires AtomicNextMultistep(arr.l, initial_state, state_after_other_multistep, other_multistep.steps, other_multistep.tid, other_multistep.tau) requires AtomicNextMultistep(arr.l, state_after_other_multistep, state_after_both_paths, mover_multistep.steps, mover_multistep.tid, mover_multistep.tau) requires mover_multistep.tid == mover_tid requires !mover_multistep.tau requires !rr.crashed(initial_state) requires !rr.crashed(state_after_other_multistep) requires rr.crashed(state_after_both_paths) requires IsPhase2(arr, state_after_other_multistep, mover_tid) requires other_multistep.tau || other_multistep.tid != mover_tid ensures AtomicNextMultistep(arr.l, initial_state, state_after_left_mover, mover_multistep.steps, mover_multistep.tid, mover_multistep.tau) ensures rr.crashed(state_after_left_mover) ensures RefinementPair(state_after_both_paths, state_after_left_mover) in rr.relation { var mover_states := AtomicGetStateSequence(arr.l, state_after_other_multistep, mover_multistep.steps, mover_multistep.tid); lemma_IfMultistepStartsInPhase2ThenEachPathDoes(arr, state_after_other_multistep, state_after_both_paths, mover_multistep, mover_states); var mover_states'; state_after_left_mover, mover_states' := lemma_DemonstrateLeftMoverSelfCrashPreservationPathSequence( arr, rr, initial_state, state_after_other_multistep, state_after_both_paths, other_multistep.steps, mover_multistep.steps, mover_states, mover_tid, other_multistep.tau, other_multistep.tid); } lemma lemma_DemonstrateLeftMoverSelfCrashPreservation( arr:AtomicReductionRequest, rr:RefinementViaReductionRequest, Armada_Multistep, Armada_Multistep>, initial_state:LState, state_after_other_multistep:LState, state_after_both_paths:LState, other_multistep:Armada_Multistep, left_mover:Armada_Multistep, mover_tid:Armada_ThreadHandle ) returns ( state_after_left_mover:LState ) requires ValidAtomicReductionRequest(arr) requires rr == GetRefinementViaReductionRequest(arr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_other_multistep, other_multistep) in rr.lspec.next requires ActionTuple(state_after_other_multistep, state_after_both_paths, left_mover) in rr.lspec.next requires !rr.crashed(initial_state) requires !rr.crashed(state_after_other_multistep) requires rr.crashed(state_after_both_paths) requires IsPhase2(arr, state_after_other_multistep, mover_tid) requires rr.idmap(left_mover) == Some(mover_tid) requires rr.idmap(other_multistep) != Some(mover_tid) ensures ActionTuple(initial_state, state_after_left_mover, left_mover) in rr.lspec.next ensures rr.crashed(state_after_left_mover) ensures RefinementPair(state_after_both_paths, state_after_left_mover) in rr.relation { lemma_StateAmongAnnotatedReachablesSatisfiesInv(arr, initial_state); state_after_left_mover := lemma_DemonstrateLeftMoverSelfCrashPreservationArmadaMultistep( arr, rr, initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover, mover_tid); } lemma lemma_LeftMoverSelfCrashPreservation( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures RefinementViaReductionSpecModule.LeftMoverSelfCrashPreservation(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); forall initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover {:trigger RefinementViaReductionSpecModule.LeftMoverSelfCrashPreservationConditions(rr, initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover)} | RefinementViaReductionSpecModule.LeftMoverSelfCrashPreservationConditions(rr, initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover) && initial_state in AnnotatedReachables(rr.lspec) && ActionTuple(initial_state, state_after_other_multistep, other_multistep) in rr.lspec.next && ActionTuple(state_after_other_multistep, state_after_both_paths, left_mover) in rr.lspec.next && !rr.crashed(initial_state) && !rr.crashed(state_after_other_multistep) && rr.crashed(state_after_both_paths) && rr.phase2(state_after_other_multistep, rr.idmap(left_mover)) && rr.idmap(left_mover) != rr.idmap(other_multistep) ensures exists left_mover', state_after_left_mover' :: && rr.idmap(left_mover') == rr.idmap(left_mover) && ActionTuple(initial_state, state_after_left_mover', left_mover') in rr.lspec.next && rr.crashed(state_after_left_mover') && RefinementPair(state_after_both_paths, state_after_left_mover') in rr.relation { var state_after_left_mover := lemma_DemonstrateLeftMoverSelfCrashPreservation(arr, rr, initial_state, state_after_other_multistep, state_after_both_paths, other_multistep, left_mover, rr.idmap(left_mover).v); } } ////////////////////////////////////// // REDUCTION REQUEST VALID ////////////////////////////////////// lemma lemma_IfAtomicReductionRequestValidThenRefinementViaReductionRequestValid ( arr:AtomicReductionRequest ) requires ValidAtomicReductionRequest(arr) ensures ValidRefinementViaReductionRequest(GetRefinementViaReductionRequest(arr)) { var rr := GetRefinementViaReductionRequest(arr); assert RefinementRelationReflexive(rr.relation); assert RefinementRelationTransitive(rr.relation); assert rr.hspec.init == rr.lspec.init; assert forall s, actor :: s in rr.lspec.init ==> !rr.phase1(s, actor) && !rr.phase2(s, actor); assert forall s, actor :: rr.phase1(s, actor) ==> !rr.phase2(s, actor); lemma_IfAtomicReductionRequestValidThenCrashingCantGoDirectlyFromPhase2ToPhase1(arr); lemma_IfAtomicReductionRequestValidThenCrashingPhaseUnaffectedByOtherActors(arr); lemma_PostCrashStepsStutter(arr); lemma_RightMoversPreserveStateRefinement(arr); lemma_LeftMoversPreserveStateRefinement(arr); lemma_RightMoversCommute(arr); lemma_LeftMoversCommute(arr); lemma_RightMoverCrashPreservation(arr); lemma_LeftMoverSelfCrashPreservation(arr); lemma_ActionSequencesLiftable(arr); lemma_LeftMoversAlwaysEnabled(arr); lemma_LeftMoversEnabledBeforeCrash(arr); } ////////////////////////////// // UTILITY FUNCTIONS ////////////////////////////// function LMultistepToHMultistep( arr:AtomicReductionRequest, lmultipath:Armada_Multistep ) : Armada_Multistep { Armada_Multistep(MapSeqToSeq(lmultipath.steps, arr.lpath_to_hpath), lmultipath.tid, lmultipath.tau) } ////////////////////////////// // UTILITY LEMMAS ////////////////////////////// lemma lemma_ArmadaGetStateSequenceLastElement( arr:AtomicReductionRequest, s:LState, s':LState, paths:seq, states:seq, tid:Armada_ThreadHandle ) requires AtomicNextMultiplePaths(arr.l, s, s', paths, tid) requires states == AtomicGetStateSequence(arr.l, s, paths, tid) ensures last(states) == s' { if |paths| > 0 { var s_mid := arr.l.path_next(s, paths[0], tid); lemma_ArmadaGetStateSequenceLastElement(arr, s_mid, s', paths[1..], states[1..], tid); } } lemma lemma_AtomicValidPathSequenceImpliesOfAnyType( asf: AtomicSpecFunctions, s: State, paths: seq, tid: Armada_ThreadHandle ) requires AtomicValidPathSequence(asf, s, paths, tid) ensures AtomicValidPathSequenceOfAnyType(asf, s, paths, tid) { if |paths| > 0 { var s_next := asf.path_next(s, paths[0], tid); lemma_AtomicValidPathSequenceImpliesOfAnyType(asf, s_next, paths[1..], tid); } } lemma lemma_AtomicValidPathSequenceImpliesRR( asf: AtomicSpecFunctions, s: State, paths: seq, tid: Armada_ThreadHandle, pos: int ) requires AtomicValidPathSequence(asf, s, paths, tid) requires 0 <= pos < |paths| ensures asf.path_type(paths[pos]).AtomicPathType_RR? { if pos > 0 { var s_next := asf.path_next(s, paths[0], tid); lemma_AtomicValidPathSequenceImpliesRR(asf, s_next, paths[1..], tid, pos-1); } } ///////////////////////////////////////////// // SHIM BETWEEN ARMADA AND COHEN-LAMPORT ///////////////////////////////////////////// function ConvertAtomicTraceEntryToMultistep( asf:AtomicSpecFunctions, entry:AtomicTraceEntry ) : Armada_Multistep { match entry case AtomicTraceEntry_Stutter => Armada_Multistep([], 0, false) case AtomicTraceEntry_Tau(tid, path) => Armada_Multistep([path], tid, true) case AtomicTraceEntry_Normal(tid, path) => Armada_Multistep([path], tid, false) case AtomicTraceEntry_Recurrent(tid, yr, rrs, rx) => Armada_Multistep([yr] + rrs + [rx], tid, false) } function ConvertMultistepToAtomicTraceEntry( arr:AtomicReductionRequest, multistep:Armada_Multistep ) : AtomicTraceEntry { if |multistep.steps| == 0 then AtomicTraceEntry_Stutter() else var hpath0 := arr.lpath_to_hpath(multistep.steps[0]); if multistep.tau then AtomicTraceEntry_Tau(multistep.tid, hpath0) else if |multistep.steps| == 1 then AtomicTraceEntry_Normal(multistep.tid, hpath0) else AtomicTraceEntry_Recurrent( multistep.tid, hpath0, MapSeqToSeq(multistep.steps[1..|multistep.steps|-1], arr.lpath_to_hpath), arr.lpath_to_hpath(last(multistep.steps)) ) } lemma lemma_ConvertAtomicTraceEntryToMultistepMaintainsNext( asf:AtomicSpecFunctions, s:State, s':State, lentry:AtomicTraceEntry ) requires AtomicPathRequiresOK(asf) requires AtomicPathTypeAlwaysMatchesPCTypes(asf) requires AtomicNext(asf, s, s', lentry) ensures var hmultistep := ConvertAtomicTraceEntryToMultistep(asf, lentry); AtomicNextMultistep(asf, s, s', hmultistep.steps, hmultistep.tid, hmultistep.tau) { var hmultistep := ConvertAtomicTraceEntryToMultistep(asf, lentry); if !lentry.AtomicTraceEntry_Recurrent? { // Everything but the recurrent case is trivial return; } var tid, yr, rrs, rx := lentry.recurrent_tid, lentry.yr, lentry.rrs, lentry.rx; var s1 := asf.path_next(s, yr, tid); var s2 := AtomicGetStateAfterPaths(asf, s1, rrs, tid); lemma_AtomicValidPathSequenceImpliesOfAnyType(asf, s1, rrs, tid); var paths := [yr] + rrs + [rx]; var states_mid := AtomicGetStateSequence(asf, s1, rrs, tid); var states := [s] + states_mid + [s']; forall i | 0 <= i < |paths| ensures asf.path_valid(states[i], paths[i], tid) ensures states[i+1] == asf.path_next(states[i], paths[i], tid) ensures !asf.path_type(paths[i]).AtomicPathType_Tau? ensures i > 0 ==> var ty := asf.path_type(paths[i]); ty.AtomicPathType_RY? || ty.AtomicPathType_RS? || ty.AtomicPathType_RR? { if i == 0 { assert states[i] == s; assert states[i+1] == s1; assert paths[i] == yr; } else if i < |paths|-1 { lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(asf, s1, s2, rrs, tid, states_mid, i-1); assert asf.path_valid(states_mid[i-1], rrs[i-1], tid); assert states_mid[i-1+1] == asf.path_next(states_mid[i-1], rrs[i-1], tid); assert states[i] == states_mid[i-1]; assert states[i+1] == states_mid[i-1+1]; lemma_AtomicValidPathSequenceImpliesRR(asf, s1, rrs, tid, i-1); } else { lemma_AtomicNextLastElement(asf, s1, s2, rrs, tid, states_mid); assert states[i] == s2; assert states[i+1] == s'; assert paths[i] == rx; } } forall i | 0 < i < |paths| ensures !AtomicThreadYielding(asf, states[i], tid) { assert asf.path_valid(states[i], paths[i], tid); assert states[i+1] == asf.path_next(states[i], paths[i], tid); var ty := asf.path_type(paths[i]); assert ty.AtomicPathType_RY? || ty.AtomicPathType_RS? || ty.AtomicPathType_RR?; assert !AtomicThreadYielding(asf, states[i], tid); } var pos := 0; while pos < |paths| invariant 0 <= pos <= |paths| invariant AtomicNextMultiplePaths(asf, s, states[pos], paths[..pos], tid) invariant states[..pos+1] == AtomicGetStateSequence(asf, s, paths[..pos], tid) { lemma_ExtendingStateSequenceWorks(asf, s, states[pos], paths[..pos], states[..pos+1], tid, paths[pos], states[pos+1]); assert paths[..pos+1] == paths[..pos] + [paths[pos]]; assert states[..pos+1+1] == states[..pos+1] + [states[pos+1]]; pos := pos + 1; } assert s' == states[pos]; assert paths[..pos] == paths; assert AtomicNextMultiplePaths(asf, s, s', paths, tid); } lemma lemma_IfBehaviorSatisfiesGenericSpecThenItSatisfiesRefinementViaReductionLSpec( arr:AtomicReductionRequest, lb:AnnotatedBehavior> ) returns ( hb:AnnotatedBehavior> ) requires ValidAtomicReductionRequest(arr) requires AnnotatedBehaviorSatisfiesSpec(lb, AtomicAnnotatedSpec(arr.l)) ensures AnnotatedBehaviorSatisfiesSpec(hb, GetRefinementViaReductionLSpec(arr)) ensures hb.states == lb.states { var htrace := MapSeqToSeq(lb.trace, entry => ConvertAtomicTraceEntryToMultistep(arr.l, entry)); hb := AnnotatedBehavior(lb.states, htrace); var lspec := AtomicAnnotatedSpec(arr.l); var hspec := GetRefinementViaReductionLSpec(arr); forall i {:trigger ActionTuple(hb.states[i], hb.states[i+1], hb.trace[i]) in hspec.next} | 0 <= i < |hb.trace| ensures ActionTuple(hb.states[i], hb.states[i+1], hb.trace[i]) in hspec.next { assert ActionTuple(lb.states[i], lb.states[i+1], lb.trace[i]) in AtomicAnnotatedSpec(arr.l).next; lemma_ConvertAtomicTraceEntryToMultistepMaintainsNext(arr.l, lb.states[i], lb.states[i+1], lb.trace[i]); } } lemma lemma_LHMaintainsNextRecurrent( arr:AtomicReductionRequest, ls:LState, ls':LState, lmultistep:Armada_Multistep, hs:HState, hs':HState, hentry:AtomicTraceEntry ) requires ValidAtomicReductionRequest(arr) requires arr.inv(ls) requires GenericNextReduced(arr, ls, ls', lmultistep.steps, lmultistep.tid, lmultistep.tau) requires !lmultistep.tau requires |lmultistep.steps| > 1 requires hs == arr.lstate_to_hstate(ls) requires hs' == arr.lstate_to_hstate(ls') requires hentry == ConvertMultistepToAtomicTraceEntry(arr, lmultistep) requires hentry.AtomicTraceEntry_Recurrent? ensures AtomicNext(arr.h, hs, hs', hentry) { var lpaths := lmultistep.steps; var tid, lyr, lrrs := lmultistep.tid, lpaths[0], lpaths[1..|lpaths|-1]; var hyr, hrrs, hrx := hentry.yr, hentry.rrs, hentry.rx; var lstates := AtomicGetStateSequence(arr.l, ls, lpaths, tid); lemma_AtomicNextLastElement(arr.l, ls, ls', lpaths, tid, lstates); var hstates := MapSeqToSeq(lstates, arr.lstate_to_hstate); lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, ls, ls', lpaths, tid, lstates, 0); assert arr.h.path_valid(hs, hyr, tid); assert hstates[1] == arr.h.path_next(hs, hyr, tid); assert !IsNonyieldingOrInPhase(arr, ls, tid); assert AtomicThreadYielding(arr.h, hs, tid); assert IsNonyieldingOrInPhase(arr, lstates[1], tid); assert !AtomicThreadYielding(arr.h, hstates[1], tid); var pos := 0; while pos < |hrrs| invariant 0 <= pos <= |hrrs| invariant forall p :: p in hrrs[..pos] ==> arr.h.path_type(p).AtomicPathType_RR? invariant AtomicValidPathSequence(arr.h, hstates[1], hrrs[..pos], tid) invariant hstates[pos+1] == AtomicGetStateAfterPaths(arr.h, hstates[1], hrrs[..pos], tid) invariant arr.inv(lstates[pos+1]) { lemma_AtomicValidPathSequenceImpliesOfAnyType(arr.h, hstates[1], hrrs[..pos], tid); lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, ls, ls', lpaths, tid, lstates, pos+1); assert hrrs[pos] == arr.lpath_to_hpath(lpaths[pos+1]); assert hstates[pos+1] == arr.lstate_to_hstate(lstates[pos+1]); assert arr.h.path_valid(hstates[pos+1], hrrs[pos], tid); assert hstates[pos+1+1] == arr.h.path_next(hstates[pos+1], hrrs[pos], tid); lemma_ExtendAtomicGetStateAfterPaths(arr.h, hstates[1], hstates[pos+1], hrrs[..pos], tid, hrrs[pos]); assert hrrs[..pos+1] == hrrs[..pos] + [hrrs[pos]]; lemma_ExtendAtomicValidPathSequence(arr.h, hstates[1], hstates[pos+1], hrrs[..pos], tid, hrrs[pos]); assert IsNonyieldingOrInPhase(arr, lstates[pos+1], tid); assert !AtomicThreadYielding(arr.h, hstates[pos+1], tid); assert IsNonyieldingOrInPhase(arr, lstates[pos+1+1], tid); assert !AtomicThreadYielding(arr.h, hstates[pos+1+1], tid); lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, ls, ls', lpaths, tid, lstates, pos+1+1); assert arr.h.path_type(hrrs[pos]).AtomicPathType_RR?; assert forall p :: p in hrrs[..pos+1] ==> p in hrrs[..pos] || p == hrrs[pos]; pos := pos + 1; } assert hrrs[..pos] == hrrs; lemma_AtomicValidPathSequenceOfAnyTypeImpliesValidPath(arr.l, ls, ls', lpaths, tid, lstates, pos+1); assert AtomicValidRecursiveStep(arr.h, hs, tid, hyr, hrrs, hrx); } lemma lemma_LHMaintainsNext( arr:AtomicReductionRequest, ls:LState, ls':LState, lmultistep:Armada_Multistep, hs:HState, hs':HState, hentry:AtomicTraceEntry ) requires ValidAtomicReductionRequest(arr) requires arr.inv(ls) requires ActionTuple(ls, ls', lmultistep) in GetRefinementViaReductionHSpec(arr).next; requires hs == arr.lstate_to_hstate(ls) requires hs' == arr.lstate_to_hstate(ls') requires hentry == ConvertMultistepToAtomicTraceEntry(arr, lmultistep) ensures ActionTuple(hs, hs', hentry) in AtomicAnnotatedSpec(arr.h).next { assert GenericNextReduced(arr, ls, ls', lmultistep.steps, lmultistep.tid, lmultistep.tau); if |lmultistep.steps| == 0 { return; } var lstates := AtomicGetStateSequence(arr.l, ls, lmultistep.steps, lmultistep.tid); lemma_AtomicNextLastElement(arr.l, ls, ls', lmultistep.steps, lmultistep.tid, lstates); if lmultistep.tau { return; } if |lmultistep.steps| == 1 { return; } lemma_LHMaintainsNextRecurrent(arr, ls, ls', lmultistep, hs, hs', hentry); } lemma lemma_AtomicInvariantHoldsAtIntermediateStateAtomicNextMultiplePaths( arr:AtomicReductionRequest, s:LState, s':LState, paths:seq, tid:Armada_ThreadHandle, states:seq, pos:int ) requires ValidAtomicReductionRequest(arr) requires AtomicNextMultiplePaths(arr.l, s, s', paths, tid) requires states == AtomicGetStateSequence(arr.l, s, paths, tid) requires 0 <= pos < |states| requires arr.inv(states[0]) ensures arr.inv(states[pos]) decreases pos { if pos > 0 { var s_mid := arr.l.path_next(s, paths[0], tid); lemma_AtomicInvariantHoldsAtIntermediateStateAtomicNextMultiplePaths(arr, s_mid, s', paths[1..], tid, states[1..], pos-1); } } lemma lemma_GenericNextReducedBehaviorSatisfiesInv( arr:AtomicReductionRequest, b:AnnotatedBehavior>, i:int ) requires ValidAtomicReductionRequest(arr) requires AnnotatedBehaviorSatisfiesSpec(b, GetRefinementViaReductionHSpec(arr)) requires 0 <= i < |b.states| ensures arr.inv(b.states[i]) { if i == 0 { return; } lemma_GenericNextReducedBehaviorSatisfiesInv(arr, b, i-1); var spec := GetRefinementViaReductionHSpec(arr); var prev := i-1; var s := b.states[prev]; var s' := b.states[prev+1]; var multistep := b.trace[prev]; var paths := multistep.steps; var tid := multistep.tid; var states := AtomicGetStateSequence(arr.l, s, paths, tid); assert s' == b.states[i]; assert 0 <= prev < |b.trace|; assert ActionTuple(b.states[prev], b.states[prev+1], b.trace[prev]) in spec.next; assert GenericNextReduced(arr, s, s', paths, multistep.tid, multistep.tau); assert AtomicNextMultiplePaths(arr.l, s, s', paths, tid); lemma_AtomicInvariantHoldsAtIntermediateStateAtomicNextMultiplePaths(arr, s, s', paths, tid, states, |states|-1); assert arr.inv(states[|states|-1]); assert arr.inv(last(states)); lemma_ArmadaGetStateSequenceLastElement(arr, s, s', paths, states, tid); assert last(states) == s'; assert arr.inv(s'); } } ================================================ FILE: Armada/strategies/reduction/AtomicReductionSpec.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file is the specification for a request to perform refinement via a Cohen-Lamport reduction // on an Armada atomic behavior. Such a reduction takes a behavior satisfying a low-level spec, which // allows threads to sometimes be in phase 1 or 2 between trace entries, and returns a behavior // satisfying a higher-level specification that only allows those phases in the middle of // trace entries. It does so by lifting sequences of trace entries in the lower-level specification to // single trace entries in the higher-level specification. // // To use this specification, first create a request r of type AtomicReductionRequest. Then, prove // that it's a valid request, i.e., that ValidAtomicReductionRequest(r). Finally, call // lemma_PerformArmadaReduction (in AtomicReduction.i.dfy). // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "../../util/option.s.dfy" include "../../util/collections/seqs.s.dfy" include "../refinement/GeneralRefinementLemmas.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../generic/GenericArmadaSpec.i.dfy" include "../generic/GenericArmadaAtomic.i.dfy" module AtomicReductionSpecModule { import opened util_option_s import opened util_collections_seqs_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GenericArmadaSpecModule import opened GenericArmadaAtomicModule import opened ArmadaCommonDefinitions datatype AtomicReductionRequest = AtomicReductionRequest( l:AtomicSpecFunctions, h:AtomicSpecFunctions, relation:RefinementRelation, inv:LState->bool, self_relation:RefinementRelation, lstate_to_hstate:LState->HState, lpath_to_hpath:LPath->HPath, lpc_to_hpc:LPC->HPC, is_phase1:LPC->bool, is_phase2:LPC->bool, generate_left_mover:(LState, Armada_ThreadHandle)->LPath, left_mover_generation_progress:(LState,Armada_ThreadHandle)->int ) predicate LInitImpliesHInit( arr:AtomicReductionRequest ) { forall ls :: arr.l.init(ls) ==> arr.h.init(arr.lstate_to_hstate(ls)) } predicate LStateToHStateMapsPCsCorrectly( arr:AtomicReductionRequest ) { forall ls, tid :: var hs := arr.lstate_to_hstate(ls); var lpc := arr.l.get_thread_pc(ls, tid); var hpc := arr.h.get_thread_pc(hs, tid); hpc == if lpc.Some? then Some(arr.lpc_to_hpc(lpc.v)) else None() } predicate LHYieldingCorrespondence( arr:AtomicReductionRequest ) { forall lpc :: var hpc := arr.lpc_to_hpc(lpc); arr.h.is_pc_nonyielding(hpc) <==> (arr.l.is_pc_nonyielding(lpc) || arr.is_phase1(lpc) || arr.is_phase2(lpc)) } predicate LHPathTypesMatchYR(lty:AtomicPathType, hty:AtomicPathType) { match lty case AtomicPathType_Tau => hty.AtomicPathType_Tau? case AtomicPathType_YY => hty.AtomicPathType_YY? || hty.AtomicPathType_YR? || hty.AtomicPathType_RY? || hty.AtomicPathType_RR? case AtomicPathType_YS => hty.AtomicPathType_YS? || hty.AtomicPathType_RS? case AtomicPathType_YR => hty.AtomicPathType_YR? || hty.AtomicPathType_RR? case AtomicPathType_RY => hty.AtomicPathType_RY? || hty.AtomicPathType_RR? case AtomicPathType_RS => hty.AtomicPathType_RS? case AtomicPathType_RR => hty.AtomicPathType_RR? } predicate LHPathPropertiesMatch( arr:AtomicReductionRequest ) { forall lpath :: var hpath := arr.lpath_to_hpath(lpath); LHPathTypesMatchYR(arr.l.path_type(lpath), arr.h.path_type(hpath)) } predicate LPathImpliesHPath( arr:AtomicReductionRequest ) { forall ls, lpath, tid :: arr.inv(ls) && arr.l.path_valid(ls, lpath, tid) ==> var ls' := arr.l.path_next(ls, lpath, tid); var hs := arr.lstate_to_hstate(ls); var hpath := arr.lpath_to_hpath(lpath); var hs' := arr.lstate_to_hstate(ls'); && arr.h.path_valid(hs, hpath, tid) && hs' == arr.h.path_next(hs, hpath, tid) } predicate StateConversionPreservesOK( arr:AtomicReductionRequest ) { forall ls :: arr.h.state_ok(arr.lstate_to_hstate(ls)) == arr.l.state_ok(ls) } predicate StateConversionSatisfiesRelation( arr:AtomicReductionRequest ) { forall ls :: RefinementPair(ls, arr.lstate_to_hstate(ls)) in arr.relation } predicate ThreadsDontStartInAnyPhase( arr:AtomicReductionRequest ) { forall s, tid :: arr.l.init(s) && arr.l.get_thread_pc(s, tid).Some? ==> var pc := arr.l.get_thread_pc(s, tid).v; !arr.is_phase1(pc) && !arr.is_phase2(pc) } predicate PhasesDontOverlap( arr:AtomicReductionRequest ) { forall pc :: arr.is_phase1(pc) ==> !arr.is_phase2(pc) } predicate ThreadCantAffectOtherThreadPhaseExceptViaFork ( arr:AtomicReductionRequest ) { forall s, path, mover_tid, other_tid :: arr.l.path_valid(s, path, mover_tid) && mover_tid != other_tid ==> var s' := arr.l.path_next(s, path, mover_tid); var pc := arr.l.get_thread_pc(s, other_tid); var pc' := arr.l.get_thread_pc(s', other_tid); (pc' != pc ==> pc.None? && !arr.is_phase1(pc'.v) && !arr.is_phase2(pc'.v) && !arr.l.is_pc_nonyielding(pc'.v)) } predicate PhasesPrecededByYielding( arr:AtomicReductionRequest ) { forall s, path, tid :: var s' := arr.l.path_next(s, path, tid); var pc := arr.l.get_thread_pc(s, tid); var pc' := arr.l.get_thread_pc(s', tid); && arr.l.path_valid(s, path, tid) && pc'.Some? && (arr.is_phase1(pc'.v) || arr.is_phase2(pc'.v)) && pc.Some? && (!arr.is_phase1(pc.v) && !arr.is_phase2(pc.v)) ==> !arr.l.is_pc_nonyielding(pc.v) } predicate PhasesSucceededByYielding( arr:AtomicReductionRequest ) { forall s, path, tid :: var s' := arr.l.path_next(s, path, tid); var pc := arr.l.get_thread_pc(s, tid); var pc' := arr.l.get_thread_pc(s', tid); && arr.l.path_valid(s, path, tid) && pc.Some? && (arr.is_phase1(pc.v) || arr.is_phase2(pc.v)) && pc'.Some? && (!arr.is_phase1(pc'.v) && !arr.is_phase2(pc'.v)) ==> !arr.l.is_pc_nonyielding(pc'.v) } predicate Phase2NotFollowedByPhase1( arr:AtomicReductionRequest ) { forall s, path, tid :: var s' := arr.l.path_next(s, path, tid); var pc := arr.l.get_thread_pc(s, tid); var pc' := arr.l.get_thread_pc(s', tid); && arr.l.path_valid(s, path, tid) && pc.Some? && arr.is_phase2(pc.v) && pc'.Some? && !arr.is_phase2(pc'.v) ==> !arr.is_phase1(pc'.v) } predicate RightMoversPreserveStateRefinement ( arr:AtomicReductionRequest ) { forall s, path, tid :: arr.l.path_valid(s, path, tid) && !arr.l.path_type(path).AtomicPathType_Tau? ==> var s' := arr.l.path_next(s, path, tid); var pc' := arr.l.get_thread_pc(s', tid); (pc'.Some? && arr.is_phase1(pc'.v) && arr.l.state_ok(s') ==> RefinementPair(s', s) in arr.self_relation) } predicate LeftMoversPreserveStateRefinement ( arr:AtomicReductionRequest ) { forall s, path, tid :: arr.l.path_valid(s, path, tid) && !arr.l.path_type(path).AtomicPathType_Tau? ==> var s' := arr.l.path_next(s, path, tid); var pc := arr.l.get_thread_pc(s, tid); (pc.Some? && arr.is_phase2(pc.v) ==> RefinementPair(s, s') in arr.self_relation) } predicate RightMoversCommuteConditions( arr:AtomicReductionRequest, initial_state:LState, right_mover:LPath, other_path:LPath, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) { var other_tau := arr.l.path_type(other_path).AtomicPathType_Tau?; var state_after_right_mover := arr.l.path_next(initial_state, right_mover, mover_tid); var state_after_both_paths := arr.l.path_next(state_after_right_mover, other_path, other_tid); var pc := arr.l.get_thread_pc(state_after_right_mover, mover_tid); && arr.inv(initial_state) && !arr.l.path_type(right_mover).AtomicPathType_Tau? && arr.l.path_valid(initial_state, right_mover, mover_tid) && pc.Some? && arr.is_phase1(pc.v) && arr.l.path_valid(state_after_right_mover, other_path, other_tid) && arr.l.state_ok(state_after_both_paths) && (other_tau || other_tid != mover_tid) } predicate RightMoversCommute( arr:AtomicReductionRequest ) { forall initial_state, right_mover, other_path, mover_tid, other_tid {:trigger RightMoversCommuteConditions(arr, initial_state, right_mover, other_path, mover_tid, other_tid)} :: RightMoversCommuteConditions(arr, initial_state, right_mover, other_path, mover_tid, other_tid) ==> var state_after_right_mover := arr.l.path_next(initial_state, right_mover, mover_tid); var state_after_both_paths := arr.l.path_next(state_after_right_mover, other_path, other_tid); var new_middle_state := arr.l.path_next(initial_state, other_path, other_tid); && arr.l.path_valid(initial_state, other_path, other_tid) && arr.l.path_valid(new_middle_state, right_mover, mover_tid) && state_after_both_paths == arr.l.path_next(new_middle_state, right_mover, mover_tid) } predicate LeftMoversCommuteConditions( arr:AtomicReductionRequest, initial_state:LState, other_path:LPath, left_mover:LPath, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) { var state_after_other_path := arr.l.path_next(initial_state, other_path, other_tid); var state_after_both_paths := arr.l.path_next(state_after_other_path, left_mover, mover_tid); var pc := arr.l.get_thread_pc(state_after_other_path, mover_tid); && arr.inv(initial_state) && arr.l.path_valid(initial_state, other_path, other_tid) && pc.Some? && arr.is_phase2(pc.v) && !arr.l.path_type(left_mover).AtomicPathType_Tau? && arr.l.path_valid(state_after_other_path, left_mover, mover_tid) && (arr.l.path_type(other_path).AtomicPathType_Tau? || other_tid != mover_tid) && arr.l.state_ok(state_after_both_paths) } predicate LeftMoversCommute( arr:AtomicReductionRequest ) { forall initial_state, other_path, left_mover, mover_tid, other_tid {:trigger LeftMoversCommuteConditions(arr, initial_state, other_path, left_mover, mover_tid, other_tid)} :: LeftMoversCommuteConditions(arr, initial_state, other_path, left_mover, mover_tid, other_tid) ==> var state_after_other_path := arr.l.path_next(initial_state, other_path, other_tid); var state_after_both_paths := arr.l.path_next(state_after_other_path, left_mover, mover_tid); var new_middle_state := arr.l.path_next(initial_state, left_mover, mover_tid); && arr.l.path_valid(initial_state, left_mover, mover_tid) && arr.l.path_valid(new_middle_state, other_path, other_tid) && state_after_both_paths == arr.l.path_next(new_middle_state, other_path, other_tid) } predicate LeftMoversAlwaysEnabledConditions ( arr:AtomicReductionRequest, s:LState, tid:Armada_ThreadHandle ) { && arr.inv(s) && arr.l.state_ok(s) && arr.l.get_thread_pc(s, tid).Some? && arr.is_phase2(arr.l.get_thread_pc(s, tid).v) } predicate LeftMoversAlwaysEnabled( arr:AtomicReductionRequest ) { forall s, tid {:trigger LeftMoversAlwaysEnabledConditions(arr, s, tid)} :: LeftMoversAlwaysEnabledConditions(arr, s, tid) ==> var path := arr.generate_left_mover(s, tid); && arr.l.path_valid(s, path, tid) && !arr.l.path_type(path).AtomicPathType_Tau? && var s' := arr.l.path_next(s, path, tid); 0 <= arr.left_mover_generation_progress(s', tid) < arr.left_mover_generation_progress(s, tid) } predicate RightMoverCrashPreservationConditions ( arr:AtomicReductionRequest, initial_state:LState, right_mover:LPath, other_path:LPath, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) { var state_after_right_mover := arr.l.path_next(initial_state, right_mover, mover_tid); var state_after_both_paths := arr.l.path_next(state_after_right_mover, other_path, other_tid); var pc := arr.l.get_thread_pc(state_after_right_mover, mover_tid); var other_tau := arr.l.path_type(other_path).AtomicPathType_Tau?; && arr.inv(initial_state) && arr.l.path_valid(initial_state, right_mover, mover_tid) && arr.l.path_valid(state_after_right_mover, other_path, other_tid) && !arr.l.state_ok(state_after_both_paths) && !arr.l.path_type(right_mover).AtomicPathType_Tau? && pc.Some? && arr.is_phase1(pc.v) && (other_tau || other_tid != mover_tid) } predicate RightMoverCrashPreservation( arr:AtomicReductionRequest ) { forall initial_state, right_mover, other_path, mover_tid, other_tid {:trigger RightMoverCrashPreservationConditions(arr, initial_state, right_mover, other_path, mover_tid, other_tid)} :: RightMoverCrashPreservationConditions(arr, initial_state, right_mover, other_path, mover_tid, other_tid) ==> var state_after_right_mover := arr.l.path_next(initial_state, right_mover, mover_tid); var state_after_both_paths := arr.l.path_next(state_after_right_mover, other_path, other_tid); var state_after_other_path := arr.l.path_next(initial_state, other_path, other_tid); && arr.l.path_valid(initial_state, other_path, other_tid) && !arr.l.state_ok(state_after_other_path) && RefinementPair(state_after_both_paths, state_after_other_path) in arr.self_relation } predicate LeftMoverCrashPreservationConditions ( arr:AtomicReductionRequest, initial_state:LState, left_mover:LPath, other_path:LPath, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) { var state_after_other_path := arr.l.path_next(initial_state, other_path, other_tid); var pc := arr.l.get_thread_pc(initial_state, mover_tid); var other_tau := arr.l.path_type(other_path).AtomicPathType_Tau?; && arr.inv(initial_state) && !arr.l.path_type(left_mover).AtomicPathType_Tau? && arr.l.path_valid(initial_state, left_mover, mover_tid) && arr.l.path_valid(initial_state, other_path, other_tid) && arr.l.state_ok(initial_state) && !arr.l.state_ok(state_after_other_path) && pc.Some? && arr.is_phase2(pc.v) && (other_tau || other_tid != mover_tid) } predicate LeftMoverCrashPreservation( arr:AtomicReductionRequest ) { forall initial_state, left_mover, other_path, mover_tid, other_tid {:trigger LeftMoverCrashPreservationConditions(arr, initial_state, left_mover, other_path, mover_tid, other_tid)} :: LeftMoverCrashPreservationConditions(arr, initial_state, left_mover, other_path, mover_tid, other_tid) ==> var state_after_other_path := arr.l.path_next(initial_state, other_path, other_tid); var state_after_left_mover := arr.l.path_next(initial_state, left_mover, mover_tid); var state_after_both_paths := arr.l.path_next(state_after_left_mover, other_path, other_tid); && arr.l.path_valid(state_after_left_mover, other_path, other_tid) && !arr.l.state_ok(state_after_both_paths) && RefinementPair(state_after_other_path, state_after_both_paths) in arr.self_relation } predicate LeftMoverSelfCrashPreservationConditions ( arr:AtomicReductionRequest, initial_state:LState, left_mover:LPath, other_path:LPath, mover_tid:Armada_ThreadHandle, other_tid:Armada_ThreadHandle ) { var state_after_other_path := arr.l.path_next(initial_state, other_path, other_tid); var state_after_both_paths := arr.l.path_next(state_after_other_path, left_mover, mover_tid); var pc := arr.l.get_thread_pc(state_after_other_path, mover_tid); var other_tau := arr.l.path_type(other_path).AtomicPathType_Tau?; && arr.inv(initial_state) && !arr.l.path_type(left_mover).AtomicPathType_Tau? && arr.l.path_valid(initial_state, other_path, other_tid) && arr.l.path_valid(state_after_other_path, left_mover, mover_tid) && arr.l.state_ok(initial_state) && !arr.l.state_ok(state_after_both_paths) && pc.Some? && arr.is_phase2(pc.v) && (other_tau || other_tid != mover_tid) } predicate LeftMoverSelfCrashPreservation( arr:AtomicReductionRequest ) { forall initial_state, left_mover, other_path, mover_tid, other_tid {:trigger LeftMoverSelfCrashPreservationConditions(arr, initial_state, left_mover, other_path, mover_tid, other_tid)} :: LeftMoverSelfCrashPreservationConditions(arr, initial_state, left_mover, other_path, mover_tid, other_tid) ==> var state_after_other_path := arr.l.path_next(initial_state, other_path, other_tid); var state_after_both_paths := arr.l.path_next(state_after_other_path, left_mover, mover_tid); var state_after_left_mover := arr.l.path_next(initial_state, left_mover, mover_tid); && arr.l.path_valid(initial_state, left_mover, mover_tid) && !arr.l.state_ok(state_after_left_mover) && RefinementPair(state_after_both_paths, state_after_left_mover) in arr.self_relation } predicate ValidAtomicReductionRequest( arr:AtomicReductionRequest ) { && RefinementRelationReflexive(arr.self_relation) && RefinementRelationTransitive(arr.self_relation) && RefinementRelationsConvolve(arr.self_relation, arr.relation, arr.relation) && LInitImpliesHInit(arr) && AtomicInitImpliesInv(arr.l, arr.inv) && AtomicPathPreservesInv(arr.l, arr.inv) && AtomicPathRequiresOK(arr.l) && AtomicInitImpliesYielding(arr.l) && AtomicInitImpliesOK(arr.l) && AtomicPathTypeAlwaysMatchesPCTypes(arr.l) && AtomicPathTypeAlwaysMatchesPCTypes(arr.h) && LStateToHStateMapsPCsCorrectly(arr) && LHYieldingCorrespondence(arr) && LHPathPropertiesMatch(arr) && LPathImpliesHPath(arr) && StateConversionPreservesOK(arr) && StateConversionSatisfiesRelation(arr) && ThreadsDontStartInAnyPhase(arr) && PhasesDontOverlap(arr) && PhasesPrecededByYielding(arr) && PhasesSucceededByYielding(arr) && Phase2NotFollowedByPhase1(arr) && AtomicSteppingThreadHasPC(arr.l) && AtomicTauLeavesPCUnchanged(arr.l) && ThreadCantAffectOtherThreadPhaseExceptViaFork(arr) && RightMoversPreserveStateRefinement(arr) && LeftMoversPreserveStateRefinement(arr) && RightMoversCommute(arr) && LeftMoversCommute(arr) && RightMoverCrashPreservation(arr) && LeftMoverCrashPreservation(arr) && LeftMoverSelfCrashPreservation(arr) && LeftMoversAlwaysEnabled(arr) } } ================================================ FILE: Armada/strategies/reduction/CohenLamportReduction.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file contains the lemma for performing a Cohen-Lamport reduction on a behavior. Such a // reduction takes a behavior where some states are in phase 1 or 2, and returns a behavior in which // no state (except possibly the last, crashing state) is in phase 1 or 2. That resulting behavior // satisfies the same specification, but is a refinement of the original behavior. // // To use this lemma, lemma_PerformCohenLamportReduction, you need to first create a request r of // type CohenLamportReductionRequest (defined in CohenLamportReductionSpec.i.dfy), then prove that // it's a valid request, i.e., that ValidCohenLamportReductionRequest(r). // // The lemma allows behaviors that crash as their final step. But, it demans that action sequences // be reducible if they crash in the middle, i.e., even if the actor performing those action // sequences executes a step that crashes while in phase 1 or 2. // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "CohenLamportReductionSpec.i.dfy" include "CohenLamportReductionLemmas.i.dfy" module CohenLamportReductionModule { import opened AnnotatedBehaviorModule import opened GeneralRefinementModule import opened RefinementConvolutionModule import opened CohenLamportReductionSpecModule import opened CohenLamportReductionLemmasModule lemma lemma_PerformCohenLamportReduction( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures forall i, actor :: 0 <= i < |hb.states| ==> var s := hb.states[i]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) { var mb := lemma_RemoveAllNonmovers(clrr, lb); hb := lemma_RemoveAllRightMovers(clrr, mb); lemma_RefinementConvolutionTransitive(lb.states, mb.states, hb.states, clrr.relation); } } ================================================ FILE: Armada/strategies/reduction/CohenLamportReductionLemmas.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file contains lemmas useful in effecting a Cohen-Lamport reduction on a behavior. They // support lemma_PerformCohenLamportReduction (in CohenLamportReduction.i.dfy). // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "CohenLamportReductionSpec.i.dfy" include "../../util/collections/seqs.i.dfy" module CohenLamportReductionLemmasModule { import opened util_collections_seqs_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened CohenLamportReductionSpecModule import opened util_collections_seqs_i lemma lemma_InstantiateLeftMoversBeforeCrash( clrr:CohenLamportReductionRequest, initial_state:State, post_crash_state:State, crash_step:Step, actor:Actor ) returns ( states:seq, trace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires LeftMoversEnabledBeforeCrashConditions(clrr, initial_state, post_crash_state, crash_step, actor) ensures StateNextSeq(states, trace, clrr.spec.next) ensures |states| > 2 ensures states[0] == initial_state ensures !clrr.crashed(states[|states|-2]) ensures !clrr.phase2(states[|states|-2], actor) ensures forall i :: 0 <= i < |states|-2 ==> clrr.phase2(states[i], actor) ensures forall i :: 0 <= i < |trace|-1 ==> clrr.idmap(trace[i]) == actor ensures clrr.idmap(last(trace)) != actor ensures clrr.crashed(last(states)) ensures BehaviorRefinesBehavior([initial_state, post_crash_state], states, clrr.relation) { var mover_states, mover_trace, crash_step', post_crash_state' :| && StateNextSeq(mover_states, mover_trace, clrr.spec.next) && mover_states[0] == initial_state && !clrr.crashed(last(mover_states)) && !clrr.phase2(last(mover_states), actor) && (forall i :: 0 <= i < |mover_states|-1 ==> clrr.phase2(mover_states[i], actor)) && (forall step :: step in mover_trace ==> clrr.idmap(step) == actor) && ActionTuple(last(mover_states), post_crash_state', crash_step') in clrr.spec.next && clrr.idmap(crash_step') == clrr.idmap(crash_step) && clrr.crashed(post_crash_state') && RefinementPair(post_crash_state, post_crash_state') in clrr.relation; var pos := 0; while pos < |mover_trace| invariant 0 <= pos <= |mover_trace| invariant forall i :: 0 <= i <= pos ==> RefinementPair(initial_state, mover_states[i]) in clrr.relation { assert RefinementPair(initial_state, mover_states[pos]) in clrr.relation; assert ActionTuple(mover_states[pos], mover_states[pos+1], mover_trace[pos]) in clrr.spec.next; assert RefinementPair(mover_states[pos], mover_states[pos+1]) in clrr.relation; assert RefinementPair(initial_state, mover_states[pos+1]) in clrr.relation; pos := pos + 1; } states := mover_states + [post_crash_state']; trace := mover_trace + [crash_step']; var lm_map := [RefinementRange(0, |mover_states|-1), RefinementRange(|mover_states|, |mover_states|)]; assert BehaviorRefinesBehaviorUsingRefinementMap([initial_state, post_crash_state], states, clrr.relation, lm_map); } lemma lemma_CreateLeftMoversBeforeCrash( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires !clrr.phase1(lstates[0], step_actor) requires !clrr.crashed(lstates[0]) requires forall i :: 0 <= i < |ltrace| ==> clrr.idmap(ltrace[i]) != step_actor requires clrr.phase2(last(lstates), step_actor) requires clrr.crashed(last(lstates)) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures |hstates| > |lstates| ensures forall i :: 0 <= i < |ltrace| ==> hstates[i] == lstates[i] ensures forall i :: 0 <= i < |ltrace|-1 ==> htrace[i] == ltrace[i] ensures forall i :: |ltrace|-1 <= i < |htrace|-1 ==> clrr.idmap(htrace[i]) == step_actor ensures forall i :: |ltrace|-1 <= i < |htrace|-1 ==> clrr.phase2(hstates[i], step_actor) ensures clrr.idmap(last(htrace)) != step_actor ensures !clrr.phase2(hstates[|htrace|-1], step_actor) ensures !clrr.crashed(hstates[|hstates|-2]) ensures clrr.crashed(last(hstates)) { var penult := |ltrace|-1; assert ActionTuple(lstates[penult], lstates[penult+1], ltrace[penult]) in clrr.spec.next; lemma_StateNextSeqMaintainsAnnotatedReachables(lstates, ltrace, clrr.spec); assert lstates[penult] in lstates; assert LeftMoversEnabledBeforeCrashConditions(clrr, lstates[penult], lstates[penult+1], ltrace[penult], step_actor); var end_states', end_trace' := lemma_InstantiateLeftMoversBeforeCrash(clrr, lstates[penult], lstates[penult+1], ltrace[penult], step_actor); assert clrr.idmap(last(end_trace')) != step_actor; assert lstates[penult..] == [lstates[penult], lstates[penult+1]]; lemma_ExtendBehaviorRefinesBehaviorLeft(lstates[penult..], end_states', clrr.relation, lstates[..penult]); hstates := lstates[..penult] + end_states'; htrace := ltrace[..penult] + end_trace'; lemma_LastOfConcatenationIsLastOfLatter(ltrace[..penult], end_trace'); assert last(htrace) == last(end_trace'); lemma_TakePlusDropIsSeq(lstates, penult); assert BehaviorRefinesBehavior(lstates, hstates, clrr.relation); assert |lstates[..penult]| == penult; assert |ltrace[..penult]| == penult; forall i {:trigger ActionTuple(hstates[i], hstates[i+1], htrace[i]) in clrr.spec.next} | 0 <= i < |htrace| ensures ActionTuple(hstates[i], hstates[i+1], htrace[i]) in clrr.spec.next { lemma_IndexIntoConcatenation(lstates[..penult], end_states', i); lemma_IndexIntoConcatenation(lstates[..penult], end_states', i+1); lemma_IndexIntoConcatenation(ltrace[..penult], end_trace', i); if i < penult-1 { assert hstates[i] == lstates[i]; assert hstates[i+1] == lstates[i+1]; assert htrace[i] == ltrace[i]; assert ActionTuple(lstates[i], lstates[i+1], ltrace[i]) in clrr.spec.next; } else if i == penult - 1 { assert hstates[i] == lstates[i]; assert hstates[i+1] == end_states'[0] == lstates[penult] == lstates[i+1]; assert htrace[i] == ltrace[i]; assert ActionTuple(lstates[i], lstates[i+1], ltrace[i]) in clrr.spec.next; } else { var iadj := i - penult; assert hstates[i] == end_states'[iadj]; calc { hstates[i+1]; end_states'[i+1-penult]; { assert i+1-penult == iadj+1; } end_states'[iadj+1]; } assert htrace[i] == end_trace'[iadj]; assert ActionTuple(end_states'[iadj], end_states'[iadj+1], end_trace'[iadj]) in clrr.spec.next; } } assert StateNextSeq(hstates, htrace, clrr.spec.next); forall i | 0 <= i < |ltrace| ensures hstates[i] == lstates[i] { lemma_IndexIntoConcatenation(lstates[..penult], end_states', i); if i < penult { assert hstates[i] == lstates[..penult][i] == lstates[i]; } else { assert i == penult; assert hstates[i] == end_states'[0] == lstates[penult] == lstates[i]; } } forall i | 0 <= i < |ltrace|-1 ensures htrace[i] == ltrace[i] { lemma_IndexIntoConcatenation(lstates[..penult], end_states', i); assert htrace[i] == ltrace[..penult][i] == ltrace[i]; } } lemma lemma_BringLeftMoversToStartByCreatingLeftMoversBeforeCrash( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor ) returns ( hstates:seq, htrace:seq, end_pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires !clrr.phase1(lstates[0], step_actor) requires !clrr.crashed(lstates[0]) requires forall i :: 0 <= i < |ltrace| ==> clrr.idmap(ltrace[i]) != step_actor requires clrr.phase2(last(lstates), step_actor) requires clrr.crashed(last(lstates)) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| <= |lstates| + end_pos ensures 0 <= end_pos < |hstates| ensures var s := hstates[end_pos]; !clrr.crashed(s) ==> !clrr.phase1(s, step_actor) && !clrr.phase2(s, step_actor) ensures forall i :: 0 <= i < end_pos ==> clrr.idmap(htrace[i]) == step_actor ensures forall i :: 0 <= i < end_pos ==> clrr.phase2(hstates[i], step_actor) { var mstates, mtrace := lemma_CreateLeftMoversBeforeCrash(clrr, lstates, ltrace, step_actor); var hstates_trunc, htrace_trunc; hstates_trunc, htrace_trunc, end_pos := lemma_BringLeftMoversToStartAfterCreatingLeftMovers(clrr, all_but_last(mstates), all_but_last(mtrace), step_actor, |ltrace|-1); assert !clrr.crashed(last(all_but_last(mstates))); assert last(hstates_trunc) == last(all_but_last(mstates)); lemma_ExtendBehaviorRefinesBehaviorRightOne(all_but_last(mstates), hstates_trunc, clrr.relation, last(mstates)); assert BehaviorRefinesBehavior(all_but_last(mstates) + [last(mstates)], hstates_trunc + [last(mstates)], clrr.relation); hstates := hstates_trunc + [last(mstates)]; htrace := htrace_trunc + [last(mtrace)]; lemma_AllButLastPlusLastIsSeq(mstates); assert BehaviorRefinesBehavior(mstates, hstates, clrr.relation); forall i {:trigger ActionTuple(hstates[i], hstates[i+1], htrace[i]) in clrr.spec.next} | 0 <= i < |htrace| ensures ActionTuple(hstates[i], hstates[i+1], htrace[i]) in clrr.spec.next { if i < |htrace|-1 { assert hstates[i] == hstates_trunc[i]; assert hstates[i+1] == hstates_trunc[i+1]; assert htrace[i] == htrace_trunc[i]; assert ActionTuple(hstates_trunc[i], hstates_trunc[i+1], htrace_trunc[i]) in clrr.spec.next; } else { var j := |mstates|-2; assert hstates[i] == last(hstates_trunc) == last(all_but_last(mstates)) == mstates[j]; assert hstates[i+1] == last(mstates) == mstates[j+1]; assert htrace[i] == last(mtrace) == mtrace[j]; assert ActionTuple(mstates[j], mstates[j+1], mtrace[j]) in clrr.spec.next; } } assert StateNextSeq(hstates, htrace, clrr.spec.next); lemma_RefinementConvolutionTransitive(lstates, mstates, hstates, clrr.relation); } lemma lemma_BringLeftMoversToStartAfterCreatingLeftMovers( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, left_mover_pos:int ) returns ( hstates:seq, htrace:seq, end_pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires 0 <= left_mover_pos < |ltrace| requires forall i :: 0 <= i < left_mover_pos ==> clrr.idmap(ltrace[i]) != step_actor requires forall i :: left_mover_pos <= i < |ltrace| ==> clrr.idmap(ltrace[i]) == step_actor requires forall i :: left_mover_pos <= i < |ltrace| ==> clrr.phase2(lstates[i], step_actor) requires clrr.crashed(last(lstates)) || !clrr.phase2(last(lstates), step_actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| <= |lstates| ensures end_pos == |ltrace| - left_mover_pos ensures 0 <= end_pos < |hstates| ensures var s := hstates[end_pos]; !clrr.crashed(s) ==> !clrr.phase1(s, step_actor) && !clrr.phase2(s, step_actor) ensures forall i :: 0 <= i < end_pos ==> clrr.idmap(htrace[i]) == step_actor ensures forall i :: 0 <= i < end_pos ==> clrr.phase2(hstates[i], step_actor) ensures !clrr.crashed(last(lstates)) ==> last(hstates) == last(lstates) decreases |lstates| { end_pos := |ltrace| - left_mover_pos; var mstates, mtrace := lemma_BringLeftMoverToStart(clrr, lstates, ltrace, step_actor, left_mover_pos); if left_mover_pos == |ltrace|-1 { hstates := mstates; htrace := mtrace; return; } var pos_plus := left_mover_pos+1; assert ActionTuple(lstates[pos_plus], lstates[pos_plus+1], ltrace[pos_plus]) in clrr.spec.next; lemma_ReduceStateNextSeqLeft(mstates, mtrace, clrr.spec.next); lemma_StateNextSeqMaintainsAnnotatedReachables(mstates, mtrace, clrr.spec); assert mstates[1..][0] in mstates; var hstates_trunc, htrace_trunc, end_pos_trunc := lemma_BringLeftMoversToStartAfterCreatingLeftMovers(clrr, mstates[1..], mtrace[1..], step_actor, left_mover_pos); hstates := [mstates[0]] + hstates_trunc; htrace := [mtrace[0]] + htrace_trunc; lemma_ExtendStateNextSeqLeft(hstates_trunc, htrace_trunc, clrr.spec.next, mstates[0], mtrace[0]); lemma_ExtendBehaviorRefinesBehaviorLeftOne(mstates[1..], hstates_trunc, clrr.relation, mstates[0]); lemma_SequenceIsCarPlusCdr(mstates); lemma_RefinementConvolutionTransitive(lstates, mstates, hstates, clrr.relation); } lemma lemma_BringLeftMoversToStartByCreatingLeftMoversAtEnd( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor ) returns ( hstates:seq, htrace:seq, end_pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires forall i :: 0 <= i < |ltrace| ==> clrr.idmap(ltrace[i]) != step_actor requires clrr.phase2(last(lstates), step_actor) requires !clrr.crashed(last(lstates)) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| <= |lstates| + end_pos ensures 0 <= end_pos < |hstates| ensures var s := hstates[end_pos]; !clrr.crashed(s) ==> !clrr.phase1(s, step_actor) && !clrr.phase2(s, step_actor) ensures forall i :: 0 <= i < end_pos ==> clrr.idmap(htrace[i]) == step_actor ensures forall i :: 0 <= i < end_pos ==> clrr.phase2(hstates[i], step_actor) { lemma_StateNextSeqMaintainsAnnotatedReachables(lstates, ltrace, clrr.spec); assert last(lstates) in lstates; assert LeftMoversAlwaysEnabledConditions(clrr, last(lstates), step_actor); var end_states', end_trace' :| && StateNextSeq(end_states', end_trace', clrr.spec.next) && end_states'[0] == last(lstates) && (clrr.crashed(last(end_states')) || !clrr.phase2(last(end_states'), step_actor)) && (forall i :: 0 <= i < |end_states'|-1 ==> clrr.phase2(end_states'[i], step_actor)) && (forall step :: step in end_trace' ==> clrr.idmap(step) == step_actor); var pos := 0; while pos < |end_trace'| invariant 0 <= pos <= |end_trace'| invariant forall i :: 0 <= i <= pos ==> RefinementPair(last(lstates), end_states'[i]) in clrr.relation { assert RefinementPair(last(lstates), end_states'[pos]) in clrr.relation; assert ActionTuple(end_states'[pos], end_states'[pos+1], end_trace'[pos]) in clrr.spec.next; assert RefinementPair(end_states'[pos], end_states'[pos+1]) in clrr.relation; assert RefinementPair(last(lstates), end_states'[pos+1]) in clrr.relation; pos := pos + 1; } var end_states := [last(lstates)]; var lm_map := [RefinementRange(0, |end_trace'|)]; assert BehaviorRefinesBehaviorUsingRefinementMap(end_states, end_states', clrr.relation, lm_map); assert BehaviorRefinesBehavior(end_states, end_states', clrr.relation); lemma_ExtendBehaviorRefinesBehaviorLeft(end_states, end_states', clrr.relation, all_but_last(lstates)); var mstates := all_but_last(lstates) + end_states'; var mtrace := ltrace + end_trace'; lemma_AllButLastPlusLastIsSeq(lstates); assert BehaviorRefinesBehavior(lstates, mstates, clrr.relation); forall i {:trigger ActionTuple(mstates[i], mstates[i+1], mtrace[i]) in clrr.spec.next} | 0 <= i < |mtrace| ensures ActionTuple(mstates[i], mstates[i+1], mtrace[i]) in clrr.spec.next { if i < |ltrace| { assert mstates[i] == lstates[i]; assert mstates[i+1] == lstates[i+1]; assert mtrace[i] == ltrace[i]; assert ActionTuple(lstates[i], lstates[i+1], ltrace[i]) in clrr.spec.next; } else { var iminus := i - |ltrace|; assert mstates[i] == end_states'[iminus]; assert mstates[i+1] == end_states'[iminus+1]; assert mtrace[i] == end_trace'[iminus]; assert ActionTuple(end_states'[iminus], end_states'[iminus+1], end_trace'[iminus]) in clrr.spec.next; } } assert StateNextSeq(mstates, mtrace, clrr.spec.next); hstates, htrace, end_pos := lemma_BringLeftMoversToStartAfterCreatingLeftMovers(clrr, mstates, mtrace, step_actor, |ltrace|); lemma_RefinementConvolutionTransitive(lstates, mstates, hstates, clrr.relation); } lemma lemma_BringLeftMoversToStartByCreatingLeftMovers( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor ) returns ( hstates:seq, htrace:seq, end_pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires !clrr.phase1(lstates[0], step_actor) requires !clrr.crashed(lstates[0]) requires clrr.phase2(lstates[0], step_actor) requires forall i :: 0 <= i < |ltrace| ==> clrr.idmap(ltrace[i]) != step_actor requires clrr.phase2(last(lstates), step_actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| <= |lstates| + end_pos ensures 0 <= end_pos < |hstates| ensures var s := hstates[end_pos]; !clrr.crashed(s) ==> !clrr.phase1(s, step_actor) && !clrr.phase2(s, step_actor) ensures forall i :: 0 <= i < end_pos ==> clrr.idmap(htrace[i]) == step_actor ensures forall i :: 0 <= i < end_pos ==> clrr.phase2(hstates[i], step_actor) { if clrr.crashed(last(lstates)) { hstates, htrace, end_pos := lemma_BringLeftMoversToStartByCreatingLeftMoversBeforeCrash(clrr, lstates, ltrace, step_actor); } else { hstates, htrace, end_pos := lemma_BringLeftMoversToStartByCreatingLeftMoversAtEnd(clrr, lstates, ltrace, step_actor); } } lemma lemma_BringLeftMoverLeftOneCaseCrashing( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires |ltrace| == 2 requires clrr.idmap(ltrace[0]) != step_actor requires clrr.idmap(ltrace[1]) == step_actor requires clrr.phase2(lstates[1], step_actor) requires clrr.crashed(lstates[2]) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| <= |lstates| ensures |htrace| == 1 ensures clrr.crashed(hstates[1]) ensures clrr.idmap(htrace[0]) == step_actor ensures clrr.phase2(hstates[0], step_actor) { var zero := 0; assert ActionTuple(lstates[zero], lstates[zero+1], ltrace[zero]) in clrr.spec.next; var one := 1; assert ActionTuple(lstates[one], lstates[one+1], ltrace[one]) in clrr.spec.next; assert LeftMoverSelfCrashPreservationConditions(clrr, lstates[0], lstates[1], lstates[2], ltrace[0], ltrace[1]); var left_mover', state_after_left_mover' :| && clrr.idmap(left_mover') == clrr.idmap(ltrace[1]) && ActionTuple(lstates[0], state_after_left_mover', left_mover') in clrr.spec.next && clrr.crashed(state_after_left_mover') && RefinementPair(lstates[2], state_after_left_mover') in clrr.relation; hstates := [lstates[0], state_after_left_mover']; htrace := [left_mover']; var lh_map := [RefinementRange(0, 0), RefinementRange(1, 1), RefinementRange(1, 1)]; forall i, j {:trigger RefinementPair(lstates[i], hstates[j]) in clrr.relation} | 0 <= i < |lstates| && lh_map[i].first <= j <= lh_map[i].last ensures RefinementPair(lstates[i], hstates[j]) in clrr.relation { if i == 0 && j == 0 { assert lstates[i] == lstates[0]; assert hstates[j] == lstates[0]; assert RefinementPair(lstates[0], lstates[0]) in clrr.relation; } else if i == 1 && j == 1 { assert lstates[i] == lstates[1]; assert hstates[j] == state_after_left_mover'; assert ActionTuple(lstates[1], lstates[2], ltrace[1]) in clrr.spec.next; assert RefinementPair(lstates[1], lstates[2]) in clrr.relation; assert RefinementPair(lstates[2], state_after_left_mover') in clrr.relation; assert RefinementPair(lstates[1], state_after_left_mover') in clrr.relation; } else { assert i == 2 && j == 1; assert lstates[i] == lstates[2]; assert hstates[j] == state_after_left_mover'; assert RefinementPair(lstates[2], state_after_left_mover') in clrr.relation; } } assert BehaviorRefinesBehaviorUsingRefinementMap(lstates, hstates, clrr.relation, lh_map); assert BehaviorRefinesBehavior(lstates, hstates, clrr.relation); } lemma lemma_BringLeftMoverLeftOneCaseNotCrashing( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires |ltrace| >= 2 requires clrr.idmap(ltrace[0]) != step_actor requires clrr.idmap(ltrace[1]) == step_actor requires clrr.phase2(lstates[1], step_actor) requires !clrr.crashed(lstates[2]) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| == |lstates| ensures !clrr.crashed(hstates[1]) ensures !clrr.crashed(hstates[2]) ensures hstates[2..] == lstates[2..] ensures htrace[2..] == ltrace[2..] ensures clrr.idmap(htrace[0]) == step_actor ensures clrr.idmap(htrace[1]) != step_actor ensures clrr.phase2(hstates[0], step_actor) ensures clrr.phase2(hstates[1], step_actor) <==> clrr.phase2(lstates[2], step_actor) { var zero := 0; assert ActionTuple(lstates[zero], lstates[zero+1], ltrace[zero]) in clrr.spec.next; var one := 1; assert ActionTuple(lstates[one], lstates[one+1], ltrace[one]) in clrr.spec.next; assert LeftMoversCommuteConditions(clrr, lstates[0], lstates[1], lstates[2], ltrace[0], ltrace[1]); var new_middle_state, left_mover', other_step' :| && ActionTuple(lstates[0], new_middle_state, left_mover') in clrr.spec.next && ActionTuple(new_middle_state, lstates[2], other_step') in clrr.spec.next && clrr.idmap(left_mover') == clrr.idmap(ltrace[1]) && clrr.idmap(other_step') == clrr.idmap(ltrace[0]); var hstates_init := [lstates[0], new_middle_state, lstates[2]]; var htrace_init := [left_mover', other_step']; assert StateNextSeq(hstates_init, htrace_init, clrr.spec.next); hstates := hstates_init + lstates[3..]; htrace := htrace_init + ltrace[2..]; assert StateNextSeq(hstates, htrace, clrr.spec.next); var lh_map := [RefinementRange(0, 1), RefinementRange(2, 2), RefinementRange(2, 2)]; var lstates_init := lstates[..3]; forall i, j {:trigger RefinementPair(lstates_init[i], hstates_init[j]) in clrr.relation} | 0 <= i < |lstates_init| && lh_map[i].first <= j <= lh_map[i].last ensures RefinementPair(lstates_init[i], hstates_init[j]) in clrr.relation { if i == 0 && j == 0 { assert lstates_init[i] == lstates[0]; assert hstates_init[j] == lstates[0]; assert RefinementPair(lstates[0], lstates[0]) in clrr.relation; } else if i == 0 && j == 1 { assert lstates_init[i] == lstates[0]; assert hstates_init[j] == new_middle_state; assert ActionTuple(lstates[0], new_middle_state, left_mover') in clrr.spec.next; assert RefinementPair(lstates[0], new_middle_state) in clrr.relation; } else if i == 1 && j == 2 { assert lstates_init[i] == lstates[1]; assert hstates_init[j] == lstates[2]; assert ActionTuple(lstates[1], lstates[2], ltrace[1]) in clrr.spec.next; assert RefinementPair(lstates[1], lstates[2]) in clrr.relation; } else { assert i == 2 && j == 2; assert lstates_init[i] == lstates[2]; assert hstates_init[j] == lstates[2]; assert RefinementPair(lstates[2], lstates[2]) in clrr.relation; } } assert BehaviorRefinesBehaviorUsingRefinementMap(lstates_init, hstates_init, clrr.relation, lh_map); assert BehaviorRefinesBehavior(lstates_init, hstates_init, clrr.relation); lemma_ExtendBehaviorRefinesBehaviorRight(lstates_init, hstates_init, clrr.relation, lstates[3..]); lemma_TakePlusDropIsSeq(lstates, 3); } lemma lemma_BringLeftMoverLeftOne( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires |ltrace| >= 2 requires clrr.idmap(ltrace[0]) != step_actor requires clrr.idmap(ltrace[1]) == step_actor requires clrr.phase2(lstates[1], step_actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| <= |lstates| ensures |htrace| > 0 ensures !clrr.phase1(hstates[1], step_actor) ensures if clrr.crashed(lstates[2]) then && |hstates| == 2 && clrr.crashed(hstates[1]) else && |hstates| == |lstates| && hstates[2..] == lstates[2..] && htrace[2..] == ltrace[2..] && !clrr.crashed(hstates[1]) && (clrr.phase2(hstates[1], step_actor) <==> clrr.phase2(lstates[2], step_actor)) && clrr.idmap(htrace[1]) != step_actor ensures clrr.idmap(htrace[0]) == step_actor ensures clrr.phase2(hstates[0], step_actor) { if clrr.crashed(lstates[2]) { if 2 < |ltrace| { var two := 2; assert ActionTuple(lstates[two], lstates[two+1], ltrace[two]) in clrr.spec.next; assert false; } hstates, htrace := lemma_BringLeftMoverLeftOneCaseCrashing(clrr, lstates, ltrace, step_actor); } else { hstates, htrace := lemma_BringLeftMoverLeftOneCaseNotCrashing(clrr, lstates, ltrace, step_actor); } } lemma lemma_BringLeftMoverToStart( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, left_mover_pos:int ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires 0 <= left_mover_pos < |ltrace| requires forall i :: 0 <= i < left_mover_pos ==> clrr.idmap(ltrace[i]) != step_actor requires clrr.idmap(ltrace[left_mover_pos]) == step_actor requires clrr.phase2(lstates[left_mover_pos], step_actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| <= |lstates| ensures |htrace| > 0 ensures clrr.idmap(htrace[0]) == step_actor ensures !clrr.phase1(hstates[1], step_actor) ensures clrr.phase2(hstates[0], step_actor) ensures if clrr.crashed(lstates[left_mover_pos+1]) then && |hstates| == 2 && clrr.crashed(hstates[1]) else && |hstates| == |lstates| && !clrr.crashed(hstates[1]) && hstates[left_mover_pos+1..] == lstates[left_mover_pos+1..] && htrace[left_mover_pos+1..] == ltrace[left_mover_pos+1..] && (clrr.phase2(hstates[1], step_actor) <==> clrr.phase2(lstates[left_mover_pos+1], step_actor)) && (forall i :: 1 <= i <= left_mover_pos ==> clrr.idmap(htrace[i]) != step_actor) decreases left_mover_pos { if clrr.crashed(lstates[left_mover_pos+1]) && |ltrace| > left_mover_pos+1 { var pos_plus := left_mover_pos + 1; assert ActionTuple(lstates[pos_plus], lstates[pos_plus+1], ltrace[pos_plus]) in clrr.spec.next; assert false; } if left_mover_pos == 0 { hstates := lstates; htrace := ltrace; lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lstates, clrr.relation); return; } var prev := left_mover_pos-1; lemma_DropStateNextSeq(lstates, ltrace, clrr.spec.next, prev); lemma_StateNextSeqMaintainsAnnotatedReachables(lstates, ltrace, clrr.spec); assert lstates[prev..][0] in lstates; var mstates_trunc, mtrace_trunc := lemma_BringLeftMoverLeftOne(clrr, lstates[prev..], ltrace[prev..], step_actor); if !clrr.crashed(lstates[left_mover_pos+1]) { assert !clrr.crashed(lstates[prev..][2]); assert |mstates_trunc| == |lstates[prev..]| == |lstates| - prev; assert mtrace_trunc[2..] == ltrace[prev..][2..] == ltrace[left_mover_pos+1..]; } var mstates := lstates[..prev] + mstates_trunc; var mtrace := ltrace[..prev] + mtrace_trunc; lemma_ExtendBehaviorRefinesBehaviorLeft(lstates[prev..], mstates_trunc, clrr.relation, lstates[..prev]); lemma_TakePlusDropIsSeq(lstates, prev); lemma_TakePlusDropIsSeq(ltrace, prev); assert BehaviorRefinesBehavior(lstates, mstates, clrr.relation); forall i {:trigger ActionTuple(mstates[i], mstates[i+1], mtrace[i]) in clrr.spec.next} | 0 <= i < |mtrace| ensures ActionTuple(mstates[i], mstates[i+1], mtrace[i]) in clrr.spec.next { if i < prev-1 { assert mstates[i] == lstates[i]; assert mstates[i+1] == lstates[i+1]; assert mtrace[i] == ltrace[i]; assert ActionTuple(lstates[i], lstates[i+1], ltrace[i]) in clrr.spec.next; } else if i == prev-1 { assert mstates[i] == lstates[i]; assert mstates[i+1] == mstates_trunc[0] == lstates[prev..][0] == lstates[prev] == lstates[i+1]; assert mtrace[i] == ltrace[i]; assert ActionTuple(lstates[i], lstates[i+1], ltrace[i]) in clrr.spec.next; } else { var iminus := i-prev; assert mstates[i] == mstates_trunc[iminus]; assert mstates[i+1] == mstates_trunc[iminus+1]; assert mtrace[i] == mtrace_trunc[iminus]; assert ActionTuple(mstates_trunc[iminus], mstates_trunc[iminus+1], mtrace_trunc[iminus]) in clrr.spec.next; } } assert StateNextSeq(mstates, mtrace, clrr.spec.next); hstates, htrace := lemma_BringLeftMoverToStart(clrr, mstates, mtrace, step_actor, left_mover_pos-1); calc { mstates[left_mover_pos-1+1]; (lstates[..prev] + mstates_trunc)[left_mover_pos]; mstates_trunc[left_mover_pos - prev]; } lemma_RefinementConvolutionTransitive(lstates, mstates, hstates, clrr.relation); } lemma lemma_FindFirstLeftMover( clrr:CohenLamportReductionRequest, states:seq, trace:seq, step_actor:Actor ) returns ( pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(states, trace, clrr.spec.next) requires states[0] in AnnotatedReachables(clrr.spec) requires clrr.phase2(states[0], step_actor) ensures 0 <= pos <= |trace| ensures forall i :: 0 <= i < pos ==> clrr.idmap(trace[i]) != step_actor ensures clrr.phase2(states[pos], step_actor) ensures pos < |trace| ==> clrr.idmap(trace[pos]) == step_actor { pos := 0; while pos < |trace| && clrr.idmap(trace[pos]) != step_actor invariant 0 <= pos <= |trace| invariant forall i :: 0 <= i < pos ==> clrr.idmap(trace[i]) != step_actor invariant clrr.phase2(states[pos], step_actor) { assert ActionTuple(states[pos], states[pos+1], trace[pos]) in clrr.spec.next; pos := pos + 1; } } lemma lemma_BringLeftMoversToStart( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor ) returns ( hstates:seq, htrace:seq, end_pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires clrr.crashed(lstates[0]) || !clrr.phase1(lstates[0], step_actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |hstates| <= |lstates| + end_pos ensures 0 <= end_pos < |hstates| ensures var s := hstates[end_pos]; !clrr.crashed(s) ==> !clrr.phase1(s, step_actor) && !clrr.phase2(s, step_actor) ensures forall i :: 0 <= i < end_pos ==> clrr.idmap(htrace[i]) == step_actor ensures forall i :: 0 <= i < end_pos ==> clrr.phase2(hstates[i], step_actor) decreases |lstates| { if clrr.crashed(lstates[0]) || !clrr.phase2(lstates[0], step_actor) { hstates := lstates; htrace := ltrace; end_pos := 0; lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lstates, clrr.relation); return; } var left_mover_pos := lemma_FindFirstLeftMover(clrr, lstates, ltrace, step_actor); if left_mover_pos == |ltrace| { hstates, htrace, end_pos := lemma_BringLeftMoversToStartByCreatingLeftMovers(clrr, lstates, ltrace, step_actor); return; } var mstates, mtrace := lemma_BringLeftMoverToStart(clrr, lstates, ltrace, step_actor, left_mover_pos); var zero := 0; assert ActionTuple(mstates[zero], mstates[zero+1], mtrace[zero]) in clrr.spec.next; lemma_NextMaintainsAnnotatedReachables(mstates[zero], mstates[zero+1], mtrace[zero], clrr.spec); lemma_ReduceStateNextSeqLeft(mstates, mtrace, clrr.spec.next); var m2states, m2trace, m2end_pos := lemma_BringLeftMoversToStart(clrr, mstates[1..], mtrace[1..], step_actor); hstates := [mstates[0]] + m2states; htrace := [mtrace[0]] + m2trace; lemma_ExtendStateNextSeqLeft(m2states, m2trace, clrr.spec.next, mstates[0], mtrace[0]); lemma_ExtendBehaviorRefinesBehaviorLeftOne(mstates[1..], m2states, clrr.relation, mstates[0]); lemma_SequenceIsCarPlusCdr(mstates); lemma_RefinementConvolutionTransitive(lstates, mstates, hstates, clrr.relation); end_pos := m2end_pos + 1; } lemma lemma_EarlierStateInBehaviorAlsoNotCrashed( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, pos2:int, pos1:int ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires 0 <= pos1 <= pos2 < |lb.states| requires !clrr.crashed(lb.states[pos2]) ensures !clrr.crashed(lb.states[pos1]) { if pos1 < pos2 { var pos := pos2-1; assert ActionTuple(lb.states[pos], lb.states[pos+1], lb.trace[pos]) in clrr.spec.next; lemma_EarlierStateInBehaviorAlsoNotCrashed(clrr, lb, pos, pos1); } } lemma lemma_EstablishBehaviorRefinesBehaviorAfterMovingRightMoverRight( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, step_actor:Actor, right_mover_pos:int, new_middle_state:State, other_step':Step, right_mover':Step, hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires forall i :: 0 <= i < |lb.trace| ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires !clrr.crashed(last(lb.states)) requires 0 <= right_mover_pos < |lb.trace|-1 requires clrr.idmap(lb.trace[right_mover_pos]) == step_actor requires forall i :: right_mover_pos < i < |lb.trace| ==> clrr.idmap(lb.trace[i]) != step_actor requires clrr.phase1(lb.states[right_mover_pos+1], step_actor) requires ActionTuple(lb.states[right_mover_pos], new_middle_state, other_step') in clrr.spec.next requires ActionTuple(new_middle_state, lb.states[right_mover_pos+2], right_mover') in clrr.spec.next requires clrr.idmap(other_step') == clrr.idmap(lb.trace[right_mover_pos+1]) requires clrr.idmap(right_mover') == clrr.idmap(lb.trace[right_mover_pos]) requires hb.states == lb.states[..right_mover_pos+1] + [new_middle_state] + lb.states[right_mover_pos+2..] requires hb.trace == lb.trace[..right_mover_pos] + [other_step', right_mover'] + lb.trace[right_mover_pos+2..] ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures last(hb.states) == last(lb.states) ensures |hb.states| == |lb.states| ensures |hb.trace| == |lb.trace| ensures clrr.idmap(hb.trace[right_mover_pos+1]) == step_actor { var lb2 := lb.states[right_mover_pos..right_mover_pos+3]; var hb2 := [lb.states[right_mover_pos], new_middle_state, lb.states[right_mover_pos+2]]; var right_mover_pos_plus := right_mover_pos+1; forall ensures RefinementPair(lb2[2], hb2[1]) in clrr.relation { assert ActionTuple(new_middle_state, lb.states[right_mover_pos+2], right_mover') in clrr.spec.next; assert clrr.idmap(right_mover') == step_actor; assert ActionTuple(lb.states[right_mover_pos_plus], lb.states[right_mover_pos_plus+1], lb.trace[right_mover_pos_plus]) in clrr.spec.next; assert clrr.idmap(lb.trace[right_mover_pos_plus]) != step_actor; assert clrr.phase1(lb.states[right_mover_pos_plus+1], step_actor); lemma_EarlierStateInBehaviorAlsoNotCrashed(clrr, lb, |lb.states|-1, right_mover_pos+2); assert !clrr.crashed(lb.states[right_mover_pos+2]); assert RefinementPair(lb.states[right_mover_pos+2], new_middle_state) in clrr.relation; } forall ensures RefinementPair(lb2[1], hb2[0]) in clrr.relation { assert ActionTuple(lb.states[right_mover_pos], lb.states[right_mover_pos+1], lb.trace[right_mover_pos]) in clrr.spec.next; assert clrr.idmap(lb.trace[right_mover_pos]) == step_actor; lemma_EarlierStateInBehaviorAlsoNotCrashed(clrr, lb, |lb.states|-1, right_mover_pos+1); assert !clrr.crashed(lb.states[right_mover_pos+1]); assert RefinementPair(lb.states[right_mover_pos+1], lb.states[right_mover_pos]) in clrr.relation; } forall ensures RefinementPair(lb2[0], hb2[0]) in clrr.relation { assert hb2[0] == lb2[0]; } forall ensures RefinementPair(lb2[2], hb2[2]) in clrr.relation { assert hb2[2] == lb2[2]; } var lh_map := [RefinementRange(0, 0), RefinementRange(0, 0), RefinementRange(1, 2)]; assert BehaviorRefinesBehaviorUsingRefinementMap(lb2, hb2, clrr.relation, lh_map); lemma_ExtendBehaviorRefinesBehaviorLeft(lb2, hb2, clrr.relation, lb.states[..right_mover_pos]); lemma_ExtendBehaviorRefinesBehaviorRight(lb.states[..right_mover_pos] + lb2, lb.states[..right_mover_pos] + hb2, clrr.relation, lb.states[right_mover_pos+3..]); assert BehaviorRefinesBehavior(lb.states[..right_mover_pos] + lb2 + lb.states[right_mover_pos+3..], lb.states[..right_mover_pos] + hb2 + lb.states[right_mover_pos+3..], clrr.relation); lemma_SeqEqualsThreeWayConcatentation(lb.states, right_mover_pos, right_mover_pos+3); assert hb.states == lb.states[..right_mover_pos] + hb2 + lb.states[right_mover_pos+3..]; } lemma lemma_EstablishBehaviorSatisfiesSpecAfterMovingRightMoverRight( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, step_actor:Actor, right_mover_pos:int, new_middle_state:State, other_step':Step, right_mover':Step, hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires forall i :: 0 <= i < |lb.trace| ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires !clrr.crashed(last(lb.states)) requires 0 <= right_mover_pos < |lb.trace|-1 requires clrr.idmap(lb.trace[right_mover_pos]) == step_actor requires forall i :: right_mover_pos < i < |lb.trace| ==> clrr.idmap(lb.trace[i]) != step_actor requires clrr.phase1(lb.states[right_mover_pos+1], step_actor) requires ActionTuple(lb.states[right_mover_pos], new_middle_state, other_step') in clrr.spec.next requires ActionTuple(new_middle_state, lb.states[right_mover_pos+2], right_mover') in clrr.spec.next requires clrr.idmap(other_step') == clrr.idmap(lb.trace[right_mover_pos+1]) requires clrr.idmap(right_mover') == clrr.idmap(lb.trace[right_mover_pos]) requires hb.states == lb.states[..right_mover_pos+1] + [new_middle_state] + lb.states[right_mover_pos+2..] requires hb.trace == lb.trace[..right_mover_pos] + [other_step', right_mover'] + lb.trace[right_mover_pos+2..] ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) { forall i {:trigger ActionTuple(hb.states[i], hb.states[i+1], hb.trace[i]) in clrr.spec.next} | 0 <= i < |hb.trace| ensures ActionTuple(hb.states[i], hb.states[i+1], hb.trace[i]) in clrr.spec.next { if i == right_mover_pos { assert hb.states[i] == lb.states[right_mover_pos]; assert hb.states[i+1] == new_middle_state; assert hb.trace[i] == other_step'; assert ActionTuple(lb.states[right_mover_pos], new_middle_state, other_step') in clrr.spec.next; } else if i == right_mover_pos + 1 { assert hb.states[i] == new_middle_state; assert hb.states[i+1] == lb.states[right_mover_pos+2]; assert hb.trace[i] == right_mover'; assert ActionTuple(new_middle_state, lb.states[right_mover_pos+2], right_mover') in clrr.spec.next; } else { assert hb.states[i] == lb.states[i]; assert hb.states[i+1] == lb.states[i+1]; assert hb.trace[i] == lb.trace[i]; assert ActionTuple(lb.states[i], lb.states[i+1], lb.trace[i]) in clrr.spec.next; } } } lemma lemma_EstablishStillNoNonmoversAfterMovingRightMoverRight( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, step_actor:Actor, right_mover_pos:int, new_middle_state:State, other_step':Step, right_mover':Step, hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires forall i :: 0 <= i < |lb.trace| ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires !clrr.crashed(last(lb.states)) requires 0 <= right_mover_pos < |lb.trace|-1 requires clrr.idmap(lb.trace[right_mover_pos]) == step_actor requires forall i :: right_mover_pos < i < |lb.trace| ==> clrr.idmap(lb.trace[i]) != step_actor requires clrr.phase1(lb.states[right_mover_pos+1], step_actor) requires ActionTuple(lb.states[right_mover_pos], new_middle_state, other_step') in clrr.spec.next requires ActionTuple(new_middle_state, lb.states[right_mover_pos+2], right_mover') in clrr.spec.next requires clrr.idmap(other_step') == clrr.idmap(lb.trace[right_mover_pos+1]) requires clrr.idmap(right_mover') == clrr.idmap(lb.trace[right_mover_pos]) requires hb.states == lb.states[..right_mover_pos+1] + [new_middle_state] + lb.states[right_mover_pos+2..] requires hb.trace == lb.trace[..right_mover_pos] + [other_step', right_mover'] + lb.trace[right_mover_pos+2..] ensures forall i :: 0 <= i < |hb.trace| ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) { var right_mover_pos_plus := right_mover_pos + 1; lemma_EarlierStateInBehaviorAlsoNotCrashed(clrr, lb, |lb.states|-1, right_mover_pos+2); forall i | 0 <= i < |hb.trace| ensures !IsNonmoverPos(clrr, hb.states, hb.trace, i) { if i == right_mover_pos { assert hb.states[i] == lb.states[right_mover_pos]; assert hb.states[i+1] == new_middle_state; assert !IsNonmoverPos(clrr, lb.states, lb.trace, right_mover_pos_plus); assert ActionTuple(lb.states[right_mover_pos], lb.states[right_mover_pos+1], lb.trace[right_mover_pos]) in clrr.spec.next; assert ActionTuple(new_middle_state, lb.states[right_mover_pos_plus+1], right_mover') in clrr.spec.next; assert !IsNonmoverStep(clrr, lb.states[right_mover_pos], new_middle_state, clrr.idmap(hb.trace[i])); } else if i == right_mover_pos + 1 { assert hb.states[i] == new_middle_state; assert hb.states[i+1] == lb.states[right_mover_pos+2]; } else { assert !IsNonmoverPos(clrr, lb.states, lb.trace, i); } } } lemma lemma_MoveRightMoverRight( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, step_actor:Actor, right_mover_pos:int ) returns ( hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires forall i :: 0 <= i < |lb.trace| ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires !clrr.crashed(last(lb.states)) requires 0 <= right_mover_pos < |lb.trace|-1 requires clrr.idmap(lb.trace[right_mover_pos]) == step_actor requires forall i :: right_mover_pos < i < |lb.trace| ==> clrr.idmap(lb.trace[i]) != step_actor requires clrr.phase1(lb.states[right_mover_pos+1], step_actor) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures last(hb.states) == last(lb.states) ensures |hb.states| == |lb.states| ensures forall i :: 0 <= i < |hb.trace| ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) ensures clrr.idmap(hb.trace[right_mover_pos+1]) == step_actor ensures clrr.phase1(hb.states[right_mover_pos+2], step_actor) ensures forall i :: right_mover_pos + 1 < i < |hb.trace| ==> clrr.idmap(hb.trace[i]) != step_actor { assert ActionTuple(lb.states[right_mover_pos], lb.states[right_mover_pos+1], lb.trace[right_mover_pos]) in clrr.spec.next; var right_mover_pos_plus := right_mover_pos + 1; assert ActionTuple(lb.states[right_mover_pos_plus], lb.states[right_mover_pos_plus+1], lb.trace[right_mover_pos_plus]) in clrr.spec.next; lemma_StateInAnnotatedBehaviorInAnnotatedReachables(clrr.spec, lb, right_mover_pos); lemma_EarlierStateInBehaviorAlsoNotCrashed(clrr, lb, |lb.states|-1, right_mover_pos_plus+1); assert RightMoversCommuteConditions(clrr, lb.states[right_mover_pos], lb.states[right_mover_pos+1], lb.states[right_mover_pos_plus+1], lb.trace[right_mover_pos], lb.trace[right_mover_pos+1]); var new_middle_state, other_step', right_mover' :| && ActionTuple(lb.states[right_mover_pos], new_middle_state, other_step') in clrr.spec.next && ActionTuple(new_middle_state, lb.states[right_mover_pos_plus+1], right_mover') in clrr.spec.next && clrr.idmap(other_step') == clrr.idmap(lb.trace[right_mover_pos+1]) && clrr.idmap(right_mover') == clrr.idmap(lb.trace[right_mover_pos]); var hstates := lb.states[..right_mover_pos+1] + [new_middle_state] + lb.states[right_mover_pos+2..]; var htrace := lb.trace[..right_mover_pos] + [other_step', right_mover'] + lb.trace[right_mover_pos+2..]; hb := AnnotatedBehavior(hstates, htrace); lemma_EstablishBehaviorRefinesBehaviorAfterMovingRightMoverRight( clrr, lb, step_actor, right_mover_pos, new_middle_state, other_step', right_mover', hb); lemma_EstablishBehaviorSatisfiesSpecAfterMovingRightMoverRight( clrr, lb, step_actor, right_mover_pos, new_middle_state, other_step', right_mover', hb); lemma_EstablishStillNoNonmoversAfterMovingRightMoverRight( clrr, lb, step_actor, right_mover_pos, new_middle_state, other_step', right_mover', hb); } lemma lemma_MoveRightMoverToEnd( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, step_actor:Actor, right_mover_pos:int ) returns ( hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires forall i :: 0 <= i < |lb.trace| ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires !clrr.crashed(last(lb.states)) requires 0 <= right_mover_pos < |lb.trace| requires clrr.idmap(lb.trace[right_mover_pos]) == step_actor requires forall i :: right_mover_pos < i < |lb.trace| ==> clrr.idmap(lb.trace[i]) != step_actor requires clrr.phase1(lb.states[right_mover_pos+1], step_actor) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures last(hb.states) == last(lb.states) ensures |hb.states| == |lb.states| ensures forall i :: 0 <= i < |hb.trace| ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) ensures clrr.idmap(last(hb.trace)) == step_actor decreases |lb.trace| - right_mover_pos { if right_mover_pos == |lb.trace|-1 { hb := lb; lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lb.states, clrr.relation); return; } var mb := lemma_MoveRightMoverRight(clrr, lb, step_actor, right_mover_pos); hb := lemma_MoveRightMoverToEnd(clrr, mb, step_actor, right_mover_pos+1); lemma_RefinementConvolutionTransitive(lb.states, mb.states, hb.states, clrr.relation); } lemma lemma_BringRightMoversToEnd( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, step_actor:Actor ) returns ( hb:AnnotatedBehavior, start_pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires forall i :: 0 <= i < |lb.trace| ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires !clrr.crashed(last(lb.states)) requires !clrr.phase2(last(lb.states), step_actor) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures last(hb.states) == last(lb.states) ensures |hb.states| == |lb.states| ensures forall i :: 0 <= i < |hb.trace| ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) ensures 0 <= start_pos < |hb.states| ensures !clrr.crashed(hb.states[start_pos]) ensures !clrr.phase1(hb.states[start_pos], step_actor) ensures !clrr.phase2(hb.states[start_pos], step_actor) ensures forall i :: start_pos <= i < |hb.trace| ==> clrr.idmap(hb.trace[i]) == step_actor ensures forall i :: start_pos < i < |hb.states| ==> clrr.phase1(hb.states[i], step_actor) decreases |lb.states| { if !clrr.phase1(last(lb.states), step_actor) { start_pos := |lb.states| - 1; hb := lb; lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lb.states, clrr.relation); return; } var right_mover_pos := lemma_FindLastRightMover(clrr, lb, step_actor); var mb := lemma_MoveRightMoverToEnd(clrr, lb, step_actor, right_mover_pos); assert BehaviorRefinesBehavior(lb.states, mb.states, clrr.relation); var mb_trunc := AnnotatedBehavior(all_but_last(mb.states), all_but_last(mb.trace)); lemma_ReduceStateNextSeqRight(mb.states, mb.trace, clrr.spec.next); forall i | 0 <= i < |mb_trunc.states|-1 ensures !IsNonmoverPos(clrr, mb_trunc.states, mb_trunc.trace, i) { assert !IsNonmoverPos(clrr, mb.states, mb.trace, i); } forall ensures !clrr.crashed(last(mb_trunc.states)) { var penult := |mb.states|-2; assert ActionTuple(mb.states[penult], mb.states[penult+1], mb.trace[penult]) in clrr.spec.next; } var mb2; mb2, start_pos := lemma_BringRightMoversToEnd(clrr, mb_trunc, step_actor); assert BehaviorRefinesBehavior(mb_trunc.states, mb2.states, clrr.relation); hb := AnnotatedBehavior(mb2.states + [last(mb.states)], mb2.trace + [last(mb.trace)]); lemma_ExtendBehaviorRefinesBehaviorRightOne(mb_trunc.states, mb2.states, clrr.relation, last(mb.states)); lemma_AllButLastPlusLastIsSeq(mb.states); lemma_RefinementConvolutionTransitive(lb.states, mb.states, hb.states, clrr.relation); lemma_ExtendStateNextSeqRight(mb2.states, mb2.trace, clrr.spec.next, last(mb.states), last(mb.trace)); forall i | 0 <= i < |hb.trace| ensures !IsNonmoverPos(clrr, hb.states, hb.trace, i); { if i < |mb2.states|-1 { assert !IsNonmoverPos(clrr, mb2.states, mb2.trace, i); } else { assert i == |mb2.states|-1 == |mb.states|-2; assert hb.states[i] == last(mb2.states) == last(mb_trunc.states) == mb.states[|mb.states|-2]; assert hb.states[i+1] == last(mb.states); assert !IsNonmoverPos(clrr, mb.states, mb.trace, i); } } } lemma lemma_FindLastRightMover( clrr:CohenLamportReductionRequest, b:AnnotatedBehavior, step_actor:Actor ) returns ( pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(b, clrr.spec) requires !clrr.crashed(last(b.states)) requires clrr.phase1(last(b.states), step_actor) requires !clrr.phase2(last(b.states), step_actor) ensures 0 <= pos < |b.trace| ensures clrr.idmap(b.trace[pos]) == step_actor ensures forall i :: pos < i < |b.trace| ==> clrr.idmap(b.trace[i]) != step_actor ensures clrr.phase1(b.states[pos+1], step_actor) { pos := |b.trace|; while pos > 0 && clrr.idmap(b.trace[pos-1]) != step_actor invariant 0 <= pos <= |b.trace| invariant clrr.phase1(b.states[pos], step_actor) invariant forall i :: pos <= i < |b.trace| ==> clrr.idmap(b.trace[i]) != step_actor invariant forall i :: pos <= i < |b.states| ==> clrr.phase1(b.states[i], step_actor) decreases pos { var prev := pos-1; assert ActionTuple(b.states[prev], b.states[prev+1], b.trace[prev]) in clrr.spec.next; pos := pos - 1; } assert pos != 0; pos := pos - 1; } lemma lemma_StateNextSeqCausesPhaseMatch( clrr:CohenLamportReductionRequest, states:seq, trace:seq, step_actor:Actor, other_actor:Actor ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(states, trace, clrr.spec.next) requires forall step :: step in trace ==> clrr.idmap(step) == step_actor requires step_actor != other_actor ensures clrr.phase1(states[0], other_actor) <==> clrr.phase1(last(states), other_actor) ensures clrr.phase2(states[0], other_actor) <==> clrr.phase2(last(states), other_actor) { if |trace| > 0 { var zero := 0; assert ActionTuple(states[zero], states[zero+1], trace[zero]) in clrr.spec.next; lemma_ReduceStateNextSeqLeft(states, trace, clrr.spec.next); lemma_StateNextSeqCausesPhaseMatch(clrr, states[1..], trace[1..], step_actor, other_actor); } } lemma lemma_ShowBehaviorRefinementAfterLifting( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, start_pos:int, nonmover_pos:int, end_pos:int, step_actor:Actor, hstep:Step, hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires 0 <= start_pos <= nonmover_pos < end_pos < |lb.states| requires forall i :: start_pos <= i < end_pos ==> clrr.idmap(lb.trace[i]) == step_actor requires forall i :: start_pos < i <= nonmover_pos ==> clrr.phase1(lb.states[i], step_actor) requires forall i :: nonmover_pos < i < end_pos ==> clrr.phase2(lb.states[i], step_actor) requires ActionTuple(lb.states[start_pos], lb.states[end_pos], hstep) in clrr.spec.next requires hb.states == lb.states[..start_pos+1] + lb.states[end_pos..] requires hb.trace == lb.trace[..start_pos] + [hstep] + lb.trace[end_pos..] ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) { var pos := start_pos; while pos < nonmover_pos invariant start_pos <= pos <= nonmover_pos invariant forall i :: start_pos <= i <= pos ==> RefinementPair(lb.states[i], lb.states[start_pos]) in clrr.relation { assert RefinementPair(lb.states[pos], lb.states[start_pos]) in clrr.relation; assert ActionTuple(lb.states[pos], lb.states[pos+1], lb.trace[pos]) in clrr.spec.next; assert clrr.phase1(lb.states[pos+1], step_actor); var pos_plus := pos+1; assert ActionTuple(lb.states[pos_plus], lb.states[pos_plus+1], lb.trace[pos_plus]) in clrr.spec.next; assert !clrr.crashed(lb.states[pos+1]); assert RefinementPair(lb.states[pos+1], lb.states[pos]) in clrr.relation; assert RefinementPair(lb.states[pos+1], lb.states[start_pos]) in clrr.relation; pos := pos + 1; } pos := end_pos; while pos > nonmover_pos + 1 invariant nonmover_pos < pos <= end_pos invariant forall i :: pos <= i <= end_pos ==> RefinementPair(lb.states[i], lb.states[end_pos]) in clrr.relation { assert RefinementPair(lb.states[pos], lb.states[end_pos]) in clrr.relation; var prev := pos-1; assert ActionTuple(lb.states[prev], lb.states[prev+1], lb.trace[prev]) in clrr.spec.next; assert clrr.phase2(lb.states[prev], step_actor); assert RefinementPair(lb.states[prev], lb.states[prev+1]) in clrr.relation; assert RefinementPair(lb.states[prev], lb.states[end_pos]) in clrr.relation; pos := prev; } var lb2 := lb.states[start_pos..end_pos+1]; var hb2 := hb.states[start_pos..start_pos+2]; assert hb2[0] == lb.states[start_pos]; assert hb2[1] == lb.states[end_pos]; var lh_map1 := RepeatingValue(RefinementRange(0, 0), nonmover_pos - start_pos + 1); var lh_map2 := RepeatingValue(RefinementRange(1, 1), end_pos - nonmover_pos); var lh_map := lh_map1 + lh_map2; lemma_IndexIntoConcatenation(lh_map1, lh_map2, end_pos-start_pos); forall i | 0 <= i < |lh_map| - 1 ensures lh_map[i+1].first == lh_map[i].last || lh_map[i+1].first == lh_map[i].last + 1 { lemma_IndexIntoConcatenation(lh_map1, lh_map2, i); lemma_IndexIntoConcatenation(lh_map1, lh_map2, i+1); } assert BehaviorRefinesBehaviorUsingRefinementMap(lb2, hb2, clrr.relation, lh_map); lemma_ExtendBehaviorRefinesBehaviorLeft(lb2, hb2, clrr.relation, lb.states[..start_pos]); assert hb.states[..start_pos] == lb.states[..start_pos]; lemma_ExtendBehaviorRefinesBehaviorRight(lb.states[..start_pos] + lb2, hb.states[..start_pos] + hb2, clrr.relation, lb.states[end_pos+1..]); assert hb.states[start_pos+2..] == lb.states[end_pos+1..]; lemma_SeqEqualsThreeWayConcatentation(lb.states, start_pos, end_pos+1); lemma_SeqEqualsThreeWayConcatentation(hb.states, start_pos, start_pos+2); } lemma lemma_ShowBehaviorSatisfiesSpecAfterLifting( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, start_pos:int, nonmover_pos:int, end_pos:int, step_actor:Actor, hstep:Step, hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires 0 <= start_pos <= nonmover_pos < end_pos < |lb.states| requires forall i :: start_pos <= i < end_pos ==> clrr.idmap(lb.trace[i]) == step_actor requires forall i :: start_pos < i <= nonmover_pos ==> clrr.phase1(lb.states[i], step_actor) requires forall i :: nonmover_pos < i < end_pos ==> clrr.phase2(lb.states[i], step_actor) requires ActionTuple(lb.states[start_pos], lb.states[end_pos], hstep) in clrr.spec.next requires hb.states == lb.states[..start_pos+1] + lb.states[end_pos..] requires hb.trace == lb.trace[..start_pos] + [hstep] + lb.trace[end_pos..] ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) { assert hb.states[0] == lb.states[0]; forall i | 0 <= i < |hb.trace| ensures ActionTuple(hb.states[i], hb.states[i+1], hb.trace[i]) in clrr.spec.next { if i < start_pos { assert hb.states[i] == lb.states[i]; assert hb.states[i+1] == lb.states[i+1]; assert hb.trace[i] == lb.trace[i]; assert ActionTuple(lb.states[i], lb.states[i+1], lb.trace[i]) in clrr.spec.next; } else if i == start_pos { assert hb.states[i] == lb.states[start_pos]; assert hb.states[i+1] == lb.states[end_pos]; assert hb.trace[i] == hstep; assert ActionTuple(lb.states[start_pos], lb.states[end_pos], hstep) in clrr.spec.next; } else { var iadj := i + (end_pos - start_pos - 1); assert hb.states[i] == lb.states[iadj]; assert hb.states[i+1] == lb.states[iadj+1]; assert hb.trace[i] == lb.trace[iadj]; assert ActionTuple(lb.states[iadj], lb.states[iadj+1], lb.trace[iadj]) in clrr.spec.next; } } } lemma lemma_LiftActionSequence( clrr:CohenLamportReductionRequest, states:seq, trace:seq, actor:Actor ) returns ( hstep:Step ) requires ActionSequencesLiftable(clrr) requires ActionSequencesLiftableConditions(clrr, states, trace, actor) ensures ActionTuple(states[0], last(states), hstep) in clrr.spec.next ensures clrr.idmap(hstep) == actor { hstep :| ActionTuple(states[0], last(states), hstep) in clrr.spec.next && clrr.idmap(hstep) == actor; } lemma lemma_RemoveNonmoverAtPos( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, nonmover_pos:int, step_actor:Actor ) returns ( hb:AnnotatedBehavior, pos_past_nonmovers:int ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires 0 <= nonmover_pos < |lb.trace| requires forall i :: 0 <= i < nonmover_pos ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires clrr.idmap(lb.trace[nonmover_pos]) == step_actor requires IsNonmoverPos(clrr, lb.states, lb.trace, nonmover_pos) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures |hb.states| - pos_past_nonmovers < |lb.states| - nonmover_pos ensures 0 <= pos_past_nonmovers < |hb.states| ensures forall i :: 0 <= i < pos_past_nonmovers ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) { var mb, start_pos, end_pos := lemma_BringMoversCloseToNonmoverAtPos(clrr, lb, nonmover_pos, step_actor); var states := mb.states[start_pos..end_pos+1]; var trace := mb.trace[start_pos..end_pos]; forall i | 0 <= i < |trace| ensures ActionTuple(states[i], states[i+1], trace[i]) in clrr.spec.next ensures !clrr.crashed(states[i]) { var pos := start_pos+i; assert ActionTuple(mb.states[pos], mb.states[pos+1], mb.trace[pos]) in clrr.spec.next; } assert ActionSequencesLiftableConditions(clrr, states, trace, step_actor); var hstep := lemma_LiftActionSequence(clrr, states, trace, step_actor); var hstates := mb.states[..start_pos+1] + mb.states[end_pos..]; var htrace := mb.trace[..start_pos] + [hstep] + mb.trace[end_pos..]; hb := AnnotatedBehavior(hstates, htrace); pos_past_nonmovers := start_pos + 1; lemma_ShowBehaviorRefinementAfterLifting(clrr, mb, start_pos, nonmover_pos, end_pos, step_actor, hstep, hb); lemma_ShowBehaviorSatisfiesSpecAfterLifting(clrr, mb, start_pos, nonmover_pos, end_pos, step_actor, hstep, hb); lemma_RefinementConvolutionTransitive(lb.states, mb.states, hb.states, clrr.relation); forall i | 0 <= i < pos_past_nonmovers ensures !IsNonmoverPos(clrr, hb.states, hb.trace, i) { if i < start_pos { assert hb.states[i] == mb.states[i]; assert hb.states[i+1] == mb.states[i+1]; assert !IsNonmoverPos(clrr, mb.states, mb.trace, i); } else { assert hb.states[i] == mb.states[start_pos] == states[0]; assert hb.states[i+1] == mb.states[end_pos] == last(states); assert ActionTuple(states[0], last(states), hstep) in clrr.spec.next; assert !IsNonmoverStep(clrr, states[0], last(states), step_actor); } } } lemma lemma_BringMoversCloseToNonmoverAtPos( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, nonmover_pos:int, step_actor:Actor ) returns ( hb:AnnotatedBehavior, start_pos:int, end_pos:int ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires 0 <= nonmover_pos < |lb.trace| requires forall i :: 0 <= i < nonmover_pos ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires clrr.idmap(lb.trace[nonmover_pos]) == step_actor requires IsNonmoverPos(clrr, lb.states, lb.trace, nonmover_pos) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures |hb.states| < |lb.states| + (end_pos - nonmover_pos) ensures 0 <= start_pos <= nonmover_pos < end_pos < |hb.states| ensures forall i :: 0 <= i < nonmover_pos ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) ensures !clrr.crashed(hb.states[start_pos]) ensures !clrr.phase1(hb.states[start_pos], step_actor) ensures !clrr.phase2(hb.states[start_pos], step_actor) ensures hb.states[nonmover_pos] == lb.states[nonmover_pos] ensures hb.states[nonmover_pos+1] == lb.states[nonmover_pos+1] ensures var s := hb.states[end_pos]; !clrr.crashed(s) ==> !clrr.phase1(s, step_actor) && !clrr.phase2(s, step_actor) ensures forall i :: start_pos <= i < end_pos ==> clrr.idmap(hb.trace[i]) == step_actor ensures forall i :: start_pos < i <= nonmover_pos ==> clrr.phase1(hb.states[i], step_actor) ensures forall i :: nonmover_pos < i < end_pos ==> clrr.phase2(hb.states[i], step_actor) { lemma_TakeStateNextSeq(lb.states, lb.trace, clrr.spec.next, nonmover_pos); var pre_nonmover_behavior := AnnotatedBehavior(lb.states[..nonmover_pos+1], lb.trace[..nonmover_pos]); forall i | 0 <= i < |pre_nonmover_behavior.states|-1 ensures !IsNonmoverPos(clrr, pre_nonmover_behavior.states, pre_nonmover_behavior.trace, i) { assert pre_nonmover_behavior.states[i] == lb.states[i]; assert pre_nonmover_behavior.states[i+1] == lb.states[i+1]; assert pre_nonmover_behavior.trace[i] == lb.trace[i]; assert !IsNonmoverPos(clrr, lb.states, lb.trace, i); } assert ActionTuple(lb.states[nonmover_pos], lb.states[nonmover_pos+1], lb.trace[nonmover_pos]) in clrr.spec.next; var pre_nonmover_behavior'; pre_nonmover_behavior', start_pos := lemma_BringRightMoversToEnd(clrr, pre_nonmover_behavior, step_actor); lemma_DropStateNextSeq(lb.states, lb.trace, clrr.spec.next, nonmover_pos+1); lemma_StateInAnnotatedBehaviorInAnnotatedReachables(clrr.spec, lb, nonmover_pos+1); var post_nonmover_states', post_nonmover_trace', end_pos_relative := lemma_BringLeftMoversToStart(clrr, lb.states[nonmover_pos+1..], lb.trace[nonmover_pos+1..], step_actor); var hstates' := pre_nonmover_behavior'.states + post_nonmover_states'; var htrace' := pre_nonmover_behavior'.trace + [lb.trace[nonmover_pos]] + post_nonmover_trace'; end_pos := nonmover_pos + 1 + end_pos_relative; hb := AnnotatedBehavior(hstates', htrace'); lemma_ConcatenatingBehaviorsPreservesRefinement(lb.states[..nonmover_pos+1], lb.states[nonmover_pos+1..], pre_nonmover_behavior'.states, post_nonmover_states', clrr.relation); lemma_TakePlusDropIsSeq(lb.states, nonmover_pos+1); forall i | 0 <= i < |htrace'| ensures ActionTuple(hstates'[i], hstates'[i+1], htrace'[i]) in clrr.spec.next { if i < |pre_nonmover_behavior'.trace| { assert hstates'[i] == pre_nonmover_behavior'.states[i]; assert hstates'[i+1] == pre_nonmover_behavior'.states[i+1]; assert htrace'[i] == pre_nonmover_behavior'.trace[i]; assert ActionTuple(pre_nonmover_behavior'.states[i], pre_nonmover_behavior'.states[i+1], pre_nonmover_behavior'.trace[i]) in clrr.spec.next; } else if i == |pre_nonmover_behavior'.trace| { assert hstates'[i] == last(pre_nonmover_behavior'.states) == lb.states[nonmover_pos]; assert hstates'[i+1] == post_nonmover_states'[0] == lb.states[nonmover_pos+1]; assert htrace'[i] == lb.trace[nonmover_pos]; assert ActionTuple(lb.states[nonmover_pos], lb.states[nonmover_pos+1], lb.trace[nonmover_pos]) in clrr.spec.next; } else { var j := i - |pre_nonmover_behavior'.states|; assert hstates'[i] == post_nonmover_states'[j]; assert hstates'[i+1] == post_nonmover_states'[j+1]; assert htrace'[i] == post_nonmover_trace'[j]; assert ActionTuple(post_nonmover_states'[j], post_nonmover_states'[j+1], post_nonmover_trace'[j]) in clrr.spec.next; } } forall i | 0 <= i < nonmover_pos ensures !IsNonmoverPos(clrr, hb.states, hb.trace, i); { assert hb.states[i] == pre_nonmover_behavior'.states[i]; assert hb.states[i+1] == pre_nonmover_behavior'.states[i+1]; assert hb.trace[i] == pre_nonmover_behavior'.trace[i]; assert !IsNonmoverPos(clrr, pre_nonmover_behavior'.states, pre_nonmover_behavior'.trace, i); } } lemma lemma_RemoveAnyNonmoverAtPos( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, nonmover_pos:int ) returns ( hb:AnnotatedBehavior, pos_past_nonmovers:int ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires 0 <= nonmover_pos < |lb.trace| requires forall i :: 0 <= i < nonmover_pos ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures |hb.states| - pos_past_nonmovers < |lb.states| - nonmover_pos ensures 0 <= pos_past_nonmovers < |hb.states| ensures forall i :: 0 <= i < pos_past_nonmovers ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) { assert ActionTuple(lb.states[nonmover_pos], lb.states[nonmover_pos+1], lb.trace[nonmover_pos]) in clrr.spec.next; var step_actor := clrr.idmap(lb.trace[nonmover_pos]); if !IsNonmoverPos(clrr, lb.states, lb.trace, nonmover_pos) { hb := lb; lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lb.states, clrr.relation); pos_past_nonmovers := nonmover_pos + 1; return; } hb, pos_past_nonmovers := lemma_RemoveNonmoverAtPos(clrr, lb, nonmover_pos, step_actor); } lemma lemma_RemoveAllNonmoversFromPos( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, nonmover_pos:int ) returns ( hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires 0 <= nonmover_pos < |lb.states| requires forall i :: 0 <= i < nonmover_pos ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures forall i :: 0 <= i < |hb.trace| ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) decreases |lb.states| - nonmover_pos { if nonmover_pos == |lb.trace| { hb := lb; lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lb.states, clrr.relation); } else { var mb, pos_past_nonmovers := lemma_RemoveAnyNonmoverAtPos(clrr, lb, nonmover_pos); hb := lemma_RemoveAllNonmoversFromPos(clrr, mb, pos_past_nonmovers); lemma_RefinementConvolutionTransitive(lb.states, mb.states, hb.states, clrr.relation); } } predicate IsNonmoverStep( clrr:CohenLamportReductionRequest, s:State, s':State, actor:Actor ) { || (!clrr.phase1(s, actor) && !clrr.phase2(s, actor) && clrr.phase2(s', actor) && !clrr.crashed(s')) || (clrr.phase1(s, actor) && !clrr.phase1(s', actor)) || (clrr.phase1(s, actor) && clrr.crashed(s')) } predicate IsNonmoverPos( clrr:CohenLamportReductionRequest, states:seq, trace:seq, pos:int ) requires 0 <= pos < |trace| < |states| { IsNonmoverStep(clrr, states[pos], states[pos+1], clrr.idmap(trace[pos])) } lemma lemma_RemoveAllNonmovers( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures forall i :: 0 <= i < |hb.trace| ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) { hb := lemma_RemoveAllNonmoversFromPos(clrr, lb, 0); } lemma lemma_RemoveRightMoverFromPosCaseBeforeCrash( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, right_mover_pos:int ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires 0 <= right_mover_pos < |ltrace|-1 requires !clrr.crashed(lstates[right_mover_pos+1]) requires clrr.phase1(lstates[right_mover_pos+1], step_actor) requires clrr.idmap(ltrace[right_mover_pos]) == step_actor requires clrr.crashed(lstates[right_mover_pos+2]) requires forall i :: 0 <= i < |ltrace| ==> !IsNonmoverPos(clrr, lstates, ltrace, i) requires forall i :: 0 <= i < right_mover_pos ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) requires forall i :: right_mover_pos < i < |ltrace| ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |htrace| <= |ltrace| ensures forall i :: 0 <= i < |htrace| ==> !IsNonmoverPos(clrr, hstates, htrace, i) ensures forall i :: 0 <= i < |htrace| ==> var actor := clrr.idmap(htrace[i]); var s := hstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) { assert ActionTuple(lstates[right_mover_pos], lstates[right_mover_pos+1], ltrace[right_mover_pos]) in clrr.spec.next; var pos_plus := right_mover_pos + 1; assert ActionTuple(lstates[pos_plus], lstates[pos_plus+1], ltrace[pos_plus]) in clrr.spec.next; if |ltrace| != right_mover_pos + 2 { var pos_plus_2 := right_mover_pos + 2; assert ActionTuple(lstates[pos_plus_2], lstates[pos_plus_2+1], ltrace[pos_plus_2]) in clrr.spec.next; assert false; } lemma_StateNextSeqMaintainsAnnotatedReachables(lstates, ltrace, clrr.spec); assert lstates[right_mover_pos] in lstates; assert !IsNonmoverPos(clrr, lstates, ltrace, right_mover_pos+1); assert RightMoverCrashPreservationConditions(clrr, lstates[right_mover_pos], lstates[right_mover_pos+1], lstates[right_mover_pos+2], ltrace[right_mover_pos], ltrace[right_mover_pos+1]); var other_step', state_after_other_step' :| && clrr.idmap(other_step') == clrr.idmap(ltrace[right_mover_pos+1]) && ActionTuple(lstates[right_mover_pos], state_after_other_step', other_step') in clrr.spec.next && clrr.crashed(state_after_other_step') && RefinementPair(lstates[right_mover_pos+2], state_after_other_step') in clrr.relation; hstates := lstates[..right_mover_pos+1] + [state_after_other_step']; htrace := ltrace[..right_mover_pos] + [other_step']; assert StateNextSeq(hstates, htrace, clrr.spec.next); var lh_map := ConvertMapToSeq( |lstates|, map i | 0 <= i < |lstates| :: if i <= right_mover_pos then RefinementRange(i, i) else if i == right_mover_pos+1 then RefinementRange(right_mover_pos, right_mover_pos) else RefinementRange(right_mover_pos+1, right_mover_pos+1)); forall i, j {:trigger RefinementPair(lstates[i], hstates[j]) in clrr.relation} | 0 <= i < |lstates| && lh_map[i].first <= j <= lh_map[i].last ensures RefinementPair(lstates[i], hstates[j]) in clrr.relation { if i <= right_mover_pos { assert j == i; assert hstates[j] == lstates[i]; assert RefinementPair(lstates[i], lstates[i]) in clrr.relation; } else if i == right_mover_pos+1 { assert j == right_mover_pos; assert hstates[j] == lstates[right_mover_pos]; assert ActionTuple(lstates[right_mover_pos], lstates[right_mover_pos+1], ltrace[right_mover_pos]) in clrr.spec.next; assert !clrr.crashed(lstates[right_mover_pos+1]); assert clrr.idmap(ltrace[right_mover_pos]) == step_actor; assert clrr.phase1(lstates[right_mover_pos+1], step_actor); assert RefinementPair(lstates[right_mover_pos+1], lstates[right_mover_pos]) in clrr.relation; } else { assert i == right_mover_pos+2; assert j == right_mover_pos+1; assert hstates[j] == state_after_other_step'; assert RefinementPair(lstates[right_mover_pos+2], state_after_other_step') in clrr.relation; } } assert BehaviorRefinesBehaviorUsingRefinementMap(lstates, hstates, clrr.relation, lh_map); forall i | 0 <= i < |htrace| ensures !IsNonmoverPos(clrr, hstates, htrace, i) { if i < right_mover_pos { assert !IsNonmoverPos(clrr, lstates, ltrace, i); } else { assert hstates[i] == lstates[right_mover_pos]; assert hstates[i+1] == state_after_other_step'; assert htrace[i] == other_step'; } } } lemma lemma_RemoveRightMoverFromPosCaseAtEnd( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, right_mover_pos:int ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires |ltrace| > 0 requires right_mover_pos == |ltrace|-1; requires !clrr.crashed(lstates[right_mover_pos+1]) requires clrr.phase1(lstates[right_mover_pos+1], step_actor) requires clrr.idmap(ltrace[right_mover_pos]) == step_actor requires forall i :: 0 <= i < |ltrace| ==> !IsNonmoverPos(clrr, lstates, ltrace, i) requires forall i :: 0 <= i < right_mover_pos ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) requires forall i :: right_mover_pos < i < |ltrace| ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |htrace| <= |ltrace| ensures forall i :: 0 <= i < |htrace| ==> !IsNonmoverPos(clrr, hstates, htrace, i) ensures forall i :: 0 <= i < |htrace| ==> var actor := clrr.idmap(htrace[i]); var s := hstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) { hstates := all_but_last(lstates); htrace := all_but_last(ltrace); lemma_ReduceStateNextSeqRight(lstates, ltrace, clrr.spec.next); var lh_map := ConvertMapToSeq(|lstates|, map i | 0 <= i < |lstates| :: if i < |ltrace| then RefinementRange(i, i) else RefinementRange(|hstates|-1, |hstates|-1)); forall i, j {:trigger RefinementPair(lstates[i], hstates[j]) in clrr.relation} | 0 <= i < |lstates| && lh_map[i].first <= j <= lh_map[i].last ensures RefinementPair(lstates[i], hstates[j]) in clrr.relation { if i < |ltrace| { assert j == i; assert hstates[j] == lstates[i]; assert RefinementPair(lstates[i], lstates[i]) in clrr.relation; } else { var ult := |ltrace|-1; assert lstates[i] == lstates[ult+1]; assert j == |hstates|-1 == |ltrace|-1 == ult; assert hstates[j] == lstates[ult]; assert ActionTuple(lstates[ult], lstates[ult+1], ltrace[ult]) in clrr.spec.next; assert clrr.idmap(ltrace[ult]) == step_actor; assert clrr.phase1(lstates[ult+1], step_actor); assert RefinementPair(lstates[ult+1], lstates[ult]) in clrr.relation; } } assert BehaviorRefinesBehaviorUsingRefinementMap(lstates, hstates, clrr.relation, lh_map); forall i | 0 <= i < |htrace| ensures !IsNonmoverPos(clrr, hstates, htrace, i) { assert !IsNonmoverPos(clrr, lstates, ltrace, i); } } lemma lemma_EstablishBehaviorRefinesBehaviorAfterMovingRightMoverForRemoval( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, right_mover_pos:int, new_middle_state:State, other_step':Step, right_mover':Step, hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires 0 <= right_mover_pos < |ltrace|-1 requires !clrr.crashed(lstates[right_mover_pos+2]) requires clrr.phase1(lstates[right_mover_pos+1], step_actor) requires clrr.idmap(ltrace[right_mover_pos]) == step_actor requires clrr.idmap(ltrace[right_mover_pos+1]) != step_actor requires ActionTuple(lstates[right_mover_pos], new_middle_state, other_step') in clrr.spec.next requires ActionTuple(new_middle_state, lstates[right_mover_pos+2], right_mover') in clrr.spec.next requires clrr.idmap(other_step') == clrr.idmap(ltrace[right_mover_pos+1]) requires clrr.idmap(right_mover') == clrr.idmap(ltrace[right_mover_pos]) requires hstates == lstates[..right_mover_pos+1] + [new_middle_state] + lstates[right_mover_pos+2..] requires htrace == ltrace[..right_mover_pos] + [other_step', right_mover'] + ltrace[right_mover_pos+2..] ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures last(hstates) == last(lstates) ensures |hstates| == |lstates| ensures |htrace| == |ltrace| ensures clrr.idmap(htrace[right_mover_pos+1]) == step_actor { var lb2 := lstates[right_mover_pos..right_mover_pos+3]; var hb2 := [lstates[right_mover_pos], new_middle_state, lstates[right_mover_pos+2]]; var right_mover_pos_plus := right_mover_pos+1; assert ActionTuple(lstates[right_mover_pos_plus], lstates[right_mover_pos_plus+1], ltrace[right_mover_pos_plus]) in clrr.spec.next; assert !clrr.crashed(lstates[right_mover_pos_plus]); forall ensures RefinementPair(lb2[2], hb2[1]) in clrr.relation { assert ActionTuple(new_middle_state, lstates[right_mover_pos+2], right_mover') in clrr.spec.next; assert clrr.idmap(right_mover') == step_actor; assert ActionTuple(lstates[right_mover_pos_plus], lstates[right_mover_pos_plus+1], ltrace[right_mover_pos_plus]) in clrr.spec.next; assert clrr.idmap(ltrace[right_mover_pos_plus]) != step_actor; assert clrr.phase1(lstates[right_mover_pos_plus+1], step_actor); assert !clrr.crashed(lstates[right_mover_pos+2]); assert RefinementPair(lstates[right_mover_pos+2], new_middle_state) in clrr.relation; } forall ensures RefinementPair(lb2[1], hb2[0]) in clrr.relation { assert ActionTuple(lstates[right_mover_pos], lstates[right_mover_pos+1], ltrace[right_mover_pos]) in clrr.spec.next; assert clrr.idmap(ltrace[right_mover_pos]) == step_actor; assert !clrr.crashed(lstates[right_mover_pos+1]); assert RefinementPair(lstates[right_mover_pos+1], lstates[right_mover_pos]) in clrr.relation; } forall ensures RefinementPair(lb2[0], hb2[0]) in clrr.relation { assert hb2[0] == lb2[0]; } forall ensures RefinementPair(lb2[2], hb2[2]) in clrr.relation { assert hb2[2] == lb2[2]; } var lh_map := [RefinementRange(0, 0), RefinementRange(0, 0), RefinementRange(1, 2)]; assert BehaviorRefinesBehaviorUsingRefinementMap(lb2, hb2, clrr.relation, lh_map); lemma_ExtendBehaviorRefinesBehaviorLeft(lb2, hb2, clrr.relation, lstates[..right_mover_pos]); lemma_ExtendBehaviorRefinesBehaviorRight(lstates[..right_mover_pos] + lb2, lstates[..right_mover_pos] + hb2, clrr.relation, lstates[right_mover_pos+3..]); assert BehaviorRefinesBehavior(lstates[..right_mover_pos] + lb2 + lstates[right_mover_pos+3..], lstates[..right_mover_pos] + hb2 + lstates[right_mover_pos+3..], clrr.relation); lemma_SeqEqualsThreeWayConcatentation(lstates, right_mover_pos, right_mover_pos+3); assert hstates == lstates[..right_mover_pos] + hb2 + lstates[right_mover_pos+3..]; } lemma lemma_EstablishBehaviorSatisfiesSpecAfterMovingRightMoverForRemoval( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, right_mover_pos:int, new_middle_state:State, other_step':Step, right_mover':Step, hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires 0 <= right_mover_pos < |ltrace|-1 requires !clrr.crashed(lstates[right_mover_pos+2]) requires clrr.phase1(lstates[right_mover_pos+1], step_actor) requires clrr.idmap(ltrace[right_mover_pos]) == step_actor requires ActionTuple(lstates[right_mover_pos], new_middle_state, other_step') in clrr.spec.next requires ActionTuple(new_middle_state, lstates[right_mover_pos+2], right_mover') in clrr.spec.next requires clrr.idmap(other_step') == clrr.idmap(ltrace[right_mover_pos+1]) requires clrr.idmap(right_mover') == clrr.idmap(ltrace[right_mover_pos]) requires hstates == lstates[..right_mover_pos+1] + [new_middle_state] + lstates[right_mover_pos+2..] requires htrace == ltrace[..right_mover_pos] + [other_step', right_mover'] + ltrace[right_mover_pos+2..] ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] { forall i {:trigger ActionTuple(hstates[i], hstates[i+1], htrace[i]) in clrr.spec.next} | 0 <= i < |htrace| ensures ActionTuple(hstates[i], hstates[i+1], htrace[i]) in clrr.spec.next { if i == right_mover_pos { assert hstates[i] == lstates[right_mover_pos]; assert hstates[i+1] == new_middle_state; assert htrace[i] == other_step'; assert ActionTuple(lstates[right_mover_pos], new_middle_state, other_step') in clrr.spec.next; } else if i == right_mover_pos + 1 { assert hstates[i] == new_middle_state; assert hstates[i+1] == lstates[right_mover_pos+2]; assert htrace[i] == right_mover'; assert ActionTuple(new_middle_state, lstates[right_mover_pos+2], right_mover') in clrr.spec.next; } else { assert hstates[i] == lstates[i]; assert hstates[i+1] == lstates[i+1]; assert htrace[i] == ltrace[i]; assert ActionTuple(lstates[i], lstates[i+1], ltrace[i]) in clrr.spec.next; } } } lemma lemma_EstablishStillNoNonmoversAfterMovingRightMoverForRemoval( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, right_mover_pos:int, new_middle_state:State, other_step':Step, right_mover':Step, hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires 0 <= right_mover_pos < |ltrace|-1 requires !clrr.crashed(lstates[right_mover_pos+2]) requires clrr.phase1(lstates[right_mover_pos+1], step_actor) requires clrr.idmap(ltrace[right_mover_pos]) == step_actor requires clrr.idmap(ltrace[right_mover_pos+1]) != step_actor requires forall i :: 0 <= i < |ltrace| ==> !IsNonmoverPos(clrr, lstates, ltrace, i) requires forall i :: 0 <= i < right_mover_pos ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) requires forall i :: right_mover_pos < i < |ltrace| ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) requires ActionTuple(lstates[right_mover_pos], new_middle_state, other_step') in clrr.spec.next requires ActionTuple(new_middle_state, lstates[right_mover_pos+2], right_mover') in clrr.spec.next requires clrr.idmap(other_step') == clrr.idmap(ltrace[right_mover_pos+1]) requires clrr.idmap(right_mover') == clrr.idmap(ltrace[right_mover_pos]) requires hstates == lstates[..right_mover_pos+1] + [new_middle_state] + lstates[right_mover_pos+2..] requires htrace == ltrace[..right_mover_pos] + [other_step', right_mover'] + ltrace[right_mover_pos+2..] ensures forall i :: 0 <= i < |htrace| ==> !IsNonmoverPos(clrr, hstates, htrace, i) ensures forall i :: 0 <= i <= right_mover_pos ==> var actor := clrr.idmap(htrace[i]); var s := hstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) ensures forall i :: right_mover_pos+1 < i < |htrace| ==> var actor := clrr.idmap(htrace[i]); var s := hstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) { var right_mover_pos_plus := right_mover_pos + 1; forall i | 0 <= i < |htrace| ensures !IsNonmoverPos(clrr, hstates, htrace, i) { if i == right_mover_pos { assert hstates[i] == lstates[right_mover_pos]; assert hstates[i+1] == new_middle_state; assert !IsNonmoverPos(clrr, lstates, ltrace, right_mover_pos_plus); assert ActionTuple(lstates[right_mover_pos], lstates[right_mover_pos+1], ltrace[right_mover_pos]) in clrr.spec.next; assert ActionTuple(new_middle_state, lstates[right_mover_pos_plus+1], right_mover') in clrr.spec.next; assert !IsNonmoverStep(clrr, lstates[right_mover_pos], new_middle_state, clrr.idmap(htrace[i])); } else if i == right_mover_pos + 1 { assert hstates[i] == new_middle_state; assert hstates[i+1] == lstates[right_mover_pos+2]; } else { assert !IsNonmoverPos(clrr, lstates, ltrace, i); } } forall i | 0 <= i <= right_mover_pos ensures var actor := clrr.idmap(htrace[i]); var s := hstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) { if i < right_mover_pos { assert htrace[i] == ltrace[i]; assert hstates[i+1] == lstates[i+1]; assert var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor); } else { assert i == right_mover_pos; assert htrace[i] == other_step'; var other_actor := clrr.idmap(other_step'); assert other_actor == clrr.idmap(ltrace[right_mover_pos+1]); assert hstates[i+1] == new_middle_state; assert ActionTuple(new_middle_state, lstates[right_mover_pos+2], right_mover') in clrr.spec.next; assert clrr.idmap(right_mover') != other_actor; assert clrr.phase1(new_middle_state, other_actor) <==> clrr.phase1(lstates[right_mover_pos+2], other_actor); assert clrr.phase2(new_middle_state, other_actor) <==> clrr.phase2(lstates[right_mover_pos+2], other_actor); assert var actor := clrr.idmap(ltrace[right_mover_pos_plus]); var s := lstates[right_mover_pos_plus+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor); } } } lemma lemma_MoveRightMoverRightForRemoval( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, right_mover_pos:int ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires 0 <= right_mover_pos < |ltrace|-1 requires !clrr.crashed(lstates[right_mover_pos+2]) requires clrr.phase1(lstates[right_mover_pos+1], step_actor) requires clrr.idmap(ltrace[right_mover_pos]) == step_actor requires forall i :: 0 <= i < |ltrace| ==> !IsNonmoverPos(clrr, lstates, ltrace, i) requires forall i :: 0 <= i < right_mover_pos ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) requires forall i :: right_mover_pos < i < |ltrace| ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |htrace| == |ltrace| ensures forall i :: 0 <= i < |htrace| ==> !IsNonmoverPos(clrr, hstates, htrace, i) ensures forall i :: 0 <= i <= right_mover_pos ==> var actor := clrr.idmap(htrace[i]); var s := hstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) ensures forall i :: right_mover_pos+1 < i < |htrace| ==> var actor := clrr.idmap(htrace[i]); var s := hstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) ensures clrr.idmap(htrace[right_mover_pos+1]) == step_actor ensures clrr.phase1(hstates[right_mover_pos+2], step_actor) ensures !clrr.crashed(hstates[right_mover_pos+2]) { assert ActionTuple(lstates[right_mover_pos], lstates[right_mover_pos+1], ltrace[right_mover_pos]) in clrr.spec.next; var right_mover_pos_plus := right_mover_pos + 1; assert ActionTuple(lstates[right_mover_pos_plus], lstates[right_mover_pos_plus+1], ltrace[right_mover_pos_plus]) in clrr.spec.next; lemma_StateNextSeqMaintainsAnnotatedReachables(lstates, ltrace, clrr.spec); assert !IsNonmoverPos(clrr, lstates, ltrace, right_mover_pos+1); assert RightMoversCommuteConditions(clrr, lstates[right_mover_pos], lstates[right_mover_pos+1], lstates[right_mover_pos_plus+1], ltrace[right_mover_pos], ltrace[right_mover_pos+1]); var new_middle_state, other_step', right_mover' :| && ActionTuple(lstates[right_mover_pos], new_middle_state, other_step') in clrr.spec.next && ActionTuple(new_middle_state, lstates[right_mover_pos_plus+1], right_mover') in clrr.spec.next && clrr.idmap(other_step') == clrr.idmap(ltrace[right_mover_pos+1]) && clrr.idmap(right_mover') == clrr.idmap(ltrace[right_mover_pos]); hstates := lstates[..right_mover_pos+1] + [new_middle_state] + lstates[right_mover_pos+2..]; htrace := ltrace[..right_mover_pos] + [other_step', right_mover'] + ltrace[right_mover_pos+2..]; lemma_EstablishBehaviorRefinesBehaviorAfterMovingRightMoverForRemoval( clrr, lstates, ltrace, step_actor, right_mover_pos, new_middle_state, other_step', right_mover', hstates, htrace); lemma_EstablishBehaviorSatisfiesSpecAfterMovingRightMoverForRemoval( clrr, lstates, ltrace, step_actor, right_mover_pos, new_middle_state, other_step', right_mover', hstates, htrace); lemma_EstablishStillNoNonmoversAfterMovingRightMoverForRemoval( clrr, lstates, ltrace, step_actor, right_mover_pos, new_middle_state, other_step', right_mover', hstates, htrace); } lemma lemma_RemoveRightMoverFromPos( clrr:CohenLamportReductionRequest, lstates:seq, ltrace:seq, step_actor:Actor, right_mover_pos:int ) returns ( hstates:seq, htrace:seq ) requires ValidCohenLamportReductionRequest(clrr) requires StateNextSeq(lstates, ltrace, clrr.spec.next) requires lstates[0] in AnnotatedReachables(clrr.spec) requires 0 <= right_mover_pos < |ltrace| requires !clrr.crashed(lstates[right_mover_pos+1]) requires clrr.phase1(lstates[right_mover_pos+1], step_actor) requires clrr.idmap(ltrace[right_mover_pos]) == step_actor requires forall i :: 0 <= i < |ltrace| ==> !IsNonmoverPos(clrr, lstates, ltrace, i) requires forall i :: 0 <= i < right_mover_pos ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) requires forall i :: right_mover_pos < i < |ltrace| ==> var actor := clrr.idmap(ltrace[i]); var s := lstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) ensures BehaviorRefinesBehavior(lstates, hstates, clrr.relation) ensures StateNextSeq(hstates, htrace, clrr.spec.next) ensures hstates[0] == lstates[0] ensures |htrace| <= |ltrace| ensures forall i :: 0 <= i < |htrace| ==> !IsNonmoverPos(clrr, hstates, htrace, i) ensures forall i :: 0 <= i < |htrace| ==> var actor := clrr.idmap(htrace[i]); var s := hstates[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) decreases |lstates| - right_mover_pos { if right_mover_pos == |ltrace|-1 { hstates, htrace := lemma_RemoveRightMoverFromPosCaseAtEnd(clrr, lstates, ltrace, step_actor, right_mover_pos); return; } if clrr.crashed(lstates[right_mover_pos+2]) { hstates, htrace := lemma_RemoveRightMoverFromPosCaseBeforeCrash(clrr, lstates, ltrace, step_actor, right_mover_pos); return; } var mstates, mtrace := lemma_MoveRightMoverRightForRemoval(clrr, lstates, ltrace, step_actor, right_mover_pos); hstates, htrace := lemma_RemoveRightMoverFromPos(clrr, mstates, mtrace, step_actor, right_mover_pos+1); lemma_RefinementConvolutionTransitive(lstates, mstates, hstates, clrr.relation); } lemma lemma_NoNonmoversMeansNoPhase2( clrr:CohenLamportReductionRequest, b:AnnotatedBehavior, pos:int, step_actor:Actor ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(b, clrr.spec) requires forall i :: 0 <= i < |b.trace| ==> !IsNonmoverPos(clrr, b.states, b.trace, i) requires 0 <= pos < |b.states| ensures !clrr.crashed(b.states[pos]) ==> !clrr.phase2(b.states[pos], step_actor) decreases pos { if pos > 0 { var prev := pos - 1; assert ActionTuple(b.states[prev], b.states[prev+1], b.trace[prev]) in clrr.spec.next; lemma_NoNonmoversMeansNoPhase2(clrr, b, prev, step_actor); assert !IsNonmoverPos(clrr, b.states, b.trace, prev); } } lemma lemma_RemoveAllRightMoversAtOrBeforePos( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior, right_mover_pos:int ) returns ( hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires forall i :: 0 <= i < |lb.trace| ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) requires 0 <= right_mover_pos < |lb.trace| requires forall i :: right_mover_pos < i < |lb.trace| ==> var actor := clrr.idmap(lb.trace[i]); var s := lb.states[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures forall i :: 0 <= i < |hb.trace| ==> !IsNonmoverPos(clrr, hb.states, hb.trace, i) ensures forall i :: 0 <= i < |hb.trace| ==> var actor := clrr.idmap(hb.trace[i]); var s := hb.states[i+1]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) decreases right_mover_pos; { var step_actor := clrr.idmap(lb.trace[right_mover_pos]); var s := lb.states[right_mover_pos]; var s' := lb.states[right_mover_pos+1]; lemma_NoNonmoversMeansNoPhase2(clrr, lb, right_mover_pos+1, step_actor); if clrr.crashed(s') || !clrr.phase1(s', step_actor) { if right_mover_pos == 0 { hb := lb; lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lb.states, clrr.relation); } else { hb := lemma_RemoveAllRightMoversAtOrBeforePos(clrr, lb, right_mover_pos-1); } return; } var lstates_trunc := lb.states[right_mover_pos..]; var ltrace_trunc := lb.trace[right_mover_pos..]; forall i | 0 <= i < |ltrace_trunc| ensures !IsNonmoverPos(clrr, lstates_trunc, ltrace_trunc, i) { lemma_IndexIntoDrop(lb.states, right_mover_pos, i); lemma_IndexIntoDrop(lb.states, right_mover_pos, i+1); assert lstates_trunc[i] == lb.states[right_mover_pos+i]; assert lstates_trunc[i+1] == lb.states[right_mover_pos+i+1]; assert !IsNonmoverPos(clrr, lb.states, lb.trace, right_mover_pos+i); } lemma_StateInAnnotatedBehaviorInAnnotatedReachables(clrr.spec, lb, right_mover_pos); var mstates_trunc, mtrace_trunc := lemma_RemoveRightMoverFromPos(clrr, lstates_trunc, ltrace_trunc, step_actor, 0); lemma_ExtendBehaviorRefinesBehaviorLeft(lb.states[right_mover_pos..], mstates_trunc, clrr.relation, lb.states[..right_mover_pos]); lemma_TakePlusDropIsSeq(lb.states, right_mover_pos); var mb := AnnotatedBehavior(lb.states[..right_mover_pos] + mstates_trunc, lb.trace[..right_mover_pos] + mtrace_trunc); assert BehaviorRefinesBehavior(lb.states, mb.states, clrr.relation); assert StateNextSeq(mb.states, mb.trace, clrr.spec.next); forall i | 0 <= i < |mb.trace| ensures !IsNonmoverPos(clrr, mb.states, mb.trace, i) { if i < right_mover_pos-1 { assert mb.states[i] == lb.states[i]; assert mb.states[i+1] == lb.states[i+1]; assert mb.trace[i] == lb.trace[i]; assert !IsNonmoverPos(clrr, lb.states, lb.trace, i); } else if i == right_mover_pos-1 { assert mb.states[i] == lb.states[i]; assert mb.states[i+1] == mstates_trunc[0] == lstates_trunc[0] == lb.states[right_mover_pos] == lb.states[i+1]; assert mb.trace[i] == lb.trace[i]; assert !IsNonmoverPos(clrr, lb.states, lb.trace, i); } else { var iadj := i - right_mover_pos; assert mb.states[i] == mstates_trunc[iadj]; assert mb.states[i+1] == mstates_trunc[iadj+1]; assert mb.trace[i] == mtrace_trunc[iadj]; assert !IsNonmoverPos(clrr, mstates_trunc, mtrace_trunc, iadj); } } if right_mover_pos == 0 { hb := mb; } else { hb := lemma_RemoveAllRightMoversAtOrBeforePos(clrr, mb, right_mover_pos-1); lemma_RefinementConvolutionTransitive(lb.states, mb.states, hb.states, clrr.relation); } } lemma lemma_RemoveAllRightMovers( clrr:CohenLamportReductionRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidCohenLamportReductionRequest(clrr) requires AnnotatedBehaviorSatisfiesSpec(lb, clrr.spec) requires forall i :: 0 <= i < |lb.trace| ==> !IsNonmoverPos(clrr, lb.states, lb.trace, i) ensures BehaviorRefinesBehavior(lb.states, hb.states, clrr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, clrr.spec) ensures forall i, actor :: 0 <= i < |hb.states| ==> var s := hb.states[i]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) { if |lb.trace| == 0 { hb := lb; lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(lb.states, clrr.relation); } else { hb := lemma_RemoveAllRightMoversAtOrBeforePos(clrr, lb, |lb.trace|-1); } forall j, actor | 0 <= j < |hb.states| ensures var s := hb.states[j]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) { var pos := 0; while pos < |hb.trace| invariant 0 <= pos <= |hb.trace| invariant forall i :: 0 <= i <= pos ==> var s := hb.states[i]; !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) { var s := hb.states[pos]; var s' := hb.states[pos+1]; if clrr.idmap(hb.trace[pos]) == actor { assert !clrr.crashed(s') ==> !clrr.phase1(s', actor) && !clrr.phase2(s', actor); } else { assert ActionTuple(hb.states[pos], hb.states[pos+1], hb.trace[pos]) in clrr.spec.next; assert !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor); assert clrr.phase1(s, actor) <==> clrr.phase1(s', actor); assert clrr.phase2(s, actor) <==> clrr.phase2(s', actor); } pos := pos + 1; } } } } ================================================ FILE: Armada/strategies/reduction/CohenLamportReductionSpec.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file is the specification for a request to perform a Cohen-Lamport reduction on a behavior. // Such a reduction takes a behavior where some states are in phase 1 or 2, and returns a behavior // in which no state (except possibly the last, crashing state) is in phase 1 or 2. That resulting // behavior satisfies the same specification, but is a refinement of the original behavior. // // To use this specification, first create a request r of type CohenLamportReductionRequest. Then, // prove that it's a valid request, i.e., that ValidCohenLamportReductionRequest(r). Finally, call // lemma_PerformCohenLamportReduction (in CohenLamportReduction.i.dfy). // // The request specification allows behaviors that crash as their final step. But, the request // specification also demands that action sequences be reducible if they crash in the middle, i.e., // even if the actor performing those action sequences executes a step that crashes while in phase 1 // or 2. // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "../../util/collections/seqs.s.dfy" include "../../spec/refinement.s.dfy" include "../refinement/GeneralRefinementLemmas.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" module CohenLamportReductionSpecModule { import opened util_collections_seqs_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule datatype CohenLamportReductionRequest = CohenLamportReductionRequest( spec:AnnotatedBehaviorSpec, relation:RefinementRelation, idmap:Step->Actor, phase1:(State, Actor)->bool, phase2:(State, Actor)->bool, crashed:State->bool ) predicate PhaseUnaffectedByOtherActors( clrr:CohenLamportReductionRequest ) { && (forall s, s', step, actor :: ActionTuple(s, s', step) in clrr.spec.next && clrr.idmap(step) != actor ==> (clrr.phase1(s, actor) <==> clrr.phase1(s', actor))) && (forall s, s', step, actor :: ActionTuple(s, s', step) in clrr.spec.next && clrr.idmap(step) != actor ==> (clrr.phase2(s, actor) <==> clrr.phase2(s', actor))) } predicate CantGoDirectlyFromPhase2ToPhase1( clrr:CohenLamportReductionRequest ) { forall s, s', step :: ActionTuple(s, s', step) in clrr.spec.next && clrr.phase2(s, clrr.idmap(step)) ==> !clrr.phase1(s', clrr.idmap(step)) } predicate RightMoversPreserveStateRefinement( clrr:CohenLamportReductionRequest ) { forall s, s', step :: && ActionTuple(s, s', step) in clrr.spec.next && clrr.phase1(s', clrr.idmap(step)) && !clrr.crashed(s') ==> RefinementPair(s', s) in clrr.relation } predicate LeftMoversPreserveStateRefinement( clrr:CohenLamportReductionRequest ) { forall s, s', step :: && ActionTuple(s, s', step) in clrr.spec.next && clrr.phase2(s, clrr.idmap(step)) ==> RefinementPair(s, s') in clrr.relation } predicate NoStepsAfterCrash( clrr:CohenLamportReductionRequest ) { forall s, s', step :: ActionTuple(s, s', step) in clrr.spec.next ==> !clrr.crashed(s) } predicate RightMoversCommuteConditions( clrr:CohenLamportReductionRequest, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:Step, other_step:Step ) { && initial_state in AnnotatedReachables(clrr.spec) && ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next && ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next && clrr.idmap(right_mover) != clrr.idmap(other_step) && clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) && !clrr.crashed(state_after_both_steps) } predicate RightMoversCommute( clrr:CohenLamportReductionRequest ) { forall initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step {:trigger RightMoversCommuteConditions(clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step)} :: RightMoversCommuteConditions(clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step) ==> exists new_middle_state, other_step', right_mover' :: && ActionTuple(initial_state, new_middle_state, other_step') in clrr.spec.next && ActionTuple(new_middle_state, state_after_both_steps, right_mover') in clrr.spec.next && clrr.idmap(other_step') == clrr.idmap(other_step) && clrr.idmap(right_mover') == clrr.idmap(right_mover) } predicate LeftMoversCommuteConditions( clrr:CohenLamportReductionRequest, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:Step, left_mover:Step ) { && initial_state in AnnotatedReachables(clrr.spec) && ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next && ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next && clrr.idmap(other_step) != clrr.idmap(left_mover) && clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) && !clrr.crashed(state_after_both_steps) } predicate LeftMoversCommute( clrr:CohenLamportReductionRequest ) { forall initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover {:trigger LeftMoversCommuteConditions(clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover)} :: LeftMoversCommuteConditions(clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover) ==> exists new_middle_state, left_mover', other_step' :: && ActionTuple(initial_state, new_middle_state, left_mover') in clrr.spec.next && ActionTuple(new_middle_state, state_after_both_steps, other_step') in clrr.spec.next && clrr.idmap(left_mover') == clrr.idmap(left_mover) && clrr.idmap(other_step') == clrr.idmap(other_step) } predicate LeftMoversAlwaysEnabledConditions( clrr:CohenLamportReductionRequest, s:State, actor:Actor ) { && s in AnnotatedReachables(clrr.spec) && clrr.phase2(s, actor) && !clrr.crashed(s) } predicate LeftMoversAlwaysEnabled( clrr:CohenLamportReductionRequest ) { forall s, actor {:trigger LeftMoversAlwaysEnabledConditions(clrr, s, actor)} :: LeftMoversAlwaysEnabledConditions(clrr, s, actor) ==> exists states, trace :: && StateNextSeq(states, trace, clrr.spec.next) && states[0] == s && (clrr.crashed(last(states)) || !clrr.phase2(last(states), actor)) && (forall i :: 0 <= i < |states|-1 ==> clrr.phase2(states[i], actor)) && (forall step :: step in trace ==> clrr.idmap(step) == actor) } predicate LeftMoversEnabledBeforeCrashConditions( clrr:CohenLamportReductionRequest, initial_state:State, post_crash_state:State, crash_step:Step, actor:Actor ) { && initial_state in AnnotatedReachables(clrr.spec) && ActionTuple(initial_state, post_crash_state, crash_step) in clrr.spec.next && !clrr.crashed(initial_state) && clrr.crashed(post_crash_state) && clrr.idmap(crash_step) != actor && clrr.phase2(initial_state, actor) } predicate LeftMoversEnabledBeforeCrash( clrr:CohenLamportReductionRequest ) { forall initial_state, post_crash_state, crash_step, actor {:trigger LeftMoversEnabledBeforeCrashConditions(clrr, initial_state, post_crash_state, crash_step, actor)} :: LeftMoversEnabledBeforeCrashConditions(clrr, initial_state, post_crash_state, crash_step, actor) ==> exists states, trace, crash_step', post_crash_state' :: && StateNextSeq(states, trace, clrr.spec.next) && states[0] == initial_state && !clrr.crashed(last(states)) && !clrr.phase2(last(states), actor) && (forall i :: 0 <= i < |states|-1 ==> clrr.phase2(states[i], actor)) && (forall step :: step in trace ==> clrr.idmap(step) == actor) && ActionTuple(last(states), post_crash_state', crash_step') in clrr.spec.next && clrr.idmap(crash_step') == clrr.idmap(crash_step) && clrr.crashed(post_crash_state') && RefinementPair(post_crash_state, post_crash_state') in clrr.relation } predicate RightMoverCrashPreservationConditions( clrr:CohenLamportReductionRequest, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:Step, other_step:Step ) { && initial_state in AnnotatedReachables(clrr.spec) && ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next && ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next && !clrr.crashed(initial_state) && !clrr.crashed(state_after_right_mover) && clrr.crashed(state_after_both_steps) && clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) && clrr.idmap(right_mover) != clrr.idmap(other_step) } predicate RightMoverCrashPreservation( clrr:CohenLamportReductionRequest ) { forall initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step {:trigger RightMoverCrashPreservationConditions(clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step)} :: RightMoverCrashPreservationConditions(clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step) ==> exists other_step', state_after_other_step' :: && clrr.idmap(other_step') == clrr.idmap(other_step) && ActionTuple(initial_state, state_after_other_step', other_step') in clrr.spec.next && clrr.crashed(state_after_other_step') && RefinementPair(state_after_both_steps, state_after_other_step') in clrr.relation } predicate LeftMoverSelfCrashPreservationConditions( clrr:CohenLamportReductionRequest, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:Step, left_mover:Step ) { && initial_state in AnnotatedReachables(clrr.spec) && ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next && ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next && !clrr.crashed(initial_state) && !clrr.crashed(state_after_other_step) && clrr.crashed(state_after_both_steps) && clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) && clrr.idmap(left_mover) != clrr.idmap(other_step) } predicate LeftMoverSelfCrashPreservation( clrr:CohenLamportReductionRequest ) { forall initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover {:trigger LeftMoverSelfCrashPreservationConditions(clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover)} :: LeftMoverSelfCrashPreservationConditions(clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover) ==> exists left_mover', state_after_left_mover' :: && clrr.idmap(left_mover') == clrr.idmap(left_mover) && ActionTuple(initial_state, state_after_left_mover', left_mover') in clrr.spec.next && clrr.crashed(state_after_left_mover') && RefinementPair(state_after_both_steps, state_after_left_mover') in clrr.relation } predicate ActionSequencesLiftableConditions( clrr:CohenLamportReductionRequest, states:seq, trace:seq, actor:Actor ) { && StateNextSeq(states, trace, clrr.spec.next) && (forall step :: step in trace ==> clrr.idmap(step) == actor) && |states| > 1 && !clrr.phase1(states[0], actor) && !clrr.phase2(states[0], actor) && (var s := last(states); !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor)) && (forall i :: 0 <= i < |trace| ==> !clrr.crashed(states[i])) && (forall i :: 0 < i < |trace| ==> var s := states[i]; clrr.phase1(s, actor) || clrr.phase2(s, actor)) } predicate ActionSequencesLiftable( clrr:CohenLamportReductionRequest ) { forall states, trace, actor {:trigger ActionSequencesLiftableConditions(clrr, states, trace, actor)} :: ActionSequencesLiftableConditions(clrr, states, trace, actor) ==> exists hstep :: ActionTuple(states[0], last(states), hstep) in clrr.spec.next && clrr.idmap(hstep) == actor } predicate ValidCohenLamportReductionRequest( clrr:CohenLamportReductionRequest ) { && RefinementRelationReflexive(clrr.relation) && RefinementRelationTransitive(clrr.relation) && (forall s, actor :: s in clrr.spec.init ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor)) && (forall s, actor :: clrr.phase1(s, actor) ==> !clrr.phase2(s, actor)) && CantGoDirectlyFromPhase2ToPhase1(clrr) && PhaseUnaffectedByOtherActors(clrr) && RightMoversPreserveStateRefinement(clrr) && LeftMoversPreserveStateRefinement(clrr) && NoStepsAfterCrash(clrr) && RightMoversCommute(clrr) && LeftMoversCommute(clrr) && LeftMoversAlwaysEnabled(clrr) && LeftMoversEnabledBeforeCrash(clrr) && RightMoverCrashPreservation(clrr) && LeftMoverSelfCrashPreservation(clrr) && ActionSequencesLiftable(clrr) } } ================================================ FILE: Armada/strategies/reduction/RefinementViaReduction.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file contains a lemma for performing refinement via a Cohen-Lamport reduction on a // behavior. Such a reduction takes a behavior where some states are in phase 1 or 2, and returns a // behavior satisfying a higher-level specification that lacks those phases. It does so by lifting // sequences of steps in the lower-level specification to atomic steps in the higher-level // specification. // // To use this specification, first create a request r of type RefinementViaReductionRequest. Then, // prove that it's a valid request, i.e., that ValidRefinementViaReductionRequest(r). Finally, call // lemma_PerformRefinementViaReduction (in RefinementViaReduction.i.dfy). // // The request specification allows behaviors that crash as their final step. But, the request // specification also demands that action sequences be reducible if they crash in the middle, i.e., // even if the actor performing those action sequences executes a step that crashes while in phase 1 // or 2. // // This lemma makes use of a lemma (lemma_PerformCohenLamportReduction) for performing Cohen-Lamport // reduction on a behavior without changing its specification. // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "RefinementViaReductionLemmas.i.dfy" include "CohenLamportReduction.i.dfy" module RefinementViaReductionModule { import opened AnnotatedBehaviorModule import opened RefinementViaReductionSpecModule import opened RefinementViaReductionLemmasModule import opened GeneralRefinementModule import opened RefinementConvolutionModule import opened CohenLamportReductionModule lemma lemma_PerformRefinementViaReduction( rr:RefinementViaReductionRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidRefinementViaReductionRequest(rr) requires AnnotatedBehaviorSatisfiesSpec(lb, rr.lspec) ensures BehaviorRefinesBehavior(lb.states, hb.states, rr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, rr.hspec) { var cb := lemma_LiftBehaviorToCrossLevel(rr, lb); var clrr := GetCohenLamportReductionRequest(rr); lemma_IsValidCohenLamportReductionRequest(rr); var mb := lemma_PerformCohenLamportReduction(clrr, cb); lemma_RefinementConvolutionPure(lb.states, cb.states, mb.states, GetIdentityRelation(), clrr.relation, rr.relation); hb := lemma_LiftBehaviorToHighLayer(rr, mb); } } ================================================ FILE: Armada/strategies/reduction/RefinementViaReductionLemmas.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file contains lemmas useful in effecting a refinement via reduction on a behavior. They // support lemma_PerformRefinementViaReduction (in RefinementViaReduction.i.dfy). // // The general strategy is as follows. We create a cross-level specification that bridges the gap // between the low-level spec and the high-level spec. We then: // (1) Lift the given low-level behavior to produce a cross-level behavior. // (2) Use the Cohen-Lamport reduction library to reduce that to another cross-level behavior. // (3) Lift the resulting cross-level behavior to produce a high-level behavior. // // The cross-level specification has two types of steps: Low and Reducible. A Low step consists of // a single low-level step, while a Reducible step consists of a reducible sequence of low-level // steps. // // It's easy to lift a low-level behavior to produce a cross-level behavior, since it just involves // converting every step into a Low step. // // The cross-level specification satisfies the conditions required by the Cohen-Lamport reduction // library, since it provides the ability to lift any reducible sequence of steps to another step in // the same specification (by turning them into a Reducible step). // // Finally, once the Cohen-Lamport reduction library has finished with the cross-level behavior, // it's easy to lift it to the high-level spec. After all, all the non-liftable steps (the ones // that start or end in phase 1 or 2) have been eliminated. // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "RefinementViaReductionSpec.i.dfy" include "CohenLamportReductionSpec.i.dfy" include "../../util/collections/seqs.i.dfy" module RefinementViaReductionLemmasModule { import opened util_collections_seqs_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened RefinementViaReductionSpecModule import opened CohenLamportReductionSpecModule import opened util_collections_seqs_i /////////////////////////////////////////// // CrossLevel spec /////////////////////////////////////////// datatype PhaseType = PhaseType1 | PhaseType2 | PhaseTypeNeither datatype MoverType = MoverTypeRight | MoverTypeLeft | MoverTypeNonmover | MoverTypeExternal | MoverTypeImpossible datatype CrossLevelStep = | Low(actor:Actor, step:LStep) | Reducible(actor:Actor, states:seq, trace:seq) predicate ValidReducibleCrossLevelStep( rr:RefinementViaReductionRequest, states:seq, trace:seq, actor:Actor ) { && StateNextSeq(states, trace, rr.lspec.next) && (forall step :: step in trace ==> rr.idmap(step) == actor) && |states| > 1 && !rr.phase1(states[0], actor) && !rr.phase2(states[0], actor) && (var s := last(states); !rr.crashed(s) ==> !rr.phase1(s, actor) && !rr.phase2(s, actor)) && (forall i :: 0 <= i < |trace| ==> !rr.crashed(states[i])) && (forall i :: 0 < i < |trace| ==> rr.phase1(states[i], actor) || rr.phase2(states[i], actor)) } predicate CrossLevelNext( rr:RefinementViaReductionRequest, s:State, s':State, cstep:CrossLevelStep ) { match cstep case Low(actor, step) => !rr.crashed(s) && ActionTuple(s, s', step) in rr.lspec.next && actor == rr.idmap(step) case Reducible(actor, states, trace) => ValidReducibleCrossLevelStep(rr, states, trace, actor) && states[0] == s && last(states) == s' } function GetCrossLevelSpec( rr:RefinementViaReductionRequest ) : AnnotatedBehaviorSpec> { AnnotatedBehaviorSpec(rr.lspec.init, iset s, s', step | CrossLevelNext(rr, s, s', step) :: ActionTuple(s, s', step)) } function GetIdentityRelation() : RefinementRelation { iset s | true :: RefinementPair(s, s) } lemma lemma_StateInCrossLevelBehaviorReachableInBaseSpec( rr:RefinementViaReductionRequest, b:AnnotatedBehavior>, pos:int ) requires ValidRefinementViaReductionRequest(rr) requires AnnotatedBehaviorSatisfiesSpec(b, GetCrossLevelSpec(rr)) requires 0 <= pos < |b.states| ensures b.states[pos] in AnnotatedReachables(rr.lspec) decreases pos { var cspec := GetCrossLevelSpec(rr); if pos == 0 { assert b.states[pos] in cspec.init; assert b.states[pos] in rr.lspec.init; assert AnnotatedReachables(rr.lspec) == AnnotatedReachablesPremium(rr.lspec); return; } var prev := pos-1; lemma_StateInCrossLevelBehaviorReachableInBaseSpec(rr, b, prev); assert ActionTuple(b.states[prev], b.states[prev+1], b.trace[prev]) in cspec.next; if b.trace[prev].Low? { lemma_NextMaintainsAnnotatedReachables(b.states[prev], b.states[prev+1], b.trace[prev].step, rr.lspec); } else { lemma_StateNextSeqMaintainsAnnotatedReachables(b.trace[prev].states, b.trace[prev].trace, rr.lspec); assert last(b.trace[prev].states) == b.states[prev+1]; assert b.states[prev+1] in b.trace[prev].states; } } lemma lemma_AnnotatedReachablesOfCrossLevelSpec( rr:RefinementViaReductionRequest ) requires ValidRefinementViaReductionRequest(rr) ensures AnnotatedReachables(GetCrossLevelSpec(rr)) == AnnotatedReachables(rr.lspec) { var cspec := GetCrossLevelSpec(rr); reveal AnnotatedReachables(); forall s | s in AnnotatedReachables(rr.lspec) ensures s in AnnotatedReachables(cspec) { var ab :| AnnotatedBehaviorSatisfiesSpec(ab, rr.lspec) && last(ab.states) == s; var cb := lemma_LiftBehaviorToCrossLevel(rr, ab); assert AnnotatedBehaviorSatisfiesSpec(cb, cspec); var relation := GetIdentityRelation(); var ac_map :| BehaviorRefinesBehaviorUsingRefinementMap(ab.states, cb.states, relation, ac_map); assert last(ac_map).last == |cb.states|-1; assert RefinementPair(last(ab.states), last(cb.states)) in relation; } forall s | s in AnnotatedReachables(cspec) ensures s in AnnotatedReachables(rr.lspec) { var cb :| AnnotatedBehaviorSatisfiesSpec(cb, cspec) && last(cb.states) == s; lemma_StateInCrossLevelBehaviorReachableInBaseSpec(rr, cb, |cb.states|-1); } } function LiftStepToCrossLevel( rr:RefinementViaReductionRequest, s:State, s':State, step:LStep ) : CrossLevelStep { Low(rr.idmap(step), step) } lemma lemma_LiftStateNextSeqToCrossLevel( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, states:seq, ltrace:seq ) returns ( htrace:seq> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires StateNextSeq(states, ltrace, rr.lspec.next) requires !rr.crashed(last(states)) ensures StateNextSeq(states, htrace, clrr.spec.next) ensures forall state :: state in states ==> !rr.crashed(state) ensures forall i :: 0 <= i < |htrace| ==> htrace[i] == Low(rr.idmap(ltrace[i]), ltrace[i]) { forall i | 0 <= i < |states| ensures !rr.crashed(states[i]) { if rr.crashed(states[i]) { lemma_AllStatesFollowingCrashedStateSame(rr, states, ltrace, i); assert states[i] == last(states); assert false; } } htrace := MapSeqToSeq(ltrace, x => Low(rr.idmap(x), x)); forall i {:trigger ActionTuple(states[i], states[i+1], htrace[i]) in clrr.spec.next} | 0 <= i < |htrace| ensures ActionTuple(states[i], states[i+1], htrace[i]) in clrr.spec.next { assert ActionTuple(states[i], states[i+1], ltrace[i]) in rr.lspec.next; assert htrace[i].Low?; assert !rr.crashed(states[i]); assert htrace[i].actor == rr.idmap(htrace[i].step); assert CrossLevelNext(rr, states[i], states[i+1], htrace[i]); } } lemma lemma_FindFirstCrashingState( rr:RefinementViaReductionRequest, states:seq, trace:seq ) returns ( pos:int ) requires ValidRefinementViaReductionRequest(rr) requires StateNextSeq(states, trace, rr.lspec.next) ensures 0 <= pos <= |states| ensures forall i :: 0 <= i < pos ==> !rr.crashed(states[i]) ensures pos < |states| ==> rr.crashed(states[pos]) ensures pos < |states| ==> forall i :: pos < i < |states| ==> states[i] == states[pos] { pos := 0; while pos < |trace| && !rr.crashed(states[pos]) invariant 0 <= pos <= |trace| invariant forall i :: 0 <= i < pos ==> !rr.crashed(states[i]) { assert ActionTuple(states[pos], states[pos+1], trace[pos]) in rr.lspec.next; pos := pos + 1; } if pos == |trace| { if !rr.crashed(states[pos]) { pos := pos + 1; } return; } var j := pos; while j < |trace| invariant pos <= j <= |trace| invariant forall i :: pos <= i <= j ==> states[i] == states[pos] { assert ActionTuple(states[j], states[j+1], trace[j]) in rr.lspec.next; j := j + 1; } } lemma lemma_LiftBehaviorToCrossLevel( rr:RefinementViaReductionRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior> ) requires ValidRefinementViaReductionRequest(rr) requires AnnotatedBehaviorSatisfiesSpec(lb, rr.lspec) ensures BehaviorRefinesBehavior(lb.states, hb.states, GetIdentityRelation()) ensures AnnotatedBehaviorSatisfiesSpec(hb, GetCrossLevelSpec(rr)) { var pos := lemma_FindFirstCrashingState(rr, lb.states, lb.trace); var relation := GetIdentityRelation(); if pos == |lb.states| { var htrace := ConvertMapToSeq(|lb.trace|, map i | 0 <= i < |lb.trace| :: LiftStepToCrossLevel(rr, lb.states[i], lb.states[i+1], lb.trace[i])); hb := AnnotatedBehavior(lb.states, htrace); var lh_map := ConvertMapToSeq(|lb.states|, map i | 0 <= i < |lb.states| :: RefinementRange(i, i)); assert BehaviorRefinesBehaviorUsingRefinementMap(lb.states, hb.states, relation, lh_map); } else { var htrace := ConvertMapToSeq(pos, map i | 0 <= i < pos :: LiftStepToCrossLevel(rr, lb.states[i], lb.states[i+1], lb.trace[i])); hb := AnnotatedBehavior(lb.states[..pos+1], htrace); var lh_map := ConvertMapToSeq(|lb.states|, map i | 0 <= i < |lb.states| :: if i <= pos then RefinementRange(i, i) else RefinementRange(pos, pos)); assert BehaviorRefinesBehaviorUsingRefinementMap(lb.states, hb.states, relation, lh_map); } } lemma lemma_LiftBehaviorToHighLayer( rr:RefinementViaReductionRequest, cb:AnnotatedBehavior> ) returns ( hb:AnnotatedBehavior ) requires ValidRefinementViaReductionRequest(rr) requires AnnotatedBehaviorSatisfiesSpec(cb, GetCrossLevelSpec(rr)) requires forall i, actor :: 0 <= i < |cb.states| ==> var s := cb.states[i]; !rr.crashed(s) ==> !rr.phase1(s, actor) && !rr.phase2(s, actor) ensures hb.states == cb.states ensures AnnotatedBehaviorSatisfiesSpec(hb, rr.hspec) { var cspec := GetCrossLevelSpec(rr); var htrace := []; var n := 0; assert |cb.states| == |cb.trace| + 1; while n < |cb.trace| invariant 0 <= n <= |cb.trace| invariant |htrace| == n invariant forall i :: 0 <= i < n ==> ActionTuple(cb.states[i], cb.states[i+1], htrace[i]) in rr.hspec.next { assert ActionTuple(cb.states[n], cb.states[n+1], cb.trace[n]) in cspec.next; var cstep := cb.trace[n]; var states:seq, trace:seq, actor:Actor; if cstep.Low? { states := [cb.states[n], cb.states[n+1]]; trace := [cstep.step]; actor := rr.idmap(cstep.step); } else { states := cstep.states; trace := cstep.trace; actor := cstep.actor; } assert RefinementViaReductionSpecModule.ActionSequencesLiftableConditions(rr, states, trace, actor); var hstep :| ActionTuple(states[0], last(states), hstep) in rr.hspec.next; htrace := htrace + [hstep]; n := n + 1; } hb := AnnotatedBehavior(cb.states, htrace); } lemma lemma_StateInCSpecBehaviorAmongAnnotatedReachables( rr:RefinementViaReductionRequest, b:AnnotatedBehavior>, pos:int ) requires ValidRefinementViaReductionRequest(rr) requires AnnotatedBehaviorSatisfiesSpec(b, GetCrossLevelSpec(rr)) requires 0 <= pos < |b.states| ensures b.states[pos] in AnnotatedReachables(GetCrossLevelSpec(rr)) ensures b.states[pos] in AnnotatedReachables(rr.lspec) { var cspec := GetCrossLevelSpec(rr); var ar := AnnotatedReachablesPremium(cspec); lemma_AnnotatedReachablesOfCrossLevelSpec(rr); if pos > 0 { var prev_pos := pos - 1; assert ActionTuple(b.states[prev_pos], b.states[prev_pos+1], b.trace[prev_pos]) in cspec.next; lemma_StateInCSpecBehaviorAmongAnnotatedReachables(rr, b, prev_pos); assert b.states[prev_pos] in AnnotatedReachables(cspec); assert b.states[prev_pos+1] in AnnotatedReachables(cspec); } } /////////////////////////////////////////// // GENERAL CRASH LEMMAS /////////////////////////////////////////// lemma lemma_StateNextSeqPreservesCrash( rr:RefinementViaReductionRequest, states:seq, trace:seq ) requires ValidRefinementViaReductionRequest(rr) requires StateNextSeq(states, trace, rr.lspec.next) requires rr.crashed(states[0]) ensures last(states) == states[0] { if |states| > 1 { var zero := 0; assert ActionTuple(states[zero], states[zero+1], trace[zero]) in rr.lspec.next; assert rr.crashed(states[zero+1]); assert states[zero+1] == states[zero]; var states' := states[1..]; var trace' := trace[1..]; forall i {:trigger ActionTuple(states'[i], states'[i+1], trace'[i]) in rr.lspec.next} | 0 <= i < |trace'| ensures ActionTuple(states'[i], states'[i+1], trace'[i]) in rr.lspec.next { var iplus := i+1; assert states'[i] == states[iplus]; assert states'[i+1] == states[iplus+1]; assert trace'[i] == trace[iplus]; assert ActionTuple(states[iplus], states[iplus+1], trace[iplus]) in rr.lspec.next; } lemma_StateNextSeqPreservesCrash(rr, states', trace'); } } lemma lemma_CSpecNextPreservesCrash( rr:RefinementViaReductionRequest, s:State, s':State, step:CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires ActionTuple(s, s', step) in GetCrossLevelSpec(rr).next requires rr.crashed(s) ensures s' == s { if step.Reducible? { lemma_StateNextSeqPreservesCrash(rr, step.states, step.trace); } } lemma lemma_AllStatesFollowingCrashedStateSame( rr:RefinementViaReductionRequest, states:seq, trace:seq, pos:int ) requires ValidRefinementViaReductionRequest(rr) requires StateNextSeq(states, trace, rr.lspec.next) requires 0 <= pos < |states| requires rr.crashed(states[pos]) ensures forall i :: pos <= i < |states| ==> states[i] == states[pos] decreases |states|-pos { if pos < |states|-1 { assert ActionTuple(states[pos], states[pos+1], trace[pos]) in rr.lspec.next; assert rr.crashed(states[pos+1]); assert states[pos+1] == states[pos]; lemma_AllStatesFollowingCrashedStateSame(rr, states, trace, pos+1); } } lemma lemma_AllCSpecStatesFollowingCrashedStateSame( rr:RefinementViaReductionRequest, states:seq, trace:seq>, pos:int ) requires ValidRefinementViaReductionRequest(rr) requires StateNextSeq(states, trace, GetCrossLevelSpec(rr).next) requires 0 <= pos < |states| requires rr.crashed(states[pos]) ensures forall i :: pos <= i < |states| ==> states[i] == states[pos] decreases |states|-pos { var cspec := GetCrossLevelSpec(rr); if pos < |states|-1 { lemma_CSpecNextPreservesCrash(rr, states[pos], states[pos+1], trace[pos]); assert rr.crashed(states[pos+1]); lemma_AllCSpecStatesFollowingCrashedStateSame(rr, states, trace, pos+1); } } lemma lemma_CSpecNextPreservesNoncrashBackward( rr:RefinementViaReductionRequest, s:State, s':State, step:CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires ActionTuple(s, s', step) in GetCrossLevelSpec(rr).next requires !rr.crashed(s') ensures !rr.crashed(s) { if rr.crashed(s) { lemma_CSpecNextPreservesCrash(rr, s, s', step); assert false; } } lemma lemma_IfStateNextSeqDoesntCrashNoStateDoes( rr:RefinementViaReductionRequest, states:seq, trace:seq, pos:int ) requires ValidRefinementViaReductionRequest(rr) requires StateNextSeq(states, trace, rr.lspec.next) requires !rr.crashed(last(states)) requires 0 <= pos < |states| ensures !rr.crashed(states[pos]) { if pos < |states|-1 { var penult := |states|-2; assert ActionTuple(states[penult], states[penult+1], trace[penult]) in rr.lspec.next; assert !rr.crashed(states[penult]); lemma_LastOfAllButLast(states); lemma_IfStateNextSeqDoesntCrashNoStateDoes(rr, all_but_last(states), all_but_last(trace), pos); } } /////////////////////////////////////////// // CohenLamportReductionRequest /////////////////////////////////////////// function GetCohenLamportReductionIdmap( rr:RefinementViaReductionRequest ) : CrossLevelStep->Actor { (s:CrossLevelStep) => s.actor } function GetCohenLamportReductionRequest( rr:RefinementViaReductionRequest ) : CohenLamportReductionRequest> { CohenLamportReductionRequest(GetCrossLevelSpec(rr), rr.relation, GetCohenLamportReductionIdmap(rr), rr.phase1, rr.phase2, rr.crashed) } ///////////////////////////////////////////////////////////////// // Proof that CohenLamportReductionRequest is valid ///////////////////////////////////////////////////////////////// ///////////////////////// // UTILITY LEMMAS ///////////////////////// predicate PhasesMatch( rr:RefinementViaReductionRequest, s1:State, s2:State, actor:Actor ) { && (rr.crashed(s1) <==> rr.crashed(s2)) && (rr.phase1(s1, actor) <==> rr.phase1(s2, actor)) && (rr.phase2(s1, actor) <==> rr.phase2(s2, actor)) } lemma lemma_ReducibleStepStartsAndEndsOutOfPhase( rr:RefinementViaReductionRequest, s:State, s':State, step:CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires CrossLevelNext(rr, s, s', step) requires step.Reducible? ensures !rr.phase1(s, step.actor) ensures !rr.phase2(s, step.actor) ensures !rr.crashed(s') ==> !rr.phase1(s', step.actor) && !rr.phase2(s', step.actor) { } lemma lemma_StateNextSeqByOtherActorCausesPhasesMatch( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, states:seq, trace:seq, pos:int, step_actor:Actor, other_actor:Actor ) requires ValidRefinementViaReductionRequest(rr) requires StateNextSeq(states, trace, rr.lspec.next) requires forall step :: step in trace ==> rr.idmap(step) == step_actor requires step_actor != other_actor requires 0 <= pos < |states| requires !rr.crashed(states[pos]) ensures PhasesMatch(rr, states[0], states[pos], other_actor) { if pos > 0 { var prev := pos-1; assert ActionTuple(states[prev], states[prev+1], trace[prev]) in rr.lspec.next; assert rr.idmap(trace[prev]) == step_actor; assert PhasesMatch(rr, states[prev], states[prev+1], other_actor); lemma_StateNextSeqByOtherActorCausesPhasesMatch(rr, clrr, states, trace, pos-1, step_actor, other_actor); } } ///////////////////////////////////// // PhaseUnaffectedByOtherActors ///////////////////////////////////// lemma lemma_DemonstratePhaseUnaffectedByOtherActors( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, s:State, s':State, step:CrossLevelStep, actor:Actor ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires ActionTuple(s, s', step) in clrr.spec.next requires clrr.idmap(step) != actor ensures clrr.phase1(s, actor) <==> clrr.phase1(s', actor) ensures clrr.phase2(s, actor) <==> clrr.phase2(s', actor) { if step.Low? { return; } var pos := 0; while pos < |step.trace| invariant 0 <= pos <= |step.trace| invariant clrr.phase1(s, actor) <==> clrr.phase1(step.states[pos], actor) invariant clrr.phase2(s, actor) <==> clrr.phase2(step.states[pos], actor) { assert ActionTuple(step.states[pos], step.states[pos+1], step.trace[pos]) in rr.lspec.next; pos := pos + 1; } } lemma lemma_PhaseUnaffectedByOtherActors( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) ensures CohenLamportReductionSpecModule.PhaseUnaffectedByOtherActors(clrr) { forall s, s', step, actor | ActionTuple(s, s', step) in clrr.spec.next && clrr.idmap(step) != actor ensures clrr.phase1(s, actor) <==> clrr.phase1(s', actor) { lemma_DemonstratePhaseUnaffectedByOtherActors(rr, clrr, s, s', step, actor); } forall s, s', step, actor | ActionTuple(s, s', step) in clrr.spec.next && clrr.idmap(step) != actor ensures clrr.phase2(s, actor) <==> clrr.phase2(s', actor) { lemma_DemonstratePhaseUnaffectedByOtherActors(rr, clrr, s, s', step, actor); } } ///////////////////////////////////// // ActionSequencesLiftable ///////////////////////////////////// lemma lemma_DemonstrateActionSequencesLiftable( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, states:seq, trace:seq>, actor:Actor ) returns ( hstep:CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires StateNextSeq(states, trace, clrr.spec.next) requires forall step :: step in trace ==> clrr.idmap(step) == actor requires |states| > 1 requires !clrr.crashed(states[0]) requires !clrr.phase1(states[0], actor) requires !clrr.phase2(states[0], actor) requires var s := last(states); !clrr.crashed(s) ==> !clrr.phase1(s, actor) && !clrr.phase2(s, actor) requires forall i :: 0 < i < |trace| ==> clrr.phase1(states[i], actor) || clrr.phase2(states[i], actor) ensures ActionTuple(states[0], last(states), hstep) in clrr.spec.next ensures clrr.idmap(hstep) == actor { var zero := 0; assert ActionTuple(states[zero], states[zero+1], trace[zero]) in clrr.spec.next; if |trace| == 1 { hstep := trace[0]; return; } if rr.crashed(states[1]) { lemma_AllCSpecStatesFollowingCrashedStateSame(rr, states, trace, 1); assert states[1] == last(states); hstep := trace[0]; return; } forall i | 0 <= i < |trace| ensures trace[i].Low? ensures rr.idmap(trace[i].step) == actor ensures ActionTuple(states[i], states[i+1], trace[i].step) in rr.lspec.next ensures !rr.crashed(states[i]) { assert ActionTuple(states[i], states[i+1], trace[i]) in clrr.spec.next; assert clrr.idmap(trace[i]) == actor; assert trace[i].actor == actor; assert !rr.crashed(states[i]); if trace[i].Reducible? { assert CrossLevelNext(rr, states[i], states[i+1], trace[i]); assert && ValidReducibleCrossLevelStep(rr, trace[i].states, trace[i].trace, trace[i].actor) && trace[i].states[0] == states[i] && last(trace[i].states) == states[i+1]; if i == 0 { var iplus := i+1; assert 0 < iplus < |trace|; var s' := states[iplus]; assert clrr.phase1(s', actor) || clrr.phase2(s', actor); assert var s := last(trace[i].states); !rr.crashed(s) ==> !rr.phase1(s, actor) && !rr.phase2(s, actor); assert last(trace[i].states) == states[iplus] == states[1]; assert !rr.crashed(states[1]); assert false; } else { assert 0 < i < |trace|; var s := states[i]; assert clrr.phase1(s, actor) || clrr.phase2(s, actor); assert var s := trace[i].states[0]; !rr.crashed(s) && !rr.phase1(s, actor) && !rr.phase2(s, actor); assert false; } } assert trace[i].Low?; } var random_step:LStep :| true; var htrace := MapSeqToSeq(trace, (s:CrossLevelStep) => if s.Low? then s.step else random_step); hstep := Reducible(actor, states, htrace); forall step | step in htrace ensures rr.idmap(step) == actor { var s :| s in trace && step == s.step; assert clrr.idmap(s) == actor; assert rr.idmap(step) == actor; } assert ValidReducibleCrossLevelStep(rr, states, htrace, actor); } lemma lemma_ActionSequencesLiftable( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) ensures CohenLamportReductionSpecModule.ActionSequencesLiftable(clrr) { forall states, trace, actor {:trigger CohenLamportReductionSpecModule.ActionSequencesLiftableConditions(clrr, states, trace, actor)} | CohenLamportReductionSpecModule.ActionSequencesLiftableConditions(clrr, states, trace, actor) ensures exists hstep :: ActionTuple(states[0], last(states), hstep) in clrr.spec.next && clrr.idmap(hstep) == actor { var hstep := lemma_DemonstrateActionSequencesLiftable(rr, clrr, states, trace, actor); } } ///////////////////////////////////// // RightMoversCommute ///////////////////////////////////// lemma lemma_DemonstrateRightMoverCommutesCaseOneStep( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:LStep, other_step:LStep ) returns ( new_middle_state:State, other_step':LStep, right_mover':LStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in rr.lspec.next requires ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in rr.lspec.next requires rr.idmap(right_mover) != rr.idmap(other_step) requires rr.phase1(state_after_right_mover, rr.idmap(right_mover)) requires !rr.crashed(state_after_both_steps) ensures ActionTuple(initial_state, new_middle_state, other_step') in rr.lspec.next ensures ActionTuple(new_middle_state, state_after_both_steps, right_mover') in rr.lspec.next ensures rr.idmap(other_step') == rr.idmap(other_step) ensures rr.idmap(right_mover') == rr.idmap(right_mover) ensures PhasesMatch(rr, initial_state, state_after_right_mover, rr.idmap(other_step)) ensures PhasesMatch(rr, new_middle_state, state_after_both_steps, rr.idmap(other_step)) ensures PhasesMatch(rr, initial_state, new_middle_state, rr.idmap(right_mover)) ensures PhasesMatch(rr, state_after_right_mover, state_after_both_steps, rr.idmap(right_mover)) { assert RefinementViaReductionSpecModule.RightMoversCommuteConditions(rr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step); new_middle_state, other_step', right_mover' :| && ActionTuple(initial_state, new_middle_state, other_step') in rr.lspec.next && ActionTuple(new_middle_state, state_after_both_steps, right_mover') in rr.lspec.next && rr.idmap(other_step') == rr.idmap(other_step) && rr.idmap(right_mover') == rr.idmap(right_mover); } lemma lemma_DemonstrateRightMoverCommutesCaseMultipleSteps( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:LStep, other_states:seq, other_steps:seq, other_actor:Actor ) returns ( new_middle_state:State, other_states':seq, other_steps':seq, right_mover':LStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in rr.lspec.next requires StateNextSeq(other_states, other_steps, rr.lspec.next) requires forall step :: step in other_steps ==> rr.idmap(step) == other_actor requires other_states[0] == state_after_right_mover requires last(other_states) == state_after_both_steps requires rr.idmap(right_mover) != other_actor requires rr.phase1(state_after_right_mover, rr.idmap(right_mover)) requires !rr.crashed(state_after_both_steps) ensures StateNextSeq(other_states', other_steps', rr.lspec.next) ensures other_states'[0] == initial_state ensures last(other_states') == new_middle_state ensures forall step :: step in other_steps' ==> rr.idmap(step) == other_actor ensures ActionTuple(new_middle_state, state_after_both_steps, right_mover') in rr.lspec.next ensures rr.idmap(right_mover') == rr.idmap(right_mover) ensures PhasesMatch(rr, initial_state, new_middle_state, rr.idmap(right_mover)) ensures PhasesMatch(rr, state_after_right_mover, state_after_both_steps, rr.idmap(right_mover)) ensures |other_states'| == |other_states| ensures forall i :: 0 <= i < |other_states| ==> PhasesMatch(rr, other_states[i], other_states'[i], other_actor) { if |other_steps| == 0 { new_middle_state := initial_state; other_states' := [initial_state]; other_steps' := []; right_mover' := right_mover; return; } var zero := 0; assert ActionTuple(other_states[zero], other_states[zero+1], other_steps[zero]) in rr.lspec.next; lemma_IfStateNextSeqDoesntCrashNoStateDoes(rr, other_states, other_steps, zero+1); var new_middle_state_mid, other_step_mid, right_mover_mid := lemma_DemonstrateRightMoverCommutesCaseOneStep( rr, clrr, initial_state, state_after_right_mover, other_states[zero+1], right_mover, other_steps[zero]); lemma_NextMaintainsAnnotatedReachables(initial_state, new_middle_state_mid, other_step_mid, rr.lspec); lemma_ReduceStateNextSeqLeft(other_states, other_steps, rr.lspec.next); var other_states_next, other_steps_next; lemma_LastOfDropIsLast(other_states, 1); forall step | step in other_steps[1..] ensures rr.idmap(step) == other_actor { assert step in other_steps; } new_middle_state, other_states_next, other_steps_next, right_mover' := lemma_DemonstrateRightMoverCommutesCaseMultipleSteps( rr, clrr, new_middle_state_mid, other_states[zero+1], state_after_both_steps, right_mover_mid, other_states[1..], other_steps[1..], other_actor); other_states' := [initial_state] + other_states_next; other_steps' := [other_step_mid] + other_steps_next; lemma_ExtendStateNextSeqLeft(other_states_next, other_steps_next, rr.lspec.next, initial_state, other_step_mid); lemma_LastOfConcatenationIsLastOfLatter([initial_state], other_states_next); forall i | 0 <= i < |other_states| ensures PhasesMatch(rr, other_states[i], other_states'[i], other_actor) { if i == 0 { assert other_states[i] == state_after_right_mover; assert other_states'[i] == initial_state; assert PhasesMatch(rr, other_states[i], other_states'[i], other_actor); } else { var iminus := i-1; lemma_IndexIntoConcatenation([initial_state], other_states_next, i); assert other_states'[i] == other_states_next[i-1]; calc { other_states[i]; { assert i == 1+(i-1); } other_states[1+(i-1)]; { lemma_IndexIntoDrop(other_states, 1, i-1); } other_states[1..][i-1]; } assert PhasesMatch(rr, other_states_next[iminus], other_states[1..][iminus], other_actor); } } forall step | step in other_steps' ensures rr.idmap(step) == other_actor { if step in other_steps_next { assert rr.idmap(step) == other_actor; } else { assert step == other_step_mid; } } } lemma lemma_DemonstrateRightMoverCommutesCaseLow( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:CrossLevelStep, other_step:CrossLevelStep ) returns ( new_middle_state:State, other_step':CrossLevelStep, right_mover':CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next requires ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next requires clrr.idmap(right_mover) != clrr.idmap(other_step) requires clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) requires !clrr.crashed(state_after_both_steps) requires right_mover.Low? requires other_step.Low? ensures ActionTuple(initial_state, new_middle_state, other_step') in clrr.spec.next ensures ActionTuple(new_middle_state, state_after_both_steps, right_mover') in clrr.spec.next ensures clrr.idmap(other_step') == clrr.idmap(other_step) ensures clrr.idmap(right_mover') == clrr.idmap(right_mover) { var other_step_single, right_mover_single; new_middle_state, other_step_single, right_mover_single := lemma_DemonstrateRightMoverCommutesCaseOneStep( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover.step, other_step.step); other_step' := Low(rr.idmap(other_step_single), other_step_single); right_mover' := Low(rr.idmap(right_mover_single), right_mover_single); } lemma lemma_DemonstrateRightMoverCommutesCaseReducible( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:CrossLevelStep, other_step:CrossLevelStep ) returns ( new_middle_state:State, other_step':CrossLevelStep, right_mover':CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next requires ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next requires clrr.idmap(right_mover) != clrr.idmap(other_step) requires clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) requires !clrr.crashed(state_after_both_steps) requires right_mover.Low? requires other_step.Reducible? ensures ActionTuple(initial_state, new_middle_state, other_step') in clrr.spec.next ensures ActionTuple(new_middle_state, state_after_both_steps, right_mover') in clrr.spec.next ensures clrr.idmap(other_step') == clrr.idmap(other_step) ensures clrr.idmap(right_mover') == clrr.idmap(right_mover) { var other_states', other_steps', right_mover_single; new_middle_state, other_states', other_steps', right_mover_single := lemma_DemonstrateRightMoverCommutesCaseMultipleSteps( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover.step, other_step.states, other_step.trace, other_step.actor); other_step' := Reducible(other_step.actor, other_states', other_steps'); right_mover' := Low(rr.idmap(right_mover_single), right_mover_single); assert ValidReducibleCrossLevelStep(rr, other_states', other_steps', other_step.actor); assert CrossLevelNext(rr, initial_state, new_middle_state, other_step'); assert CrossLevelNext(rr, new_middle_state, state_after_both_steps, right_mover'); } lemma lemma_DemonstrateRightMoverCommutes( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:CrossLevelStep, other_step:CrossLevelStep ) returns ( new_middle_state:State, other_step':CrossLevelStep, right_mover':CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(clrr.spec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next requires ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next requires clrr.idmap(right_mover) != clrr.idmap(other_step) requires clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) requires !clrr.crashed(state_after_both_steps) ensures ActionTuple(initial_state, new_middle_state, other_step') in clrr.spec.next ensures ActionTuple(new_middle_state, state_after_both_steps, right_mover') in clrr.spec.next ensures clrr.idmap(other_step') == clrr.idmap(other_step) ensures clrr.idmap(right_mover') == clrr.idmap(right_mover) { lemma_AnnotatedReachablesOfCrossLevelSpec(rr); assert initial_state in AnnotatedReachables(rr.lspec); if right_mover.Reducible? { lemma_ReducibleStepStartsAndEndsOutOfPhase(rr, initial_state, state_after_right_mover, right_mover); assert false; } if other_step.Low? { new_middle_state, other_step', right_mover' := lemma_DemonstrateRightMoverCommutesCaseLow( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step); } else { new_middle_state, other_step', right_mover' := lemma_DemonstrateRightMoverCommutesCaseReducible( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step); } } lemma lemma_RightMoversCommute( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) ensures CohenLamportReductionSpecModule.RightMoversCommute(clrr) { forall initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step {:trigger CohenLamportReductionSpecModule.RightMoversCommuteConditions(clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step)} | CohenLamportReductionSpecModule.RightMoversCommuteConditions(clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step) && initial_state in AnnotatedReachables(clrr.spec) && ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next && ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next && clrr.idmap(right_mover) != clrr.idmap(other_step) && clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) && !clrr.crashed(state_after_both_steps) ensures exists new_middle_state, other_step', right_mover' :: && ActionTuple(initial_state, new_middle_state, other_step') in clrr.spec.next && ActionTuple(new_middle_state, state_after_both_steps, right_mover') in clrr.spec.next && clrr.idmap(other_step') == clrr.idmap(other_step) && clrr.idmap(right_mover') == clrr.idmap(right_mover) { var new_middle_state, other_step', right_mover' := lemma_DemonstrateRightMoverCommutes( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step); } } ///////////////////////////////////// // LeftMoversCommute ///////////////////////////////////// lemma lemma_DemonstrateLeftMoverCommutesCaseOneStep( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:LStep, left_mover:LStep ) returns ( new_middle_state:State, left_mover':LStep, other_step':LStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_other_step, other_step) in rr.lspec.next requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in rr.lspec.next requires rr.idmap(other_step) != rr.idmap(left_mover) requires rr.phase2(state_after_other_step, rr.idmap(left_mover)) requires !rr.crashed(state_after_both_steps) ensures ActionTuple(initial_state, new_middle_state, left_mover') in rr.lspec.next ensures ActionTuple(new_middle_state, state_after_both_steps, other_step') in rr.lspec.next ensures rr.idmap(left_mover') == rr.idmap(left_mover) ensures rr.idmap(other_step') == rr.idmap(other_step) ensures PhasesMatch(rr, initial_state, state_after_other_step, rr.idmap(left_mover)) ensures PhasesMatch(rr, new_middle_state, state_after_both_steps, rr.idmap(left_mover)) ensures PhasesMatch(rr, initial_state, new_middle_state, rr.idmap(other_step)) ensures PhasesMatch(rr, state_after_other_step, state_after_both_steps, rr.idmap(other_step)) { assert RefinementViaReductionSpecModule.LeftMoversCommuteConditions(rr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover); new_middle_state, left_mover', other_step' :| && ActionTuple(initial_state, new_middle_state, left_mover') in rr.lspec.next && ActionTuple(new_middle_state, state_after_both_steps, other_step') in rr.lspec.next && rr.idmap(left_mover') == rr.idmap(left_mover) && rr.idmap(other_step') == rr.idmap(other_step); } lemma lemma_DemonstrateLeftMoverCommutesCaseMultipleSteps( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_states:seq, other_steps:seq, other_actor:Actor, left_mover:LStep ) returns ( new_middle_state:State, left_mover':LStep, other_states':seq, other_steps':seq ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires StateNextSeq(other_states, other_steps, rr.lspec.next) requires forall step :: step in other_steps ==> rr.idmap(step) == other_actor requires other_states[0] == initial_state requires last(other_states) == state_after_other_step requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in rr.lspec.next requires rr.idmap(left_mover) != other_actor requires rr.phase2(state_after_other_step, rr.idmap(left_mover)) requires !rr.crashed(state_after_both_steps) ensures ActionTuple(initial_state, new_middle_state, left_mover') in rr.lspec.next ensures StateNextSeq(other_states', other_steps', rr.lspec.next) ensures other_states'[0] == new_middle_state ensures last(other_states') == state_after_both_steps ensures forall step :: step in other_steps' ==> rr.idmap(step) == other_actor ensures rr.idmap(left_mover') == rr.idmap(left_mover) ensures PhasesMatch(rr, initial_state, state_after_other_step, rr.idmap(left_mover)) ensures PhasesMatch(rr, new_middle_state, state_after_both_steps, rr.idmap(left_mover)) ensures |other_states'| == |other_states| ensures forall i :: 0 <= i < |other_states| ==> PhasesMatch(rr, other_states[i], other_states'[i], other_actor) decreases |other_states| { if |other_steps| == 0 { new_middle_state := state_after_both_steps; left_mover' := left_mover; other_states' := [state_after_both_steps]; other_steps' := []; return; } var penult := |other_states|-2; assert ActionTuple(other_states[penult], other_states[penult+1], other_steps[penult]) in rr.lspec.next; lemma_StateNextSeqMaintainsAnnotatedReachables(other_states, other_steps, rr.lspec); assert other_states[penult] in other_states; assert other_states[penult] in AnnotatedReachables(rr.lspec); calc { other_states[penult+1]; { assert penult+1 == |other_states|-1; } last(other_states); state_after_other_step; } var new_middle_state_mid, left_mover_mid, other_step_mid := lemma_DemonstrateLeftMoverCommutesCaseOneStep( rr, clrr, other_states[penult], other_states[penult+1], state_after_both_steps, other_steps[penult], left_mover); lemma_ReduceStateNextSeqRight(other_states, other_steps, rr.lspec.next); forall step | step in all_but_last(other_steps) ensures rr.idmap(step) == other_actor { assert step in other_steps; } lemma_LastOfAllButLast(other_states); var other_states_next, other_steps_next; new_middle_state, left_mover', other_states_next, other_steps_next := lemma_DemonstrateLeftMoverCommutesCaseMultipleSteps( rr, clrr, initial_state, other_states[penult], new_middle_state_mid, all_but_last(other_states), all_but_last(other_steps), other_actor, left_mover_mid); other_states' := other_states_next + [state_after_both_steps]; other_steps' := other_steps_next + [other_step_mid]; lemma_ExtendStateNextSeqRight(other_states_next, other_steps_next, rr.lspec.next, state_after_both_steps, other_step_mid); lemma_IndexIntoConcatenation(other_states_next, [state_after_both_steps], |other_states'|-1); forall step | step in other_steps' ensures rr.idmap(step) == other_actor { if step in other_steps_next { assert rr.idmap(step) == other_actor; } else { assert step == other_step_mid; } } forall i | 0 <= i < |other_states| ensures PhasesMatch(rr, other_states[i], other_states'[i], other_actor) { if i < |other_states_next| { assert other_states[i] == all_but_last(other_states)[i]; assert other_states'[i] == other_states_next[i]; assert PhasesMatch(rr, all_but_last(other_states)[i], other_states_next[i], other_actor); } else { assert |other_states_next| == |all_but_last(other_states)| == |other_states|-1; assert i == |other_states|-1; assert other_states[i] == last(other_states) == state_after_other_step; assert other_states'[i] == state_after_both_steps; assert PhasesMatch(rr, state_after_other_step, state_after_both_steps, other_actor); } } } lemma lemma_DemonstrateLeftMoverCommutesCaseLow( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:CrossLevelStep, left_mover:CrossLevelStep ) returns ( new_middle_state:State, left_mover':CrossLevelStep, other_step':CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next requires clrr.idmap(other_step) != clrr.idmap(left_mover) requires clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) requires !clrr.crashed(state_after_both_steps) requires other_step.Low? requires left_mover.Low? ensures ActionTuple(initial_state, new_middle_state, left_mover') in clrr.spec.next ensures ActionTuple(new_middle_state, state_after_both_steps, other_step') in clrr.spec.next ensures clrr.idmap(left_mover') == clrr.idmap(left_mover) ensures clrr.idmap(other_step') == clrr.idmap(other_step) { var left_mover_single, other_step_single; new_middle_state, left_mover_single, other_step_single := lemma_DemonstrateLeftMoverCommutesCaseOneStep( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step.step, left_mover.step); left_mover' := Low(rr.idmap(left_mover_single), left_mover_single); other_step' := Low(rr.idmap(other_step_single), other_step_single); } lemma lemma_DemonstrateLeftMoverCommutesCaseReducible( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:CrossLevelStep, left_mover:CrossLevelStep ) returns ( new_middle_state:State, left_mover':CrossLevelStep, other_step':CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next requires clrr.idmap(other_step) != clrr.idmap(left_mover) requires clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) requires !clrr.crashed(state_after_both_steps) requires other_step.Reducible? requires left_mover.Low? ensures ActionTuple(initial_state, new_middle_state, left_mover') in clrr.spec.next ensures ActionTuple(new_middle_state, state_after_both_steps, other_step') in clrr.spec.next ensures clrr.idmap(left_mover') == clrr.idmap(left_mover) ensures clrr.idmap(other_step') == clrr.idmap(other_step) { var left_mover_single, other_states', other_steps'; new_middle_state, left_mover_single, other_states', other_steps' := lemma_DemonstrateLeftMoverCommutesCaseMultipleSteps( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step.states, other_step.trace, other_step.actor, left_mover.step); left_mover' := Low(rr.idmap(left_mover_single), left_mover_single); other_step' := Reducible(other_step.actor, other_states', other_steps'); assert ValidReducibleCrossLevelStep(rr, other_states', other_steps', other_step.actor); assert CrossLevelNext(rr, new_middle_state, state_after_both_steps, other_step'); assert CrossLevelNext(rr, initial_state, new_middle_state, left_mover'); } lemma lemma_DemonstrateLeftMoverCommutes( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:CrossLevelStep, left_mover:CrossLevelStep ) returns ( new_middle_state:State, left_mover':CrossLevelStep, other_step':CrossLevelStep ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(clrr.spec) requires ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next requires clrr.idmap(other_step) != clrr.idmap(left_mover) requires clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) requires !clrr.crashed(state_after_both_steps) ensures ActionTuple(initial_state, new_middle_state, left_mover') in clrr.spec.next ensures ActionTuple(new_middle_state, state_after_both_steps, other_step') in clrr.spec.next ensures clrr.idmap(left_mover') == clrr.idmap(left_mover) ensures clrr.idmap(other_step') == clrr.idmap(other_step) { lemma_AnnotatedReachablesOfCrossLevelSpec(rr); assert initial_state in AnnotatedReachables(rr.lspec); if left_mover.Reducible? { lemma_ReducibleStepStartsAndEndsOutOfPhase(rr, state_after_other_step, state_after_both_steps, left_mover); assert false; } if other_step.Low? { new_middle_state, left_mover', other_step' := lemma_DemonstrateLeftMoverCommutesCaseLow( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover); } else { new_middle_state, left_mover', other_step' := lemma_DemonstrateLeftMoverCommutesCaseReducible( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover); } } lemma lemma_LeftMoversCommute( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) ensures CohenLamportReductionSpecModule.LeftMoversCommute(clrr) { forall initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover {:trigger CohenLamportReductionSpecModule.LeftMoversCommuteConditions(clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover)} | CohenLamportReductionSpecModule.LeftMoversCommuteConditions(clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover) && initial_state in AnnotatedReachables(clrr.spec) && ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next && ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next && clrr.idmap(other_step) != clrr.idmap(left_mover) && clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) && !clrr.crashed(state_after_both_steps) ensures exists new_middle_state, left_mover', other_step' :: && ActionTuple(initial_state, new_middle_state, left_mover') in clrr.spec.next && ActionTuple(new_middle_state, state_after_both_steps, other_step') in clrr.spec.next && clrr.idmap(left_mover') == clrr.idmap(left_mover) && clrr.idmap(other_step') == clrr.idmap(other_step) { var new_middle_state, left_mover', other_step' := lemma_DemonstrateLeftMoverCommutes( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover); } } //////////////////////////////////////// // LeftMoversAlwaysEnabled //////////////////////////////////////// lemma lemma_DemonstrateLeftMoversAlwaysEnabled( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, s:State, actor:Actor ) returns ( states:seq, trace:seq> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires s in AnnotatedReachables(clrr.spec) requires clrr.phase2(s, actor) requires !clrr.crashed(s) ensures StateNextSeq(states, trace, clrr.spec.next) ensures states[0] == s ensures clrr.crashed(last(states)) || !clrr.phase2(last(states), actor) ensures forall i :: 0 <= i < |states|-1 ==> clrr.phase2(states[i], actor) ensures forall step :: step in trace ==> clrr.idmap(step) == actor { lemma_AnnotatedReachablesOfCrossLevelSpec(rr); assert RefinementViaReductionSpecModule.LeftMoversAlwaysEnabledConditions(rr, s, actor); var states_mid, trace_mid :| && StateNextSeq(states_mid, trace_mid, rr.lspec.next) && states_mid[0] == s && (rr.crashed(last(states_mid)) || !rr.phase2(last(states_mid), actor)) && (forall i :: 0 <= i < |states_mid|-1 ==> rr.phase2(states_mid[i], actor)) && (forall step :: step in trace_mid ==> rr.idmap(step) == actor); var pos := lemma_FindFirstCrashingState(rr, states_mid, trace_mid); if pos < |states_mid| { states := states_mid[..pos+1]; trace := MapSeqToSeq(trace_mid[..pos], x => Low(rr.idmap(x), x)); assert last(states) == states_mid[pos] == last(states_mid); } else { states := states_mid; trace := MapSeqToSeq(trace_mid, x => Low(rr.idmap(x), x)); } assert forall i :: 0 <= i < |trace| ==> trace[i] == Low(rr.idmap(trace_mid[i]), trace_mid[i]); assert forall i :: 0 <= i < |trace| ==> !rr.crashed(states[i]); assert last(states) == last(states_mid); forall i {:trigger ActionTuple(states[i], states[i+1], trace[i]) in clrr.spec.next} | 0 <= i < |trace| ensures ActionTuple(states[i], states[i+1], trace[i]) in clrr.spec.next { assert ActionTuple(states[i], states[i+1], trace_mid[i]) in rr.lspec.next; assert trace[i].Low?; assert !rr.crashed(states[i]); assert trace[i].actor == rr.idmap(trace[i].step); assert CrossLevelNext(rr, states[i], states[i+1], trace[i]); } } lemma lemma_LeftMoversAlwaysEnabled( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) ensures CohenLamportReductionSpecModule.LeftMoversAlwaysEnabled(clrr) { forall s, actor {:trigger CohenLamportReductionSpecModule.LeftMoversAlwaysEnabledConditions(clrr, s, actor)} | CohenLamportReductionSpecModule.LeftMoversAlwaysEnabledConditions(clrr, s, actor) ensures exists states, trace :: && StateNextSeq(states, trace, clrr.spec.next) && states[0] == s && (clrr.crashed(last(states)) || !clrr.phase2(last(states), actor)) && (forall i :: 0 <= i < |states|-1 ==> clrr.phase2(states[i], actor)) && (forall step :: step in trace ==> clrr.idmap(step) == actor) { var states, trace := lemma_DemonstrateLeftMoversAlwaysEnabled(rr, clrr, s, actor); } } //////////////////////////////////////// // LeftMoversEnabledBeforeCrash //////////////////////////////////////// lemma lemma_MoveMultipleLeftMoversAcrossMultipleSteps( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_states:seq, other_steps:seq, other_actor:Actor, left_mover_states:seq, left_mover_steps:seq, left_mover_actor:Actor ) returns ( new_middle_state:State, left_mover_states':seq, left_mover_steps':seq, other_states':seq, other_steps':seq ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires StateNextSeq(other_states, other_steps, rr.lspec.next) requires forall step :: step in other_steps ==> rr.idmap(step) == other_actor requires other_states[0] == initial_state requires last(other_states) == state_after_other_step requires StateNextSeq(left_mover_states, left_mover_steps, rr.lspec.next) requires left_mover_states[0] == state_after_other_step requires last(left_mover_states) == state_after_both_steps requires forall step :: step in left_mover_steps ==> rr.idmap(step) == left_mover_actor requires forall i :: 0 <= i < |left_mover_states|-1 ==> rr.phase2(left_mover_states[i], left_mover_actor) requires left_mover_actor != other_actor requires !rr.crashed(state_after_both_steps) ensures StateNextSeq(left_mover_states', left_mover_steps', rr.lspec.next) ensures left_mover_states'[0] == initial_state ensures last(left_mover_states') == new_middle_state ensures forall step :: step in left_mover_steps' ==> rr.idmap(step) == left_mover_actor ensures |left_mover_states'| == |left_mover_states| ensures forall i :: 0 <= i < |left_mover_states| ==> PhasesMatch(rr, left_mover_states[i], left_mover_states'[i], left_mover_actor) ensures StateNextSeq(other_states', other_steps', rr.lspec.next) ensures other_states'[0] == new_middle_state ensures last(other_states') == state_after_both_steps ensures forall step :: step in other_steps' ==> rr.idmap(step) == other_actor ensures |other_states'| == |other_states| ensures forall i :: 0 <= i < |other_states| ==> PhasesMatch(rr, other_states[i], other_states'[i], other_actor) decreases |left_mover_states| { if |left_mover_steps| == 0 { lemma_StateNextSeqByOtherActorCausesPhasesMatch(rr, clrr, other_states, other_steps, |other_steps|, other_actor, left_mover_actor); new_middle_state := initial_state; left_mover_states' := [initial_state]; left_mover_steps' := []; other_states' := other_states; other_steps' := other_steps; return; } var zero := 0; assert ActionTuple(left_mover_states[zero], left_mover_states[zero+1], left_mover_steps[zero]) in rr.lspec.next; lemma_IfStateNextSeqDoesntCrashNoStateDoes(rr, left_mover_states, left_mover_steps, zero+1); var new_middle_state_mid, left_mover_mid, other_states_mid, other_steps_mid := lemma_DemonstrateLeftMoverCommutesCaseMultipleSteps( rr, clrr, initial_state, state_after_other_step, left_mover_states[zero+1], other_states, other_steps, other_actor, left_mover_steps[zero]); lemma_ReduceStateNextSeqLeft(left_mover_states, left_mover_steps, rr.lspec.next); lemma_NextMaintainsAnnotatedReachables(initial_state, new_middle_state_mid, left_mover_mid, rr.lspec); var left_mover_states_next, left_mover_steps_next; new_middle_state, left_mover_states_next, left_mover_steps_next, other_states', other_steps' := lemma_MoveMultipleLeftMoversAcrossMultipleSteps( rr, clrr, new_middle_state_mid, left_mover_states[zero+1], last(left_mover_states), other_states_mid, other_steps_mid, other_actor, left_mover_states[1..], left_mover_steps[1..], left_mover_actor); left_mover_states' := [initial_state] + left_mover_states_next; left_mover_steps' := [left_mover_mid] + left_mover_steps_next; lemma_ExtendStateNextSeqLeft(left_mover_states_next, left_mover_steps_next, rr.lspec.next, initial_state, left_mover_mid); forall i | 0 <= i < |left_mover_states| ensures PhasesMatch(rr, left_mover_states[i], left_mover_states'[i], left_mover_actor) { if i > 0 { var iminus := i-1; assert 0 <= iminus < |left_mover_states[1..]|; lemma_IndexIntoConcatenation([initial_state], left_mover_states_next, i); assert left_mover_states'[i] == left_mover_states_next[iminus]; calc { left_mover_states[i]; { assert i == 1+iminus; } left_mover_states[1+iminus]; { lemma_IndexIntoDrop(left_mover_states, 1, iminus); } left_mover_states[1..][iminus]; } assert PhasesMatch(rr, left_mover_states[1..][iminus], left_mover_states_next[iminus], left_mover_actor); } else { assert left_mover_states'[i] == initial_state; lemma_StateNextSeqByOtherActorCausesPhasesMatch(rr, clrr, other_states, other_steps, |other_steps|, other_actor, left_mover_actor); } } } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseOneStep( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, post_crash_state:State, crash_step:LStep, left_mover_actor:Actor ) returns ( left_mover_states:seq, left_mover_steps:seq, crash_step':LStep, post_crash_state':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, post_crash_state, crash_step) in rr.lspec.next requires !clrr.crashed(initial_state) requires rr.crashed(post_crash_state) requires rr.idmap(crash_step) != left_mover_actor requires rr.phase2(initial_state, left_mover_actor) ensures StateNextSeq(left_mover_states, left_mover_steps, rr.lspec.next) ensures left_mover_states[0] == initial_state ensures !rr.crashed(last(left_mover_states)) ensures !rr.phase2(last(left_mover_states), left_mover_actor) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> rr.phase2(left_mover_states[i], left_mover_actor) ensures forall step :: step in left_mover_steps ==> rr.idmap(step) == left_mover_actor ensures ActionTuple(last(left_mover_states), post_crash_state', crash_step') in rr.lspec.next ensures rr.idmap(crash_step') == rr.idmap(crash_step) ensures rr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in rr.relation { assert RefinementViaReductionSpecModule.LeftMoversEnabledBeforeCrashConditions(rr, initial_state, post_crash_state, crash_step, left_mover_actor); left_mover_states, left_mover_steps, crash_step', post_crash_state' :| && StateNextSeq(left_mover_states, left_mover_steps, rr.lspec.next) && left_mover_states[0] == initial_state && !rr.crashed(last(left_mover_states)) && !rr.phase2(last(left_mover_states), left_mover_actor) && (forall i :: 0 <= i < |left_mover_states|-1 ==> rr.phase2(left_mover_states[i], left_mover_actor)) && (forall step :: step in left_mover_steps ==> rr.idmap(step) == left_mover_actor) && ActionTuple(last(left_mover_states), post_crash_state', crash_step') in rr.lspec.next && rr.idmap(crash_step') == rr.idmap(crash_step) && rr.crashed(post_crash_state') && RefinementPair(post_crash_state, post_crash_state') in rr.relation; } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseLow( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, post_crash_state:State, crash_step:CrossLevelStep, left_mover_actor:Actor ) returns ( left_mover_states:seq, left_mover_steps:seq>, crash_step':CrossLevelStep, post_crash_state':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, post_crash_state, crash_step) in clrr.spec.next requires !clrr.crashed(initial_state) requires clrr.crashed(post_crash_state) requires clrr.idmap(crash_step) != left_mover_actor requires clrr.phase2(initial_state, left_mover_actor) requires crash_step.Low? ensures StateNextSeq(left_mover_states, left_mover_steps, clrr.spec.next) ensures left_mover_states[0] == initial_state ensures !clrr.crashed(last(left_mover_states)) ensures !clrr.phase2(last(left_mover_states), left_mover_actor) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> clrr.phase2(left_mover_states[i], left_mover_actor) ensures forall step :: step in left_mover_steps ==> clrr.idmap(step) == left_mover_actor ensures ActionTuple(last(left_mover_states), post_crash_state', crash_step') in clrr.spec.next ensures clrr.idmap(crash_step') == clrr.idmap(crash_step) ensures clrr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in clrr.relation { var ltrace, crash_step_single; left_mover_states, ltrace, crash_step_single, post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseOneStep( rr, clrr, initial_state, post_crash_state, crash_step.step, left_mover_actor); crash_step' := Low(rr.idmap(crash_step_single), crash_step_single); left_mover_steps := lemma_LiftStateNextSeqToCrossLevel(rr, clrr, left_mover_states, ltrace); } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseMultipleSteps( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, post_crash_state:State, crash_states:seq, crash_steps:seq, crash_actor:Actor, left_mover_actor:Actor ) returns ( left_mover_states:seq, left_mover_steps:seq, crash_states':seq, crash_steps':seq, post_crash_state':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires StateNextSeq(crash_states, crash_steps, rr.lspec.next) requires crash_states[0] == initial_state requires last(crash_states) == post_crash_state requires forall step :: step in crash_steps ==> rr.idmap(step) == crash_actor requires !clrr.crashed(initial_state) requires clrr.crashed(post_crash_state) requires crash_actor != left_mover_actor requires rr.phase2(initial_state, left_mover_actor) ensures StateNextSeq(left_mover_states, left_mover_steps, rr.lspec.next) ensures left_mover_states[0] == initial_state ensures !rr.crashed(last(left_mover_states)) ensures !rr.phase2(last(left_mover_states), left_mover_actor) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> rr.phase2(left_mover_states[i], left_mover_actor) ensures forall step :: step in left_mover_steps ==> rr.idmap(step) == left_mover_actor ensures StateNextSeq(crash_states', crash_steps', rr.lspec.next) ensures crash_states'[0] == last(left_mover_states) ensures last(crash_states') == post_crash_state' ensures forall step :: step in crash_steps' ==> rr.idmap(step) == crash_actor ensures clrr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in clrr.relation ensures |crash_states'| <= |crash_states| ensures forall i :: 0 <= i < |crash_states'|-1 ==> PhasesMatch(rr, crash_states[i], crash_states'[i], crash_actor) { var pos := lemma_FindFirstCrashingState(rr, crash_states, crash_steps); assert 0 < pos < |crash_states|; var prev := pos-1; assert post_crash_state == last(crash_states) == crash_states[pos]; assert ActionTuple(crash_states[prev], crash_states[prev+1], crash_steps[prev]) in rr.lspec.next; lemma_StateNextSeqMaintainsAnnotatedReachables(crash_states, crash_steps, rr.lspec); assert crash_states[prev] in crash_states; assert crash_states[prev] in AnnotatedReachables(rr.lspec); lemma_StateNextSeqByOtherActorCausesPhasesMatch(rr, clrr, crash_states, crash_steps, prev, crash_actor, left_mover_actor); var left_mover_states_mid, left_mover_steps_mid, crash_step_mid; left_mover_states_mid, left_mover_steps_mid, crash_step_mid, post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseOneStep( rr, clrr, crash_states[prev], crash_states[prev+1], crash_steps[prev], left_mover_actor); lemma_TakeStateNextSeq(crash_states, crash_steps, rr.lspec.next, prev); var new_middle_state, other_states, other_steps; new_middle_state, left_mover_states, left_mover_steps, other_states, other_steps := lemma_MoveMultipleLeftMoversAcrossMultipleSteps( rr, clrr, initial_state, crash_states[prev], last(left_mover_states_mid), crash_states[..prev+1], crash_steps[..prev], crash_actor, left_mover_states_mid, left_mover_steps_mid, left_mover_actor); crash_states' := other_states + [post_crash_state']; crash_steps' := other_steps + [crash_step_mid]; lemma_ExtendStateNextSeqRight(other_states, other_steps, rr.lspec.next, post_crash_state', crash_step_mid); forall i | 0 <= i < |left_mover_states|-1 ensures rr.phase2(left_mover_states[i], left_mover_actor) { assert PhasesMatch(rr, left_mover_states_mid[i], left_mover_states[i], left_mover_actor); } forall i | 0 <= i < |crash_states'|-1 ensures PhasesMatch(rr, crash_states[i], crash_states'[i], crash_actor) { assert crash_states[i] == crash_states[..prev+1][i]; assert crash_states'[i] == other_states[i]; assert PhasesMatch(rr, crash_states[..prev+1][i], other_states[i], crash_actor); } } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseReducible( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, post_crash_state:State, crash_step:CrossLevelStep, left_mover_actor:Actor ) returns ( left_mover_states:seq, left_mover_steps:seq>, crash_step':CrossLevelStep, post_crash_state':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, post_crash_state, crash_step) in clrr.spec.next requires !clrr.crashed(initial_state) requires clrr.crashed(post_crash_state) requires clrr.idmap(crash_step) != left_mover_actor requires clrr.phase2(initial_state, left_mover_actor) requires crash_step.Reducible? ensures StateNextSeq(left_mover_states, left_mover_steps, clrr.spec.next) ensures left_mover_states[0] == initial_state ensures !clrr.crashed(last(left_mover_states)) ensures !clrr.phase2(last(left_mover_states), left_mover_actor) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> clrr.phase2(left_mover_states[i], left_mover_actor) ensures forall step :: step in left_mover_steps ==> clrr.idmap(step) == left_mover_actor ensures ActionTuple(last(left_mover_states), post_crash_state', crash_step') in clrr.spec.next ensures clrr.idmap(crash_step') == clrr.idmap(crash_step) ensures clrr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in clrr.relation { var ltrace, crash_states', crash_steps'; left_mover_states, ltrace, crash_states', crash_steps', post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseMultipleSteps( rr, clrr, initial_state, post_crash_state, crash_step.states, crash_step.trace, crash_step.actor, left_mover_actor); crash_step' := Reducible(crash_step.actor, crash_states', crash_steps'); left_mover_steps := lemma_LiftStateNextSeqToCrossLevel(rr, clrr, left_mover_states, ltrace); assert ValidReducibleCrossLevelStep(rr, crash_states', crash_steps', crash_step.actor); assert CrossLevelNext(rr, last(left_mover_states), post_crash_state', crash_step'); } lemma lemma_DemonstrateLeftMoversEnabledBeforeCrash( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, post_crash_state:State, crash_step:CrossLevelStep, left_mover_actor:Actor ) returns ( left_mover_states:seq, left_mover_steps:seq>, crash_step':CrossLevelStep, post_crash_state':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(clrr.spec) requires ActionTuple(initial_state, post_crash_state, crash_step) in clrr.spec.next requires !clrr.crashed(initial_state) requires clrr.crashed(post_crash_state) requires clrr.idmap(crash_step) != left_mover_actor requires clrr.phase2(initial_state, left_mover_actor) ensures StateNextSeq(left_mover_states, left_mover_steps, clrr.spec.next) ensures left_mover_states[0] == initial_state ensures !clrr.crashed(last(left_mover_states)) ensures !clrr.phase2(last(left_mover_states), left_mover_actor) ensures forall i :: 0 <= i < |left_mover_states|-1 ==> clrr.phase2(left_mover_states[i], left_mover_actor) ensures forall step :: step in left_mover_steps ==> clrr.idmap(step) == left_mover_actor ensures ActionTuple(last(left_mover_states), post_crash_state', crash_step') in clrr.spec.next ensures clrr.idmap(crash_step') == clrr.idmap(crash_step) ensures clrr.crashed(post_crash_state') ensures RefinementPair(post_crash_state, post_crash_state') in clrr.relation { lemma_AnnotatedReachablesOfCrossLevelSpec(rr); if crash_step.Low? { left_mover_states, left_mover_steps, crash_step', post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseLow(rr, clrr, initial_state, post_crash_state, crash_step, left_mover_actor); } else { left_mover_states, left_mover_steps, crash_step', post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrashCaseReducible(rr, clrr, initial_state, post_crash_state, crash_step, left_mover_actor); } } lemma lemma_LeftMoversEnabledBeforeCrash( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) ensures CohenLamportReductionSpecModule.LeftMoversEnabledBeforeCrash(clrr) { forall initial_state, post_crash_state, crash_step, actor {:trigger CohenLamportReductionSpecModule.LeftMoversEnabledBeforeCrashConditions(clrr, initial_state, post_crash_state, crash_step, actor)} | CohenLamportReductionSpecModule.LeftMoversEnabledBeforeCrashConditions(clrr, initial_state, post_crash_state, crash_step, actor) ensures exists states, trace, crash_step', post_crash_state' :: && StateNextSeq(states, trace, clrr.spec.next) && states[0] == initial_state && !clrr.crashed(last(states)) && !clrr.phase2(last(states), actor) && (forall i :: 0 <= i < |states|-1 ==> clrr.phase2(states[i], actor)) && (forall step :: step in trace ==> clrr.idmap(step) == actor) && ActionTuple(last(states), post_crash_state', crash_step') in clrr.spec.next && clrr.idmap(crash_step') == clrr.idmap(crash_step) && clrr.crashed(post_crash_state') && RefinementPair(post_crash_state, post_crash_state') in clrr.relation { var states, trace, crash_step', post_crash_state' := lemma_DemonstrateLeftMoversEnabledBeforeCrash(rr, clrr, initial_state, post_crash_state, crash_step, actor); } } //////////////////////////////////////// // RightMoverCrashPreservation //////////////////////////////////////// lemma lemma_DemonstrateRightMoverCrashPreservationCaseOneStep( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:LStep, other_step:LStep ) returns ( other_step':LStep, state_after_other_step':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in rr.lspec.next requires ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in rr.lspec.next requires !rr.crashed(initial_state) requires !rr.crashed(state_after_right_mover) requires rr.crashed(state_after_both_steps) requires rr.phase1(state_after_right_mover, rr.idmap(right_mover)) requires rr.idmap(right_mover) != rr.idmap(other_step) ensures rr.idmap(other_step') == rr.idmap(other_step) ensures ActionTuple(initial_state, state_after_other_step', other_step') in rr.lspec.next ensures rr.crashed(state_after_other_step') ensures RefinementPair(state_after_both_steps, state_after_other_step') in rr.relation { assert RefinementViaReductionSpecModule.RightMoverCrashPreservationConditions(rr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step); other_step', state_after_other_step' :| && rr.idmap(other_step') == rr.idmap(other_step) && ActionTuple(initial_state, state_after_other_step', other_step') in rr.lspec.next && rr.crashed(state_after_other_step') && RefinementPair(state_after_both_steps, state_after_other_step') in rr.relation; } lemma lemma_DemonstrateRightMoverCrashPreservationCaseMultipleSteps( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:LStep, other_states:seq, other_steps:seq, other_actor:Actor ) returns ( other_states':seq, other_steps':seq, state_after_other_step':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in rr.lspec.next requires StateNextSeq(other_states, other_steps, rr.lspec.next) requires other_states[0] == state_after_right_mover requires last(other_states) == state_after_both_steps requires forall step :: step in other_steps ==> rr.idmap(step) == other_actor requires !rr.crashed(initial_state) requires !rr.crashed(state_after_right_mover) requires rr.crashed(state_after_both_steps) requires rr.phase1(state_after_right_mover, rr.idmap(right_mover)) requires rr.idmap(right_mover) != other_actor ensures StateNextSeq(other_states', other_steps', rr.lspec.next) ensures other_states'[0] == initial_state ensures last(other_states') == state_after_other_step' ensures forall step :: step in other_steps' ==> rr.idmap(step) == other_actor ensures rr.crashed(state_after_other_step') ensures RefinementPair(state_after_both_steps, state_after_other_step') in rr.relation ensures |other_states'| <= |other_states| ensures forall i :: 0 <= i < |other_states'|-1 ==> PhasesMatch(rr, other_states[i], other_states'[i], other_actor) { var pos := lemma_FindFirstCrashingState(rr, other_states, other_steps); assert 0 < pos < |other_states|; var prev := pos-1; assert state_after_both_steps == last(other_states) == other_states[pos]; assert ActionTuple(other_states[prev], other_states[prev+1], other_steps[prev]) in rr.lspec.next; lemma_TakeStateNextSeq(other_states, other_steps, rr.lspec.next, prev); var new_middle_state, other_states_next, other_steps_next, right_mover' := lemma_DemonstrateRightMoverCommutesCaseMultipleSteps( rr, clrr, initial_state, state_after_right_mover, other_states[prev], right_mover, other_states[..prev+1], other_steps[..prev], other_actor); lemma_StateNextSeqMaintainsAnnotatedReachables(other_states_next, other_steps_next, rr.lspec); assert new_middle_state == last(other_states_next); assert new_middle_state in other_states_next; assert new_middle_state in AnnotatedReachables(rr.lspec); assert RefinementViaReductionSpecModule.RightMoverCrashPreservationConditions(rr, new_middle_state, other_states[prev], other_states[prev+1], right_mover', other_steps[prev]); var other_step'; other_step', state_after_other_step' :| && rr.idmap(other_step') == rr.idmap(other_steps[prev]) && ActionTuple(new_middle_state, state_after_other_step', other_step') in rr.lspec.next && rr.crashed(state_after_other_step') && RefinementPair(other_states[prev+1], state_after_other_step') in rr.relation; other_states' := other_states_next + [state_after_other_step']; other_steps' := other_steps_next + [other_step']; lemma_ExtendStateNextSeqRight(other_states_next, other_steps_next, rr.lspec.next, state_after_other_step', other_step'); } lemma lemma_DemonstrateRightMoverCrashPreservationCaseLow( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:CrossLevelStep, other_step:CrossLevelStep ) returns ( other_step':CrossLevelStep, state_after_other_step':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next requires ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next requires !clrr.crashed(initial_state) requires !clrr.crashed(state_after_right_mover) requires clrr.crashed(state_after_both_steps) requires clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) requires clrr.idmap(right_mover) != clrr.idmap(other_step) requires right_mover.Low? requires other_step.Low? ensures clrr.idmap(other_step') == clrr.idmap(other_step) ensures ActionTuple(initial_state, state_after_other_step', other_step') in clrr.spec.next ensures clrr.crashed(state_after_other_step') ensures RefinementPair(state_after_both_steps, state_after_other_step') in clrr.relation { var other_step_single; other_step_single, state_after_other_step' := lemma_DemonstrateRightMoverCrashPreservationCaseOneStep( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover.step, other_step.step); other_step' := Low(rr.idmap(other_step_single), other_step_single); } lemma lemma_DemonstrateRightMoverCrashPreservationCaseReducible( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:CrossLevelStep, other_step:CrossLevelStep ) returns ( other_step':CrossLevelStep, state_after_other_step':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next requires ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next requires !clrr.crashed(initial_state) requires !clrr.crashed(state_after_right_mover) requires clrr.crashed(state_after_both_steps) requires clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) requires clrr.idmap(right_mover) != clrr.idmap(other_step) requires right_mover.Low? requires other_step.Reducible? ensures clrr.idmap(other_step') == clrr.idmap(other_step) ensures ActionTuple(initial_state, state_after_other_step', other_step') in clrr.spec.next ensures clrr.crashed(state_after_other_step') ensures RefinementPair(state_after_both_steps, state_after_other_step') in clrr.relation { var other_states', other_steps'; other_states', other_steps', state_after_other_step' := lemma_DemonstrateRightMoverCrashPreservationCaseMultipleSteps( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover.step, other_step.states, other_step.trace, other_step.actor); other_step' := Reducible(other_step.actor, other_states', other_steps'); assert ValidReducibleCrossLevelStep(rr, other_states', other_steps', other_step.actor); assert CrossLevelNext(rr, initial_state, state_after_other_step', other_step'); } lemma lemma_DemonstrateRightMoverCrashPreservation( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:CrossLevelStep, other_step:CrossLevelStep ) returns ( other_step':CrossLevelStep, state_after_other_step':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(clrr.spec) requires ActionTuple(initial_state, state_after_right_mover, right_mover) in clrr.spec.next requires ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in clrr.spec.next requires !clrr.crashed(initial_state) requires !clrr.crashed(state_after_right_mover) requires clrr.crashed(state_after_both_steps) requires clrr.phase1(state_after_right_mover, clrr.idmap(right_mover)) requires clrr.idmap(right_mover) != clrr.idmap(other_step) ensures clrr.idmap(other_step') == clrr.idmap(other_step) ensures ActionTuple(initial_state, state_after_other_step', other_step') in clrr.spec.next ensures clrr.crashed(state_after_other_step') ensures RefinementPair(state_after_both_steps, state_after_other_step') in clrr.relation { lemma_AnnotatedReachablesOfCrossLevelSpec(rr); assert initial_state in AnnotatedReachables(rr.lspec); if right_mover.Reducible? { lemma_ReducibleStepStartsAndEndsOutOfPhase(rr, initial_state, state_after_right_mover, right_mover); assert false; } if other_step.Low? { other_step', state_after_other_step' := lemma_DemonstrateRightMoverCrashPreservationCaseLow( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step); } else { other_step', state_after_other_step' := lemma_DemonstrateRightMoverCrashPreservationCaseReducible( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step); } } lemma lemma_RightMoverCrashPreservation( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) ensures CohenLamportReductionSpecModule.RightMoverCrashPreservation(clrr) { forall initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step {:trigger CohenLamportReductionSpecModule.RightMoverCrashPreservationConditions(clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step)} | CohenLamportReductionSpecModule.RightMoverCrashPreservationConditions(clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step) ensures exists other_step', state_after_other_step' :: && clrr.idmap(other_step') == clrr.idmap(other_step) && ActionTuple(initial_state, state_after_other_step', other_step') in clrr.spec.next && clrr.crashed(state_after_other_step') && RefinementPair(state_after_both_steps, state_after_other_step') in clrr.relation { var other_step', state_after_other_step' := lemma_DemonstrateRightMoverCrashPreservation( rr, clrr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step); } } //////////////////////////////////////// // LeftMoverSelfCrashPreservation //////////////////////////////////////// lemma lemma_DemonstrateLeftMoverSelfCrashPreservationCaseOneStep( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:LStep, left_mover:LStep ) returns ( left_mover':LStep, state_after_left_mover':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_other_step, other_step) in rr.lspec.next requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in rr.lspec.next requires !rr.crashed(initial_state) requires !rr.crashed(state_after_other_step) requires rr.crashed(state_after_both_steps) requires rr.phase2(state_after_other_step, rr.idmap(left_mover)) requires rr.idmap(left_mover) != rr.idmap(other_step) ensures rr.idmap(left_mover') == rr.idmap(left_mover) ensures ActionTuple(initial_state, state_after_left_mover', left_mover') in rr.lspec.next ensures rr.crashed(state_after_left_mover') ensures RefinementPair(state_after_both_steps, state_after_left_mover') in rr.relation { assert RefinementViaReductionSpecModule.LeftMoverSelfCrashPreservationConditions(rr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover); left_mover', state_after_left_mover' :| && rr.idmap(left_mover') == rr.idmap(left_mover) && ActionTuple(initial_state, state_after_left_mover', left_mover') in rr.lspec.next && rr.crashed(state_after_left_mover') && RefinementPair(state_after_both_steps, state_after_left_mover') in rr.relation; } lemma lemma_DemonstrateLeftMoverSelfCrashPreservationCaseLow( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:CrossLevelStep, left_mover:CrossLevelStep ) returns ( left_mover':CrossLevelStep, state_after_left_mover':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next requires !clrr.crashed(initial_state) requires !clrr.crashed(state_after_other_step) requires clrr.crashed(state_after_both_steps) requires clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) requires clrr.idmap(left_mover) != clrr.idmap(other_step) requires other_step.Low? requires left_mover.Low? ensures clrr.idmap(left_mover') == clrr.idmap(left_mover) ensures ActionTuple(initial_state, state_after_left_mover', left_mover') in clrr.spec.next ensures clrr.crashed(state_after_left_mover') ensures RefinementPair(state_after_both_steps, state_after_left_mover') in rr.relation { var left_mover_single; left_mover_single, state_after_left_mover' := lemma_DemonstrateLeftMoverSelfCrashPreservationCaseOneStep( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step.step, left_mover.step); left_mover' := Low(rr.idmap(left_mover_single), left_mover_single); } lemma lemma_DemonstrateLeftMoverSelfCrashPreservationCaseMultipleSteps( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_states:seq, other_steps:seq, other_actor:Actor, left_mover:LStep ) returns ( left_mover':LStep, state_after_left_mover':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires |other_states| > 1 requires StateNextSeq(other_states, other_steps, rr.lspec.next) requires other_states[0] == initial_state requires last(other_states) == state_after_other_step requires forall step :: step in other_steps ==> rr.idmap(step) == other_actor requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in rr.lspec.next requires !rr.crashed(initial_state) requires !rr.crashed(state_after_other_step) requires rr.crashed(state_after_both_steps) requires rr.phase2(state_after_other_step, rr.idmap(left_mover)) requires rr.idmap(left_mover) != other_actor ensures rr.idmap(left_mover') == rr.idmap(left_mover) ensures ActionTuple(initial_state, state_after_left_mover', left_mover') in rr.lspec.next ensures rr.crashed(state_after_left_mover') ensures RefinementPair(state_after_both_steps, state_after_left_mover') in rr.relation decreases |other_states| { var penult := |other_states|-2; assert ActionTuple(other_states[penult], other_states[penult+1], other_steps[penult]) in rr.lspec.next; lemma_StateNextSeqMaintainsAnnotatedReachables(other_states, other_steps, rr.lspec); assert RefinementViaReductionSpecModule.LeftMoverSelfCrashPreservationConditions(rr, other_states[penult], other_states[penult+1], state_after_both_steps, other_steps[penult], left_mover); var left_mover_mid, state_after_left_mover_mid :| && rr.idmap(left_mover_mid) == rr.idmap(left_mover) && ActionTuple(other_states[penult], state_after_left_mover_mid, left_mover_mid) in rr.lspec.next && rr.crashed(state_after_left_mover_mid) && RefinementPair(state_after_both_steps, state_after_left_mover_mid) in rr.relation; if penult == 0 { left_mover' := left_mover_mid; state_after_left_mover' := state_after_left_mover_mid; } else { lemma_ReduceStateNextSeqRight(other_states, other_steps, rr.lspec.next); left_mover', state_after_left_mover' := lemma_DemonstrateLeftMoverSelfCrashPreservationCaseMultipleSteps( rr, clrr, initial_state, other_states[penult], state_after_left_mover_mid, all_but_last(other_states), all_but_last(other_steps), other_actor, left_mover_mid); } } lemma lemma_DemonstrateLeftMoverSelfCrashPreservationCaseReducible( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:CrossLevelStep, left_mover:CrossLevelStep ) returns ( left_mover':CrossLevelStep, state_after_left_mover':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(rr.lspec) requires ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next requires !clrr.crashed(initial_state) requires !clrr.crashed(state_after_other_step) requires clrr.crashed(state_after_both_steps) requires clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) requires clrr.idmap(left_mover) != clrr.idmap(other_step) requires other_step.Reducible? requires left_mover.Low? ensures clrr.idmap(left_mover') == clrr.idmap(left_mover) ensures ActionTuple(initial_state, state_after_left_mover', left_mover') in clrr.spec.next ensures clrr.crashed(state_after_left_mover') ensures RefinementPair(state_after_both_steps, state_after_left_mover') in rr.relation { var left_mover_single; left_mover_single, state_after_left_mover' := lemma_DemonstrateLeftMoverSelfCrashPreservationCaseMultipleSteps( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step.states, other_step.trace, other_step.actor, left_mover.step); left_mover' := Low(rr.idmap(left_mover_single), left_mover_single); } lemma lemma_DemonstrateLeftMoverSelfCrashPreservation( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest>, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:CrossLevelStep, left_mover:CrossLevelStep ) returns ( left_mover':CrossLevelStep, state_after_left_mover':State ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) requires initial_state in AnnotatedReachables(clrr.spec) requires ActionTuple(initial_state, state_after_other_step, other_step) in clrr.spec.next requires ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in clrr.spec.next requires !clrr.crashed(initial_state) requires !clrr.crashed(state_after_other_step) requires clrr.crashed(state_after_both_steps) requires clrr.phase2(state_after_other_step, clrr.idmap(left_mover)) requires clrr.idmap(left_mover) != clrr.idmap(other_step) ensures clrr.idmap(left_mover') == clrr.idmap(left_mover) ensures ActionTuple(initial_state, state_after_left_mover', left_mover') in clrr.spec.next ensures clrr.crashed(state_after_left_mover') ensures RefinementPair(state_after_both_steps, state_after_left_mover') in rr.relation { lemma_AnnotatedReachablesOfCrossLevelSpec(rr); assert initial_state in AnnotatedReachables(rr.lspec); if left_mover.Reducible? { lemma_ReducibleStepStartsAndEndsOutOfPhase(rr, state_after_other_step, state_after_both_steps, left_mover); assert false; } if other_step.Low? { left_mover', state_after_left_mover' := lemma_DemonstrateLeftMoverSelfCrashPreservationCaseLow( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover); } else { left_mover', state_after_left_mover' := lemma_DemonstrateLeftMoverSelfCrashPreservationCaseReducible( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover); } } lemma lemma_LeftMoverSelfCrashPreservation( rr:RefinementViaReductionRequest, clrr:CohenLamportReductionRequest> ) requires ValidRefinementViaReductionRequest(rr) requires clrr == GetCohenLamportReductionRequest(rr) ensures CohenLamportReductionSpecModule.LeftMoverSelfCrashPreservation(clrr) { forall initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover {:trigger CohenLamportReductionSpecModule.LeftMoverSelfCrashPreservationConditions(clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover)} | CohenLamportReductionSpecModule.LeftMoverSelfCrashPreservationConditions(clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover) ensures exists left_mover', state_after_left_mover' :: && clrr.idmap(left_mover') == clrr.idmap(left_mover) && ActionTuple(initial_state, state_after_left_mover', left_mover') in clrr.spec.next && clrr.crashed(state_after_left_mover') && RefinementPair(state_after_both_steps, state_after_left_mover') in clrr.relation { var left_mover', state_after_left_mover' := lemma_DemonstrateLeftMoverSelfCrashPreservation( rr, clrr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover); } } /////////////////////////////////////////////////////////////////////////// // Final demonstration that CohenLamportReductionRequest is valid /////////////////////////////////////////////////////////////////////////// lemma lemma_IsValidCohenLamportReductionRequest( rr:RefinementViaReductionRequest ) requires ValidRefinementViaReductionRequest(rr) ensures ValidCohenLamportReductionRequest(GetCohenLamportReductionRequest(rr)) { var clrr := GetCohenLamportReductionRequest(rr); lemma_AnnotatedReachablesOfCrossLevelSpec(rr); lemma_PhaseUnaffectedByOtherActors(rr, clrr); lemma_RightMoversCommute(rr, clrr); lemma_LeftMoversCommute(rr, clrr); lemma_LeftMoversAlwaysEnabled(rr, clrr); lemma_LeftMoversEnabledBeforeCrash(rr, clrr); lemma_RightMoverCrashPreservation(rr, clrr); lemma_LeftMoverSelfCrashPreservation(rr, clrr); lemma_ActionSequencesLiftable(rr, clrr); } } ================================================ FILE: Armada/strategies/reduction/RefinementViaReductionSpec.i.dfy ================================================ ///////////////////////////////////////////////////////////////////////////////////////////////////// // // This file is the specification for a request to perform refinement via a Cohen-Lamport reduction // on a behavior. Such a reduction takes a behavior where some states are in phase 1 or 2, and // returns a behavior satisfying a higher-level specification that lacks those phases. It does // so by lifting sequences of steps in the lower-level specification to atomic steps in the // higher-level specification. // // To use this specification, first create a request r of type RefinementViaReductionRequest. Then, // prove that it's a valid request, i.e., that ValidRefinementViaReductionRequest(r). Finally, call // lemma_PerformRefinementViaReduction (in RefinementViaReduction.i.dfy). // // The request specification allows behaviors that crash as their final step. But, the request // specification also demands that action sequences be reducible if they crash in the middle, i.e., // even if the actor performing those action sequences executes a step that crashes while in phase 1 // or 2. // ///////////////////////////////////////////////////////////////////////////////////////////////////// include "../../util/collections/seqs.s.dfy" include "../../spec/refinement.s.dfy" include "../refinement/GeneralRefinementLemmas.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" module RefinementViaReductionSpecModule { import opened util_collections_seqs_s import opened GeneralRefinementModule import opened GeneralRefinementLemmasModule import opened RefinementConvolutionModule import opened AnnotatedBehaviorModule import opened InvariantsModule datatype RefinementViaReductionRequest = RefinementViaReductionRequest( lspec:AnnotatedBehaviorSpec, hspec:AnnotatedBehaviorSpec, relation:RefinementRelation, idmap:LStep->Actor, phase1:(State, Actor)->bool, phase2:(State, Actor)->bool, crashed:State->bool ) predicate PhaseUnaffectedByOtherActors( rr:RefinementViaReductionRequest ) { && (forall s, s', step, actor :: ActionTuple(s, s', step) in rr.lspec.next && rr.idmap(step) != actor ==> (rr.phase1(s, actor) <==> rr.phase1(s', actor))) && (forall s, s', step, actor :: ActionTuple(s, s', step) in rr.lspec.next && rr.idmap(step) != actor ==> (rr.phase2(s, actor) <==> rr.phase2(s', actor))) } predicate CantGoDirectlyFromPhase2ToPhase1( rr:RefinementViaReductionRequest ) { forall s, s', step :: ActionTuple(s, s', step) in rr.lspec.next && rr.phase2(s, rr.idmap(step)) ==> !rr.phase1(s', rr.idmap(step)) } predicate RightMoversPreserveStateRefinement( rr:RefinementViaReductionRequest ) { forall s, s', step :: && ActionTuple(s, s', step) in rr.lspec.next && rr.phase1(s', rr.idmap(step)) && !rr.crashed(s') ==> RefinementPair(s', s) in rr.relation } predicate LeftMoversPreserveStateRefinement( rr:RefinementViaReductionRequest ) { forall s, s', step :: && ActionTuple(s, s', step) in rr.lspec.next && rr.phase2(s, rr.idmap(step)) ==> RefinementPair(s, s') in rr.relation } predicate PostCrashStepsStutter( rr:RefinementViaReductionRequest ) { forall s, s', step :: rr.crashed(s) && ActionTuple(s, s', step) in rr.lspec.next ==> s' == s } predicate RightMoversCommuteConditions( rr:RefinementViaReductionRequest, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:LStep, other_step:LStep ) { && initial_state in AnnotatedReachables(rr.lspec) && ActionTuple(initial_state, state_after_right_mover, right_mover) in rr.lspec.next && ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in rr.lspec.next && rr.idmap(right_mover) != rr.idmap(other_step) && rr.phase1(state_after_right_mover, rr.idmap(right_mover)) && !rr.crashed(state_after_both_steps) } predicate RightMoversCommute( rr:RefinementViaReductionRequest ) { forall initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step {:trigger RightMoversCommuteConditions(rr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step)} :: RightMoversCommuteConditions(rr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step) ==> exists new_middle_state, other_step', right_mover' :: && ActionTuple(initial_state, new_middle_state, other_step') in rr.lspec.next && ActionTuple(new_middle_state, state_after_both_steps, right_mover') in rr.lspec.next && rr.idmap(other_step') == rr.idmap(other_step) && rr.idmap(right_mover') == rr.idmap(right_mover) } predicate LeftMoversCommuteConditions( rr:RefinementViaReductionRequest, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:LStep, left_mover:LStep ) { && initial_state in AnnotatedReachables(rr.lspec) && ActionTuple(initial_state, state_after_other_step, other_step) in rr.lspec.next && ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in rr.lspec.next && rr.idmap(other_step) != rr.idmap(left_mover) && rr.phase2(state_after_other_step, rr.idmap(left_mover)) && !rr.crashed(state_after_both_steps) } predicate LeftMoversCommute( rr:RefinementViaReductionRequest ) { forall initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover {:trigger LeftMoversCommuteConditions(rr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover)} :: LeftMoversCommuteConditions(rr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover) ==> exists new_middle_state, left_mover', other_step' :: && ActionTuple(initial_state, new_middle_state, left_mover') in rr.lspec.next && ActionTuple(new_middle_state, state_after_both_steps, other_step') in rr.lspec.next && rr.idmap(left_mover') == rr.idmap(left_mover) && rr.idmap(other_step') == rr.idmap(other_step) } predicate LeftMoversAlwaysEnabledConditions( rr:RefinementViaReductionRequest, s:State, actor:Actor ) { && s in AnnotatedReachables(rr.lspec) && rr.phase2(s, actor) && !rr.crashed(s) } predicate LeftMoversAlwaysEnabled( rr:RefinementViaReductionRequest ) { forall s, actor {:trigger LeftMoversAlwaysEnabledConditions(rr, s, actor)} :: LeftMoversAlwaysEnabledConditions(rr, s, actor) ==> exists states, trace :: && StateNextSeq(states, trace, rr.lspec.next) && states[0] == s && (rr.crashed(last(states)) || !rr.phase2(last(states), actor)) && (forall i :: 0 <= i < |states|-1 ==> rr.phase2(states[i], actor)) && (forall step :: step in trace ==> rr.idmap(step) == actor) } predicate LeftMoversEnabledBeforeCrashConditions( rr:RefinementViaReductionRequest, initial_state:State, post_crash_state:State, crash_step:LStep, actor:Actor ) { && initial_state in AnnotatedReachables(rr.lspec) && ActionTuple(initial_state, post_crash_state, crash_step) in rr.lspec.next && !rr.crashed(initial_state) && rr.crashed(post_crash_state) && rr.idmap(crash_step) != actor && rr.phase2(initial_state, actor) } predicate LeftMoversEnabledBeforeCrash( rr:RefinementViaReductionRequest ) { forall initial_state, post_crash_state, crash_step, actor {:trigger LeftMoversEnabledBeforeCrashConditions(rr, initial_state, post_crash_state, crash_step, actor)} :: LeftMoversEnabledBeforeCrashConditions(rr, initial_state, post_crash_state, crash_step, actor) ==> exists states, trace, crash_step', post_crash_state' :: && StateNextSeq(states, trace, rr.lspec.next) && states[0] == initial_state && !rr.crashed(last(states)) && !rr.phase2(last(states), actor) && (forall i :: 0 <= i < |states|-1 ==> rr.phase2(states[i], actor)) && (forall step :: step in trace ==> rr.idmap(step) == actor) && ActionTuple(last(states), post_crash_state', crash_step') in rr.lspec.next && rr.idmap(crash_step') == rr.idmap(crash_step) && rr.crashed(post_crash_state') && RefinementPair(post_crash_state, post_crash_state') in rr.relation } predicate RightMoverCrashPreservationConditions( rr:RefinementViaReductionRequest, initial_state:State, state_after_right_mover:State, state_after_both_steps:State, right_mover:LStep, other_step:LStep ) { && initial_state in AnnotatedReachables(rr.lspec) && ActionTuple(initial_state, state_after_right_mover, right_mover) in rr.lspec.next && ActionTuple(state_after_right_mover, state_after_both_steps, other_step) in rr.lspec.next && !rr.crashed(initial_state) && !rr.crashed(state_after_right_mover) && rr.crashed(state_after_both_steps) && rr.phase1(state_after_right_mover, rr.idmap(right_mover)) && rr.idmap(right_mover) != rr.idmap(other_step) } predicate RightMoverCrashPreservation( rr:RefinementViaReductionRequest ) { forall initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step {:trigger RightMoverCrashPreservationConditions(rr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step)} :: RightMoverCrashPreservationConditions(rr, initial_state, state_after_right_mover, state_after_both_steps, right_mover, other_step) ==> exists other_step', state_after_other_step' :: && rr.idmap(other_step') == rr.idmap(other_step) && ActionTuple(initial_state, state_after_other_step', other_step') in rr.lspec.next && rr.crashed(state_after_other_step') && RefinementPair(state_after_both_steps, state_after_other_step') in rr.relation } predicate LeftMoverSelfCrashPreservationConditions( rr:RefinementViaReductionRequest, initial_state:State, state_after_other_step:State, state_after_both_steps:State, other_step:LStep, left_mover:LStep ) { && initial_state in AnnotatedReachables(rr.lspec) && ActionTuple(initial_state, state_after_other_step, other_step) in rr.lspec.next && ActionTuple(state_after_other_step, state_after_both_steps, left_mover) in rr.lspec.next && !rr.crashed(initial_state) && !rr.crashed(state_after_other_step) && rr.crashed(state_after_both_steps) && rr.phase2(state_after_other_step, rr.idmap(left_mover)) && rr.idmap(left_mover) != rr.idmap(other_step) } predicate LeftMoverSelfCrashPreservation( rr:RefinementViaReductionRequest ) { forall initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover {:trigger LeftMoverSelfCrashPreservationConditions(rr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover)} :: LeftMoverSelfCrashPreservationConditions(rr, initial_state, state_after_other_step, state_after_both_steps, other_step, left_mover) ==> exists left_mover', state_after_left_mover' :: && rr.idmap(left_mover') == rr.idmap(left_mover) && ActionTuple(initial_state, state_after_left_mover', left_mover') in rr.lspec.next && rr.crashed(state_after_left_mover') && RefinementPair(state_after_both_steps, state_after_left_mover') in rr.relation } predicate ActionSequencesLiftableConditions( rr:RefinementViaReductionRequest, states:seq, trace:seq, actor:Actor ) { && StateNextSeq(states, trace, rr.lspec.next) && (forall step :: step in trace ==> rr.idmap(step) == actor) && |states| > 1 && !rr.phase1(states[0], actor) && !rr.phase2(states[0], actor) && (var s := last(states); !rr.crashed(s) ==> !rr.phase1(s, actor) && !rr.phase2(s, actor)) && (forall i :: 0 <= i < |trace| ==> !rr.crashed(states[i])) && (forall i :: 0 < i < |trace| ==> var s := states[i]; rr.phase1(s, actor) || rr.phase2(s, actor)) } predicate ActionSequencesLiftable( rr:RefinementViaReductionRequest ) { forall states, trace, actor {:trigger ActionSequencesLiftableConditions(rr, states, trace, actor)} :: ActionSequencesLiftableConditions(rr, states, trace, actor) ==> exists hstep :: ActionTuple(states[0], last(states), hstep) in rr.hspec.next } predicate ValidRefinementViaReductionRequest( rr:RefinementViaReductionRequest ) { && RefinementRelationReflexive(rr.relation) && RefinementRelationTransitive(rr.relation) && rr.hspec.init == rr.lspec.init && (forall s, actor :: s in rr.lspec.init ==> !rr.phase1(s, actor) && !rr.phase2(s, actor)) && (forall s, actor :: rr.phase1(s, actor) ==> !rr.phase2(s, actor)) && CantGoDirectlyFromPhase2ToPhase1(rr) && PhaseUnaffectedByOtherActors(rr) && RightMoversPreserveStateRefinement(rr) && LeftMoversPreserveStateRefinement(rr) && PostCrashStepsStutter(rr) && RightMoversCommute(rr) && LeftMoversCommute(rr) && LeftMoversAlwaysEnabled(rr) && LeftMoversEnabledBeforeCrash(rr) && RightMoverCrashPreservation(rr) && LeftMoverSelfCrashPreservation(rr) && ActionSequencesLiftable(rr) } } ================================================ FILE: Armada/strategies/refinement/AnnotatedBehavior.i.dfy ================================================ include "../../spec/refinement.s.dfy" include "../../util/collections/seqs.i.dfy" module AnnotatedBehaviorModule { import opened GeneralRefinementModule import opened util_collections_seqs_s import opened util_collections_seqs_i datatype ActionTuple = ActionTuple(s:State, s':State, step:Step) datatype AnnotatedBehavior = AnnotatedBehavior( states:seq, trace:seq ) datatype AnnotatedBehaviorSpec = AnnotatedBehaviorSpec( init:iset, next:iset> ) predicate StateNextSeq( states:seq, trace:seq, next:iset> ) { && |states| == |trace| + 1 && (forall i {:trigger ActionTuple(states[i], states[i+1], trace[i]) in next} :: 0 <= i < |trace| ==> ActionTuple(states[i], states[i+1], trace[i]) in next) } predicate AnnotatedBehaviorSatisfiesSpec( b:AnnotatedBehavior, spec:AnnotatedBehaviorSpec ) { && StateNextSeq(b.states, b.trace, spec.next) && b.states[0] in spec.init } function AnnotatedBehaviorSpecToSpec(spec:AnnotatedBehaviorSpec) : Spec { Spec(spec.init, iset s, s', step | ActionTuple(s, s', step) in spec.next :: StatePair(s, s')) } predicate AnnotatedBehaviorSpecRefinesSpec( aspec:AnnotatedBehaviorSpec, spec:Spec ) { && aspec.init <= spec.init && (forall s, s', step :: ActionTuple(s, s', step) in aspec.next ==> StatePair(s, s') in spec.next) } predicate SpecRefinesAnnotatedBehaviorSpec( spec:Spec, aspec:AnnotatedBehaviorSpec ) { && spec.init <= aspec.init && (forall s, s' :: StatePair(s, s') in spec.next ==> exists step:Step :: ActionTuple(s, s', step) in aspec.next) } lemma lemma_AnnotatedBehaviorSpecEquivalentToConvertedSelf(spec:AnnotatedBehaviorSpec) ensures AnnotatedBehaviorSpecRefinesSpec(spec, AnnotatedBehaviorSpecToSpec(spec)); ensures SpecRefinesAnnotatedBehaviorSpec(AnnotatedBehaviorSpecToSpec(spec), spec); { } lemma lemma_BehaviorInAnnotatedBehaviorSatisfiesSpec( ab:AnnotatedBehavior, aspec:AnnotatedBehaviorSpec, spec:Spec ) requires AnnotatedBehaviorSatisfiesSpec(ab, aspec); requires AnnotatedBehaviorSpecRefinesSpec(aspec, spec); ensures BehaviorSatisfiesSpec(ab.states, spec); { forall i | 0 <= i < |ab.trace| ensures ActionTuple(ab.states[i], ab.states[i+1], ab.trace[i]) in aspec.next { } } lemma lemma_BehaviorInAnnotatedBehaviorSatisfiesConvertedSpec( b:AnnotatedBehavior, spec:AnnotatedBehaviorSpec ) requires AnnotatedBehaviorSatisfiesSpec(b, spec); ensures BehaviorSatisfiesSpec(b.states, AnnotatedBehaviorSpecToSpec(spec)); { lemma_AnnotatedBehaviorSpecEquivalentToConvertedSelf(spec); lemma_BehaviorInAnnotatedBehaviorSatisfiesSpec(b, spec, AnnotatedBehaviorSpecToSpec(spec)); } lemma lemma_CreateAnnotatedBehaviorFromBehavior( b:seq, spec:Spec, aspec:AnnotatedBehaviorSpec ) returns ( ab:AnnotatedBehavior ) requires SpecRefinesAnnotatedBehaviorSpec(spec, aspec); requires BehaviorSatisfiesSpec(b, spec); ensures ab.states == b; ensures AnnotatedBehaviorSatisfiesSpec(ab, aspec); { if |b| == 1 { ab := AnnotatedBehavior(b, []); } else { var ab_prefix := lemma_CreateAnnotatedBehaviorFromBehavior(all_but_last(b), spec, aspec); var penult := |b|-2; assert StatePair(b[penult], b[penult+1]) in spec.next; var step :| ActionTuple(b[|b|-2], b[|b|-2+1], step) in aspec.next; ab := AnnotatedBehavior(b, ab_prefix.trace + [step]); } } lemma lemma_ExtendStateNextSeqRight( states:seq, trace:seq, next:iset>, new_state:State, new_step:Step ) requires StateNextSeq(states, trace, next) requires ActionTuple(last(states), new_state, new_step) in next ensures StateNextSeq(states + [new_state], trace + [new_step], next) { } lemma lemma_ExtendStateNextSeqLeft( states:seq, trace:seq, next:iset>, new_state:State, new_step:Step ) requires StateNextSeq(states, trace, next) requires ActionTuple(new_state, states[0], new_step) in next ensures StateNextSeq([new_state] + states, [new_step] + trace, next) { var states' := [new_state] + states; var trace' := [new_step] + trace; forall i {:trigger ActionTuple(states'[i], states'[i+1], trace'[i]) in next} | 0 <= i < |trace'| ensures ActionTuple(states'[i], states'[i+1], trace'[i]) in next { if i == 0 { assert states'[i] == new_state; assert states'[i+1] == states[0]; assert trace'[i] == new_step; } else { var iminus := i-1; assert states'[i] == states[iminus]; assert states'[i+1] == states[iminus+1]; assert trace'[i] == trace[iminus]; assert ActionTuple(states[iminus], states[iminus+1], trace[iminus]) in next; } } } lemma lemma_ReduceStateNextSeqRight( states:seq, trace:seq, next:iset> ) requires StateNextSeq(states, trace, next) requires |trace| > 0 ensures StateNextSeq(all_but_last(states), all_but_last(trace), next) { } lemma lemma_ReduceStateNextSeqLeft( states:seq, trace:seq, next:iset> ) requires StateNextSeq(states, trace, next) requires |trace| > 0 ensures StateNextSeq(states[1..], trace[1..], next) { var states' := states[1..]; var trace' := trace[1..]; forall i {:trigger ActionTuple(states'[i], states'[i+1], trace'[i]) in next} | 0 <= i < |trace'| ensures ActionTuple(states'[i], states'[i+1], trace'[i]) in next { var iplus := i+1; assert states'[i] == states[iplus]; assert states'[i+1] == states[iplus+1]; assert trace'[i] == trace[iplus]; assert ActionTuple(states[iplus], states[iplus+1], trace[iplus]) in next; } } lemma lemma_ReplaceStateOnlyStateNextSeqRight( states:seq, trace:seq, next:iset>, new_state:State ) requires StateNextSeq(states, trace, next) requires |states| > 1 requires ActionTuple(states[|states|-2], new_state, last(trace)) in next ensures StateNextSeq(all_but_last(states) + [new_state], trace, next) { } lemma lemma_ReplaceStateNextSeqRight( states:seq, trace:seq, next:iset>, new_state:State, new_step:Step ) requires StateNextSeq(states, trace, next) requires |states| > 1 requires ActionTuple(states[|states|-2], new_state, new_step) in next ensures StateNextSeq(all_but_last(states) + [new_state], all_but_last(trace) + [new_step], next) { } lemma lemma_TakeStateNextSeq( states:seq, trace:seq, next:iset>, pos:int ) requires StateNextSeq(states, trace, next) requires |trace| > 0 requires 0 <= pos <= |trace| ensures StateNextSeq(states[..pos+1], trace[..pos], next) { } lemma lemma_DropStateNextSeq( states:seq, trace:seq, next:iset>, pos:int ) requires StateNextSeq(states, trace, next) requires |trace| > 0 requires 0 <= pos <= |trace| ensures StateNextSeq(states[pos..], trace[pos..], next) { var states' := states[pos..]; var trace' := trace[pos..]; forall i {:trigger ActionTuple(states'[i], states'[i+1], trace'[i]) in next} | 0 <= i < |trace'| ensures ActionTuple(states'[i], states'[i+1], trace'[i]) in next { var iplus := i+pos; lemma_IndexIntoDrop(states, pos, i); lemma_IndexIntoDrop(states, pos, i+1); lemma_IndexIntoDrop(trace, pos, i); assert states'[i] == states[iplus]; assert states'[i+1] == states[iplus+1]; assert trace'[i] == trace[iplus]; assert ActionTuple(states[iplus], states[iplus+1], trace[iplus]) in next; } } } ================================================ FILE: Armada/strategies/refinement/GeneralRefinementLemmas.i.dfy ================================================ include "../../spec/refinement.s.dfy" include "../../util/collections/seqs.i.dfy" module GeneralRefinementLemmasModule { import opened util_collections_seqs_i import opened util_collections_seqs_s import opened GeneralRefinementModule predicate RefinementRelationReflexive(relation:RefinementRelation) { forall x :: RefinementPair(x, x) in relation } lemma lemma_IfRefinementRelationReflexiveThenBehaviorRefinesItself(b:seq, relation:RefinementRelation) requires |b| > 0 requires RefinementRelationReflexive(relation) ensures BehaviorRefinesBehavior(b, b, relation) { var lh_map := ConvertMapToSeq(|b|, map i | 0 <= i < |b| :: RefinementRange(i, i)); assert BehaviorRefinesBehaviorUsingRefinementMap(b, b, relation, lh_map); } lemma lemma_LaterFirstBeyondEarlierLastInRefinementMap( low_level_behavior_size:int, high_level_behavior_size:int, lh_map:RefinementMap, i:int, j:int ) requires IsValidRefinementMap(low_level_behavior_size, high_level_behavior_size, lh_map) requires 0 <= i <= j < low_level_behavior_size ensures i < j ==> lh_map[i].last <= lh_map[j].first decreases j - i { if j == i || j == i + 1 { return; } lemma_LaterFirstBeyondEarlierLastInRefinementMap(low_level_behavior_size, high_level_behavior_size, lh_map, i+1, j); } lemma lemma_GetPrefixOfBehaviorsAndRefinementMap( lb:seq, hb:seq, lh_map:RefinementMap, lh_relation:RefinementRelation, new_low_behavior_size:int ) returns ( lb':seq, hb':seq, lh_map':RefinementMap ) requires BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, lh_relation, lh_map) requires 0 < new_low_behavior_size <= |lb| ensures |lb'| == new_low_behavior_size ensures lb' == lb[..new_low_behavior_size] ensures lh_map' == lh_map[..new_low_behavior_size] ensures new_low_behavior_size > 0 ==> 0 <= lh_map[new_low_behavior_size-1].last < |hb| ensures if new_low_behavior_size > 0 then hb' == hb[ .. lh_map[new_low_behavior_size-1].last + 1] else hb' == [] ensures BehaviorRefinesBehaviorUsingRefinementMap(lb', hb', lh_relation, lh_map') { if new_low_behavior_size == |lb| { lb' := lb; hb' := hb; lh_map' := lh_map; return; } var j := new_low_behavior_size - 1; assert lh_map[j].last == lh_map[j+1].first || lh_map[j].last == lh_map[j+1].first - 1; lb' := lb[..new_low_behavior_size]; lh_map' := lh_map[..new_low_behavior_size]; hb' := hb[.. last(lh_map').last+1]; forall pair | pair in lh_map' ensures 0 <= pair.first <= pair.last < |hb'|; { var i :| 0 <= i < |lh_map'| && pair == lh_map'[i]; lemma_LaterFirstBeyondEarlierLastInRefinementMap(|lb|, |hb|, lh_map, i, j); } assert BehaviorRefinesBehaviorUsingRefinementMap(lb', hb', lh_relation, lh_map'); } lemma lemma_RefinementMapIsReversible( low_level_behavior_size:int, high_level_behavior_size:int, lh_map:RefinementMap, hpos:int ) returns ( lpos:int ) requires IsValidRefinementMap(low_level_behavior_size, high_level_behavior_size, lh_map) requires 0 <= hpos < high_level_behavior_size ensures 0 <= lpos < low_level_behavior_size ensures lh_map[lpos].first <= hpos <= lh_map[lpos].last { if last(lh_map).first <= hpos <= last(lh_map).last { lpos := |lh_map| - 1; return; } var j := |lh_map| - 2; assert lh_map[j+1].first == lh_map[j].last || lh_map[j+1].first == lh_map[j].last + 1; var new_low_level_behavior_size := low_level_behavior_size - 1; var lh_map' := lh_map[..new_low_level_behavior_size]; var new_high_level_behavior_size := last(lh_map').last + 1; forall pair | pair in lh_map' ensures 0 <= pair.first <= pair.last < new_high_level_behavior_size; { var i :| 0 <= i < |lh_map'| && pair == lh_map'[i]; lemma_LaterFirstBeyondEarlierLastInRefinementMap(low_level_behavior_size, high_level_behavior_size, lh_map, i, j); } assert IsValidRefinementMap(new_low_level_behavior_size, new_high_level_behavior_size, lh_map'); lpos := lemma_RefinementMapIsReversible(new_low_level_behavior_size, new_high_level_behavior_size, lh_map', hpos); } lemma lemma_ConcatenatingBehaviorsPreservesRefinement( lb1:seq, lb2:seq, hb1:seq, hb2:seq, relation:RefinementRelation ) requires BehaviorRefinesBehavior(lb1, hb1, relation) requires BehaviorRefinesBehavior(lb2, hb2, relation) ensures BehaviorRefinesBehavior(lb1 + lb2, hb1 + hb2, relation) { var lh_map1 :| BehaviorRefinesBehaviorUsingRefinementMap(lb1, hb1, relation, lh_map1); var lh_map2 :| BehaviorRefinesBehaviorUsingRefinementMap(lb2, hb2, relation, lh_map2); var lh_map2_adjusted := MapSeqToSeq(lh_map2, (x:RefinementRange) => RefinementRange(x.first + |hb1|, x.last + |hb1|)); var lh_map := lh_map1 + lh_map2_adjusted; var lb := lb1 + lb2; var hb := hb1 + hb2; assert BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, relation, lh_map); } lemma lemma_ExtendBehaviorRefinesBehaviorRightOne(lb:seq, hb:seq, relation:RefinementRelation, s:T) requires BehaviorRefinesBehavior(lb, hb, relation) requires RefinementRelationReflexive(relation) ensures BehaviorRefinesBehavior(lb + [s], hb + [s], relation) { var lh_map :| BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, relation, lh_map); var lb' := lb + [s]; var hb' := hb + [s]; var lh_map' := lh_map + [RefinementRange(|hb|, |hb|)]; assert BehaviorRefinesBehaviorUsingRefinementMap(lb', hb', relation, lh_map'); } lemma lemma_ExtendBehaviorRefinesBehaviorRightOne_LH(lb:seq, hb:seq, relation:RefinementRelation, ls:T, hs:U) requires BehaviorRefinesBehavior(lb, hb, relation) requires RefinementPair(ls, hs) in relation ensures BehaviorRefinesBehavior(lb + [ls], hb + [hs], relation) { var lh_map :| BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, relation, lh_map); var lb' := lb + [ls]; var hb' := hb + [hs]; var lh_map' := lh_map + [RefinementRange(|hb|, |hb|)]; assert BehaviorRefinesBehaviorUsingRefinementMap(lb', hb', relation, lh_map'); } lemma lemma_ExtendBehaviorRefinesBehaviorRightOne_LStutter(lb:seq, hb:seq, relation:RefinementRelation, hs:U) requires BehaviorRefinesBehavior(lb, hb, relation) requires RefinementPair(last(lb), hs) in relation ensures BehaviorRefinesBehavior(lb, hb + [hs], relation) { var lh_map :| BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, relation, lh_map); var last_range := last(lh_map); var last_range' := last_range.(last := |hb|); var hb' := hb + [hs]; var lh_map' := lh_map[|lh_map|-1 := last_range']; assert BehaviorRefinesBehaviorUsingRefinementMap(lb, hb', relation, lh_map'); } lemma lemma_ExtendBehaviorRefinesBehaviorRightOne_HStutter(lb:seq, hb:seq, relation:RefinementRelation, ls:T) requires BehaviorRefinesBehavior(lb, hb, relation) requires RefinementPair(ls, last(hb)) in relation ensures BehaviorRefinesBehavior(lb + [ls], hb, relation) { var lh_map :| BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, relation, lh_map); var lb' := lb + [ls]; var lh_map' := lh_map + [RefinementRange(|hb|-1, |hb|-1)]; assert BehaviorRefinesBehaviorUsingRefinementMap(lb', hb, relation, lh_map'); } lemma lemma_ExtendBehaviorRefinesBehaviorLeftOne(lb:seq, hb:seq, relation:RefinementRelation, s:T) requires BehaviorRefinesBehavior(lb, hb, relation) requires RefinementRelationReflexive(relation) ensures BehaviorRefinesBehavior([s] + lb, [s] + hb, relation) { var lh_map :| BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, relation, lh_map); var lb' := [s] + lb; var hb' := [s] + hb; var lh_map' := [RefinementRange(0, 0)] + MapSeqToSeq(lh_map, (x:RefinementRange) => RefinementRange(x.first+1, x.last+1)); assert BehaviorRefinesBehaviorUsingRefinementMap(lb', hb', relation, lh_map'); } lemma lemma_ExtendBehaviorRefinesBehaviorLeftOne_LH(lb:seq, hb:seq, relation:RefinementRelation, ls:T, hs:U) requires BehaviorRefinesBehavior(lb, hb, relation) requires RefinementPair(ls, hs) in relation ensures BehaviorRefinesBehavior([ls] + lb, [hs] + hb, relation) { var lh_map :| BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, relation, lh_map); var lb' := [ls] + lb; var hb' := [hs] + hb; var lh_map' := [RefinementRange(0, 0)] + MapSeqToSeq(lh_map, (x:RefinementRange) => RefinementRange(x.first+1, x.last+1)); assert BehaviorRefinesBehaviorUsingRefinementMap(lb', hb', relation, lh_map'); } lemma lemma_ExtendBehaviorRefinesBehaviorRight(lb:seq, hb:seq, relation:RefinementRelation, eb:seq) requires BehaviorRefinesBehavior(lb, hb, relation) requires RefinementRelationReflexive(relation); ensures BehaviorRefinesBehavior(lb + eb, hb + eb, relation) decreases |eb| { if |eb| == 0 { assert lb + eb == lb; assert hb + eb == hb; } else { lemma_ExtendBehaviorRefinesBehaviorRightOne(lb, hb, relation, eb[0]); lemma_ExtendBehaviorRefinesBehaviorRight(lb + [eb[0]], hb + [eb[0]], relation, eb[1..]); lemma_SequenceIsCarPlusCdr(eb); lemma_SequenceConcatenationAssociative(lb, [eb[0]], eb[1..]); lemma_SequenceConcatenationAssociative(hb, [eb[0]], eb[1..]); } } lemma lemma_ExtendBehaviorRefinesBehaviorLeft(lb:seq, hb:seq, relation:RefinementRelation, eb:seq) requires BehaviorRefinesBehavior(lb, hb, relation) requires RefinementRelationReflexive(relation); ensures BehaviorRefinesBehavior(eb + lb, eb + hb, relation) decreases |eb| { if |eb| == 0 { assert eb + lb == lb; assert eb + hb == hb; } else { lemma_ExtendBehaviorRefinesBehaviorLeftOne(lb, hb, relation, last(eb)); lemma_ExtendBehaviorRefinesBehaviorLeft([last(eb)] + lb, [last(eb)] + hb, relation, all_but_last(eb)); lemma_AllButLastPlusLastIsSeq(eb); lemma_SequenceConcatenationAssociative(all_but_last(eb), [last(eb)], lb); lemma_SequenceConcatenationAssociative(all_but_last(eb), [last(eb)], hb); } } lemma lemma_ExtendBehaviorSatisfiesSpecRightOne(b:seq, spec:Spec, s':State) requires BehaviorSatisfiesSpec(b, spec) requires StatePair(last(b), s') in spec.next ensures BehaviorSatisfiesSpec(b + [s'], spec) { } } ================================================ FILE: Armada/strategies/refinement/RefinementConvolution.i.dfy ================================================ include "GeneralRefinementLemmas.i.dfy" module RefinementConvolutionModule { import opened GeneralRefinementLemmasModule import opened GeneralRefinementModule import opened util_collections_seqs_s predicate RefinementRelationsConvolve( lm_relation:RefinementRelation, mh_relation:RefinementRelation, lh_relation:RefinementRelation ) { forall l, m, h :: RefinementPair(l, m) in lm_relation && RefinementPair(m, h) in mh_relation ==> RefinementPair(l, h) in lh_relation } predicate RefinementRelationsQuadruplyConvolve( r12: RefinementRelation, r23: RefinementRelation, r34: RefinementRelation, r45: RefinementRelation, r15: RefinementRelation ) { forall s1, s2, s3, s4, s5 :: && RefinementPair(s1, s2) in r12 && RefinementPair(s2, s3) in r23 && RefinementPair(s3, s4) in r34 && RefinementPair(s4, s5) in r45 ==> RefinementPair(s1, s5) in r15 } predicate RefinementRelationTransitive(relation:RefinementRelation) { RefinementRelationsConvolve(relation, relation, relation) } predicate BehaviorRefinesWhatOtherBehaviorRefines( lb:seq, mb:seq, lh_relation:RefinementRelation, mh_relation:RefinementRelation ) { forall hb :: BehaviorRefinesBehavior(mb, hb, mh_relation) ==> BehaviorRefinesBehavior(lb, hb, lh_relation) } lemma lemma_RefinementConvolution( lb:seq, mb:seq, hb:seq, lm_relation:RefinementRelation, mh_relation:RefinementRelation, lh_relation:RefinementRelation, lm_map:RefinementMap, mh_map:RefinementMap ) returns ( lh_map:RefinementMap ) requires BehaviorRefinesBehaviorUsingRefinementMap(lb, mb, lm_relation, lm_map); requires BehaviorRefinesBehaviorUsingRefinementMap(mb, hb, mh_relation, mh_map); requires RefinementRelationsConvolve(lm_relation, mh_relation, lh_relation); ensures BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, lh_relation, lh_map); { if |lb| == 0 { lh_map := []; return; } if |lb| == 1 { lh_map := [ RefinementRange(0, |hb|-1) ]; assert IsValidRefinementMap(|lb|, |hb|, lh_map); forall i, j {:trigger RefinementPair(lb[i], hb[j]) in lh_relation} | 0 <= i < |lb| && lh_map[i].first <= j <= lh_map[i].last && 0 <= j < |hb| ensures RefinementPair(lb[i], hb[j]) in lh_relation; { var k := lemma_RefinementMapIsReversible(|mb|, |hb|, mh_map, j); assert lm_map[i].first <= k <= lm_map[i].last; assert RefinementPair(lb[i], mb[k]) in lm_relation; assert RefinementPair(mb[k], hb[j]) in mh_relation; } return; } var lb', mb', lm_map' := lemma_GetPrefixOfBehaviorsAndRefinementMap(lb, mb, lm_map, lm_relation, |lb|-1); var mb'', hb', mh_map' := lemma_GetPrefixOfBehaviorsAndRefinementMap(mb, hb, mh_map, mh_relation, |mb'|); assert mb'' == mb'; var lh_map' := lemma_RefinementConvolution(lb', mb', hb', lm_relation, mh_relation, lh_relation, lm_map', mh_map'); var second_from_last := |lm_map|-2; assert lm_map[second_from_last].last == lm_map[second_from_last+1].first || lm_map[second_from_last].last + 1 == lm_map[second_from_last+1].first; var new_first; var witness_to_correspondence; if lm_map[second_from_last].last == lm_map[second_from_last+1].first { new_first := last(lh_map').last; witness_to_correspondence := lm_map[second_from_last].last; } else { assert lm_map[second_from_last].last + 1 == lm_map[second_from_last+1].first; var k := lm_map[second_from_last].last; assert mh_map[k].last == mh_map[k+1].first || mh_map[k].last + 1 == mh_map[k+1].first; if mh_map[k].last == mh_map[k+1].first { new_first := last(lh_map').last; witness_to_correspondence := k+1; } else { assert mh_map[k].last + 1 == mh_map[k+1].first; new_first := last(lh_map').last + 1; witness_to_correspondence := lm_map[second_from_last+1].first; } } assert new_first == last(lh_map').last || new_first == last(lh_map').last + 1; assert lm_map[second_from_last+1].first <= witness_to_correspondence <= lm_map[second_from_last+1].last; assert mh_map[witness_to_correspondence].first <= new_first <= mh_map[witness_to_correspondence].last; lh_map := lh_map' + [ RefinementRange(new_first, |hb|-1) ]; forall i | 0 <= i < |lh_map|-1 ensures lh_map[i+1].first == lh_map[i].last || lh_map[i+1].first == lh_map[i].last + 1 { if i < |lh_map|-2 { assert lh_map[i] == lh_map'[i]; assert lh_map[i+1] == lh_map'[i+1]; assert lh_map'[i+1].first == lh_map'[i].last || lh_map'[i+1].first == lh_map'[i].last + 1; } else { assert lh_map[i] == lh_map'[i]; assert lh_map[i+1].first == new_first; assert lh_map[i+1].last == |hb|-1; assert lh_map[i+1].first == lh_map[i].last || lh_map[i+1].first == lh_map[i].last + 1; } } assert IsValidRefinementMap(|lb|, |hb|, lh_map); forall i, j {:trigger RefinementPair(lb[i], hb[j]) in lh_relation} | 0 <= i < |lb| && lh_map[i].first <= j <= lh_map[i].last ensures RefinementPair(lb[i], hb[j]) in lh_relation; { if i < |lb'| { assert lh_map'[i] == lh_map[i]; assert lh_map'[i].first <= j <= lh_map'[i].last; assert RefinementPair(lb'[i], hb'[j]) in lh_relation; } else { assert lm_map[i] == lm_map[second_from_last+1]; assert lm_map[i].first <= witness_to_correspondence <= lm_map[i].last; var mid := lemma_RefinementMapIsReversible(|mb|, |hb|, mh_map, j); assert mh_map[mid].first <= j <= mh_map[mid].last; if mid < witness_to_correspondence { lemma_LaterFirstBeyondEarlierLastInRefinementMap(|mb|, |hb|, mh_map, mid, witness_to_correspondence); assert new_first <= j <= mh_map[mid].last <= mh_map[witness_to_correspondence].first <= new_first; assert j == new_first; assert RefinementPair(lb[i], mb[witness_to_correspondence]) in lm_relation; assert RefinementPair(mb[witness_to_correspondence], hb[j]) in mh_relation; } else { assert RefinementPair(lb[i], mb[mid]) in lm_relation; assert RefinementPair(mb[mid], hb[j]) in mh_relation; } } } assert BehaviorRefinesBehaviorUsingRefinementMap(lb, hb, lh_relation, lh_map); } lemma lemma_RefinementConvolutionPure( lb:seq, mb:seq, hb:seq, lm_relation:RefinementRelation, mh_relation:RefinementRelation, lh_relation:RefinementRelation ) requires BehaviorRefinesBehavior(lb, mb, lm_relation); requires BehaviorRefinesBehavior(mb, hb, mh_relation); requires RefinementRelationsConvolve(lm_relation, mh_relation, lh_relation); ensures BehaviorRefinesBehavior(lb, hb, lh_relation); { var lm_map :| BehaviorRefinesBehaviorUsingRefinementMap(lb, mb, lm_relation, lm_map); var mh_map :| BehaviorRefinesBehaviorUsingRefinementMap(mb, hb, mh_relation, mh_map); var lh_map := lemma_RefinementConvolution(lb, mb, hb, lm_relation, mh_relation, lh_relation, lm_map, mh_map); } lemma lemma_RefinementConvolutionTransitive( lb:seq, mb:seq, hb:seq, relation:RefinementRelation ) requires BehaviorRefinesBehavior(lb, mb, relation); requires BehaviorRefinesBehavior(mb, hb, relation); requires RefinementRelationTransitive(relation); ensures BehaviorRefinesBehavior(lb, hb, relation); { lemma_RefinementConvolutionPure(lb, mb, hb, relation, relation, relation); } lemma lemma_EstablishBehaviorRefinesWhatOtherBehaviorRefines( lb:seq, mb:seq, lm_relation:RefinementRelation, mh_relation:RefinementRelation, lh_relation:RefinementRelation ) requires BehaviorRefinesBehavior(lb, mb, lm_relation); requires RefinementRelationsConvolve(lm_relation, mh_relation, lh_relation); ensures BehaviorRefinesWhatOtherBehaviorRefines(lb, mb, lh_relation, mh_relation); { forall hb | BehaviorRefinesBehavior(mb, hb, mh_relation) ensures BehaviorRefinesBehavior(lb, hb, lh_relation); { lemma_RefinementConvolutionPure(lb, mb, hb, lm_relation, mh_relation, lh_relation); } } lemma lemma_SpecRefinesSpecConvolution( spec1: Spec, spec2: Spec, spec3: Spec, r12: RefinementRelation, r23: RefinementRelation, r13: RefinementRelation ) requires SpecRefinesSpec(spec1, spec2, r12) requires SpecRefinesSpec(spec2, spec3, r23) requires RefinementRelationsConvolve(r12, r23, r13) ensures SpecRefinesSpec(spec1, spec3, r13) { forall b1 | BehaviorSatisfiesSpec(b1, spec1) ensures BehaviorRefinesSpec(b1, spec3, r13) { assert BehaviorRefinesSpec(b1, spec2, r12); var b2 :| BehaviorRefinesBehavior(b1, b2, r12) && BehaviorSatisfiesSpec(b2, spec2); assert BehaviorRefinesSpec(b2, spec3, r23); var b3 :| BehaviorRefinesBehavior(b2, b3, r23) && BehaviorSatisfiesSpec(b3, spec3); lemma_RefinementConvolutionPure(b1, b2, b3, r12, r23, r13); } } lemma lemma_SpecRefinesSpecQuadrupleConvolution( spec1: Spec, spec2: Spec, spec3: Spec, spec4: Spec, spec5: Spec, r12: RefinementRelation, r23: RefinementRelation, r34: RefinementRelation, r45: RefinementRelation, r15: RefinementRelation ) requires SpecRefinesSpec(spec1, spec2, r12) requires SpecRefinesSpec(spec2, spec3, r23) requires SpecRefinesSpec(spec3, spec4, r34) requires SpecRefinesSpec(spec4, spec5, r45) requires RefinementRelationsQuadruplyConvolve(r12, r23, r34, r45, r15) ensures SpecRefinesSpec(spec1, spec5, r15) { var r13 := iset s1, s2, s3 | RefinementPair(s1, s2) in r12 && RefinementPair(s2, s3) in r23 :: RefinementPair(s1, s3); var r14 := iset s1, s3, s4 | RefinementPair(s1, s3) in r13 && RefinementPair(s3, s4) in r34 :: RefinementPair(s1, s4); forall b1 | BehaviorSatisfiesSpec(b1, spec1) ensures BehaviorRefinesSpec(b1, spec5, r15) { assert BehaviorRefinesSpec(b1, spec2, r12); var b2 :| BehaviorRefinesBehavior(b1, b2, r12) && BehaviorSatisfiesSpec(b2, spec2); var b3 :| BehaviorRefinesBehavior(b2, b3, r23) && BehaviorSatisfiesSpec(b3, spec3); lemma_RefinementConvolutionPure(b1, b2, b3, r12, r23, r13); var b4 :| BehaviorRefinesBehavior(b3, b4, r34) && BehaviorSatisfiesSpec(b4, spec4); lemma_RefinementConvolutionPure(b1, b3, b4, r13, r34, r14); var b5 :| BehaviorRefinesBehavior(b4, b5, r45) && BehaviorSatisfiesSpec(b5, spec5); lemma_RefinementConvolutionPure(b1, b4, b5, r14, r45, r15); assert BehaviorRefinesBehavior(b1, b5, r15); } } } ================================================ FILE: Armada/strategies/starweakening/StarWeakening.i.dfy ================================================ include "StarWeakeningSpec.i.dfy" include "../../util/collections/seqs.i.dfy" include "../../spec/refinement.s.dfy" module StarWeakeningModule { import opened StarWeakeningSpecModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule import opened util_collections_seqs_i import opened util_collections_seqs_s lemma lemma_LRefinesH ( lb:AnnotatedBehavior, hb:AnnotatedBehavior, wr:StarWeakeningRequest, lh_map:RefinementMap) requires ValidStarWeakeningRequest(wr) requires AnnotatedBehaviorSatisfiesSpec(lb, wr.lspec) requires hb.states == MapSeqToSeq(lb.states, wr.converter); requires |lh_map| == |lb.states| requires forall i :: 0 <= i < |lh_map| ==> lh_map[i] == RefinementRange(i, i) ensures BehaviorRefinesBehaviorUsingRefinementMap(lb.states, hb.states, wr.relation, lh_map) { assert hb.states == MapSeqToSeq(lb.states, wr.converter); lemma_InvariantHoldsAtStep(lb, 0, wr.lspec, wr.inv); var i := 0; while i < |lb.states| invariant 0 <= i <= |lb.states| invariant forall j :: 0 <= j < i ==> RefinementPair(lb.states[j], hb.states[j]) in wr.relation; { lemma_InvariantHoldsAtStep(lb, i, wr.lspec, wr.inv); assert RefinementPair(lb.states[i], hb.states[i]) in wr.relation; i := i + 1; } } lemma lemma_PerformStarWeakening( wr:StarWeakeningRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidStarWeakeningRequest(wr) requires AnnotatedBehaviorSatisfiesSpec(lb, wr.lspec) ensures hb.states == MapSeqToSeq(lb.states, wr.converter) ensures AnnotatedBehaviorSatisfiesSpec(hb, wr.hspec) ensures BehaviorRefinesBehavior(lb.states, hb.states, wr.relation) { var hstates := [wr.converter(lb.states[0])]; var htrace := []; var lh_map := [RefinementRange(0, 0)]; var pos := 0; lemma_InvariantHoldsAtStep(lb, 0, wr.lspec, wr.inv); while pos < |lb.states| - 1 invariant 0 <= pos < |lb.states| invariant |htrace| == |hstates|-1 invariant hstates[0] in wr.hspec.init invariant forall i :: 0 <= i < |htrace| ==> ActionTuple(hstates[i], hstates[i+1], htrace[i]) in wr.hspec.next invariant last(hstates) == wr.converter(lb.states[pos]) invariant hstates == MapSeqToSeq(lb.states[..pos + 1], wr.converter) invariant forall i :: 0 <= i < |lh_map| ==> lh_map[i] == RefinementRange(i, i) invariant |lh_map| == pos + 1 { lemma_InvariantHoldsAtStep(lb, pos, wr.lspec, wr.inv); lemma_InvariantHoldsAtStep(lb, pos+1, wr.lspec, wr.inv); var ls := lb.states[pos]; var ls' := lb.states[pos+1]; var lstep := lb.trace[pos]; assert ActionTuple(ls, ls', lstep) in wr.lspec.next; assert ls in wr.inv; var hstep := wr.step_refiner(ls, lstep); htrace := htrace + [hstep]; hstates := hstates + [wr.converter(ls')]; assert hstates == MapSeqToSeq(lb.states[..pos + 2], wr.converter); lh_map := lh_map + [RefinementRange(|hstates|-1, |hstates|-1)]; pos := pos + 1; } hb := AnnotatedBehavior(hstates, htrace); lemma_LRefinesH(lb, hb, wr, lh_map); } } ================================================ FILE: Armada/strategies/starweakening/StarWeakeningSpec.i.dfy ================================================ include "../refinement/AnnotatedBehavior.i.dfy" include "../../spec/refinement.s.dfy" include "../invariants.i.dfy" module StarWeakeningSpecModule { import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule datatype StarWeakeningRequest = StarWeakeningRequest( lspec:AnnotatedBehaviorSpec, hspec:AnnotatedBehaviorSpec, relation:RefinementRelation, inv:iset, converter:(LState)->HState, step_refiner:(LState, LStep) --> HStep ) predicate ConvertingSatisfiesRelation(wr:StarWeakeningRequest) { forall ls :: ls in wr.inv ==> RefinementPair(ls, wr.converter(ls)) in wr.relation } predicate AllActionsLiftableStarWeakened(wr:StarWeakeningRequest) { forall s, s', lstep :: && ActionTuple(s, s', lstep) in wr.lspec.next && s in wr.inv ==> wr.step_refiner.requires(s, lstep) && ActionTuple(wr.converter(s), wr.converter(s'), wr.step_refiner(s, lstep)) in wr.hspec.next } predicate InitStatesEquivalent(wr:StarWeakeningRequest) { forall initial_ls | initial_ls in wr.lspec.init :: wr.converter(initial_ls) in wr.hspec.init } predicate ValidStarWeakeningRequest(wr:StarWeakeningRequest) { && InitStatesEquivalent(wr) && IsInvariantOfSpec(wr.inv, wr.lspec) && AllActionsLiftableStarWeakened(wr) && ConvertingSatisfiesRelation(wr) } } ================================================ FILE: Armada/strategies/tsoelimination/TSOElimination.i.dfy ================================================ include "TSOEliminationLemmas.i.dfy" module TSOEliminationModule { import opened AnnotatedBehaviorModule import opened GeneralRefinementModule import opened InvariantsModule import opened util_collections_seqs_s import opened TSOEliminationSpecModule import opened TSOEliminationLemmasModule lemma lemma_PerformTSOElimination( ter:TSOEliminationRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidTSOEliminationRequest(ter) requires AnnotatedBehaviorSatisfiesSpec(lb, ter.lspec) ensures BehaviorRefinesBehavior(lb.states, hb.states, ter.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, ter.hspec) { var hstates := [ter.initial_state_refiner(lb.states[0])]; var htrace := []; var lh_map := [RefinementRange(0, 0)]; var pos := 0; lemma_InvariantPredicateHoldsAtStep(lb, 0, ter.lspec, ter.inv); while pos < |lb.states| - 1 invariant 0 <= pos < |lb.states| invariant |htrace| == |hstates|-1 invariant hstates[0] in ter.hspec.init invariant forall i :: 0 <= i < |htrace| ==> ActionTuple(hstates[i], hstates[i+1], htrace[i]) in ter.hspec.next invariant ter.intermediate_relation(lb.states[pos], last(hstates)) invariant BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos+1], hstates, ter.relation, lh_map) { hstates, htrace, lh_map := lemma_PerformOneStepOfTSOElimination(ter, lb, pos, hstates, htrace, lh_map); pos := pos + 1; } hb := AnnotatedBehavior(hstates, htrace); assert lb.states[..pos+1] == lb.states; assert BehaviorRefinesBehaviorUsingRefinementMap(lb.states, hb.states, ter.relation, lh_map); } } ================================================ FILE: Armada/strategies/tsoelimination/TSOEliminationLemmas.i.dfy ================================================ include "TSOEliminationSpec.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../../util/collections/seqs.i.dfy" module TSOEliminationLemmasModule { import opened TSOEliminationSpecModule import opened util_collections_seqs_s import opened util_collections_seqs_i import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule import opened RefinementConvolutionModule lemma lemma_IntermediateRelationImpliesRefinementRelation( ter:TSOEliminationRequest, ls:LState, hs:HState ) requires ValidTSOEliminationRequest(ter) requires ter.inv(ls) requires ter.intermediate_relation(ls, hs) ensures RefinementPair(ls, hs) in ter.relation { } lemma lemma_PerformOneStepOfTSOElimination( ter:TSOEliminationRequest, lb:AnnotatedBehavior, pos:int, hstates:seq, htrace:seq, lh_map:RefinementMap ) returns ( hstates':seq, htrace':seq, lh_map':RefinementMap ) requires ValidTSOEliminationRequest(ter) requires AnnotatedBehaviorSatisfiesSpec(lb, ter.lspec) requires 0 <= pos < |lb.states| - 1 requires |htrace| == |hstates|-1 requires hstates[0] in ter.hspec.init requires forall i :: 0 <= i < |htrace| ==> ActionTuple(hstates[i], hstates[i+1], htrace[i]) in ter.hspec.next requires ter.intermediate_relation(lb.states[pos], last(hstates)) requires BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos+1], hstates, ter.relation, lh_map) ensures |htrace'| == |hstates'|-1 ensures hstates'[0] == hstates[0] ensures forall i :: 0 <= i < |htrace'| ==> ActionTuple(hstates'[i], hstates'[i+1], htrace'[i]) in ter.hspec.next ensures ter.intermediate_relation(lb.states[pos+1], last(hstates')) ensures BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos+2], hstates', ter.relation, lh_map') { var ls := lb.states[pos]; var ls' := lb.states[pos+1]; var lstep := lb.trace[pos]; var hs := last(hstates); assert ActionTuple(ls, ls', lstep) in ter.lspec.next; lemma_InvariantPredicateHoldsAtStep(lb, pos, ter.lspec, ter.inv); assert ter.inv(ls); assert ter.intermediate_relation(ls, hs); var hstep := ter.step_refiner(ls, hs, lstep); var hs' := ter.next_state(hs, hstep); assert ActionTuple(hs, hs', hstep) in ter.hspec.next; assert ter.intermediate_relation(ls', hs'); hstates' := hstates + [hs']; htrace' := htrace + [hstep]; lh_map' := lh_map + [RefinementRange(|hstates|, |hstates|)]; lemma_InvariantPredicateHoldsAtStep(lb, pos+1, ter.lspec, ter.inv); lemma_IntermediateRelationImpliesRefinementRelation(ter, ls', hs'); var lstates := lb.states[..pos+2]; forall i, j {:trigger RefinementPair(lstates[i], hstates'[j]) in ter.relation} | 0 <= i < |lstates| && lh_map'[i].first <= j <= lh_map'[i].last ensures RefinementPair(lstates[i], hstates'[j]) in ter.relation { } forall i | 0 <= i < |lh_map'|-1 ensures lh_map'[i+1].first == lh_map'[i].last || lh_map'[i+1].first == lh_map'[i].last + 1 { if i < |lh_map|-1 { assert lh_map'[i] == lh_map[i]; assert lh_map'[i+1] == lh_map[i+1]; } else { assert lh_map'[i] == last(lh_map); assert last(lh_map).last == |hstates|-1; assert lh_map'[i+1].first == |hstates|; assert lh_map'[i+1].first == lh_map'[i].last + 1; } } forall i | 0 <= i < |htrace'| ensures ActionTuple(hstates'[i], hstates'[i+1], htrace'[i]) in ter.hspec.next { if i < |htrace| { assert hstates'[i] == hstates[i]; assert hstates'[i+1] == hstates[i+1]; assert htrace'[i] == htrace[i]; } else { assert i == |htrace| == |hstates|-1; assert hstates[i] == last(hstates); assert hstates'[i] == hstates[i] == last(hstates) == hs; assert hstates'[i+1] == hs'; assert htrace'[i] == hstep; } } } } ================================================ FILE: Armada/strategies/tsoelimination/TSOEliminationSpec.i.dfy ================================================ include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../../util/option.s.dfy" include "../../spec/refinement.s.dfy" module TSOEliminationSpecModule { import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule import opened util_option_s datatype TSOEliminationRequest = TSOEliminationRequest( lspec:AnnotatedBehaviorSpec, hspec:AnnotatedBehaviorSpec, relation:RefinementRelation, inv:LState->bool, initial_state_refiner:LState->HState, step_refiner:(LState,HState,LStep)->HStep, next_state:(HState,HStep)->HState, // computes next state given current state and next step intermediate_relation:(LState, HState)->bool ) predicate InitialConditionsHold( ter:TSOEliminationRequest ) { forall ls :: ls in ter.lspec.init ==> var hs := ter.initial_state_refiner(ls); && hs in ter.hspec.init && ter.intermediate_relation(ls, hs) } predicate IntermediateRelationImpliesRelation( ter:TSOEliminationRequest ) { forall ls, hs :: ter.intermediate_relation(ls, hs) ==> RefinementPair(ls, hs) in ter.relation } predicate StepMaintainsRelation( ter:TSOEliminationRequest ) { forall ls, ls', lstep, hs :: && ter.inv(ls) && ter.intermediate_relation(ls, hs) && ActionTuple(ls, ls', lstep) in ter.lspec.next ==> var hstep := ter.step_refiner(ls, hs, lstep); var hs' := ter.next_state(hs, hstep); && ActionTuple(hs, hs', hstep) in ter.hspec.next && ter.intermediate_relation(ls', hs') } predicate ValidTSOEliminationRequest( ter:TSOEliminationRequest ) { && IsInvariantPredicateOfSpec(ter.inv, ter.lspec) && InitialConditionsHold(ter) && IntermediateRelationImpliesRelation(ter) && StepMaintainsRelation(ter) } } ================================================ FILE: Armada/strategies/varhiding/VarHiding.i.dfy ================================================ include "VarHidingSpec.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../../util/collections/seqs.i.dfy" module VarHidingModule { import opened VarHidingSpecModule import opened util_collections_maps_s import opened util_collections_seqs_s import opened util_collections_seqs_i import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule import opened RefinementConvolutionModule lemma lemma_PerformVarHiding( vr:VarHidingRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidVarHidingRequest(vr) requires AnnotatedBehaviorSatisfiesSpec(lb, vr.lspec) ensures BehaviorRefinesBehavior(lb.states, hb.states, vr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, vr.hspec) { var hstates := [vr.hider(lb.states[0])]; var htrace := []; var lh_map := [RefinementRange(0, 0)]; var pos := 0; lemma_InvariantPredicateHoldsAtStep(lb, 0, vr.lspec, vr.inv); while pos < |lb.states| - 1 invariant 0 <= pos < |lb.states| invariant |htrace| == |hstates|-1 invariant hstates[0] in vr.hspec.init invariant forall i :: 0 <= i < |htrace| ==> ActionTuple(hstates[i], hstates[i+1], htrace[i]) in vr.hspec.next invariant last(hstates) == vr.hider(lb.states[pos]) invariant BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos+1], hstates, vr.relation, lh_map) { lemma_InvariantPredicateHoldsAtStep(lb, pos, vr.lspec, vr.inv); lemma_InvariantPredicateHoldsAtStep(lb, pos+1, vr.lspec, vr.inv); var s := lb.states[pos]; var s' := lb.states[pos+1]; var lstep := lb.trace[pos]; assert ActionTuple(s, s', lstep) in vr.lspec.next; assert vr.inv(s); if (vr.hider(s) != vr.hider(s')) { var hstep := vr.step_refiner(s, lstep); htrace := htrace + [hstep]; hstates := hstates + [vr.hider(s')]; } lh_map := lh_map + [RefinementRange(|hstates|-1, |hstates|-1)]; pos := pos + 1; var lstates := lb.states[..pos+1]; forall i, j {:trigger RefinementPair(lstates[i], hstates[j]) in vr.relation} | 0 <= i < |lstates| && lh_map[i].first <= j <= lh_map[i].last ensures RefinementPair(lstates[i], hstates[j]) in vr.relation { } } hb := AnnotatedBehavior(hstates, htrace); assert lb.states[..pos+1] == lb.states; assert BehaviorRefinesBehaviorUsingRefinementMap(lb.states, hb.states, vr.relation, lh_map); } } ================================================ FILE: Armada/strategies/varhiding/VarHidingSpec.i.dfy ================================================ include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../../spec/refinement.s.dfy" include "../../util/collections/maps.s.dfy" module VarHidingSpecModule { import opened util_collections_maps_s import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule datatype VarHidingRequest = VarHidingRequest( lspec:AnnotatedBehaviorSpec, hspec:AnnotatedBehaviorSpec, relation:RefinementRelation, inv:LState->bool, hider:LState->HState, step_refiner:(LState,LStep)->HStep ) predicate AllActionsLiftableWithoutVariable( vr:VarHidingRequest) { forall s, s', lstep :: && ActionTuple(s, s', lstep) in vr.lspec.next && vr.inv(s) && vr.hider(s) != vr.hider(s') ==> ActionTuple(vr.hider(s), vr.hider(s'), vr.step_refiner(s, lstep)) in vr.hspec.next } predicate HidingSatisfiesRelation(vr:VarHidingRequest) { forall s :: vr.inv(s) ==> RefinementPair(s, vr.hider(s)) in vr.relation } predicate HidingPreservesInit(vr:VarHidingRequest) { forall s :: s in vr.lspec.init ==> vr.hider(s) in vr.hspec.init } predicate ValidVarHidingRequest(vr:VarHidingRequest) { && IsInvariantPredicateOfSpec(vr.inv, vr.lspec) && AllActionsLiftableWithoutVariable(vr) && HidingSatisfiesRelation(vr) && HidingPreservesInit(vr) } } ================================================ FILE: Armada/strategies/varintro/VarIntro.i.dfy ================================================ include "VarIntroSpec.i.dfy" include "../refinement/RefinementConvolution.i.dfy" include "../../util/collections/seqs.s.dfy" include "../../util/collections/seqs.i.dfy" module VarIntroModule { import opened VarIntroSpecModule import opened util_collections_maps_s import opened util_collections_seqs_s import opened util_collections_seqs_i import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule import opened RefinementConvolutionModule lemma lemma_PerformVarIntroOneStepCaseAtNewInstruction( vr:VarIntroRequest, lb:AnnotatedBehavior, hb:AnnotatedBehavior, lh_map:RefinementMap, pos:int ) returns ( hb':AnnotatedBehavior, lh_map':RefinementMap ) requires ValidVarIntroRequest(vr) requires AnnotatedBehaviorSatisfiesSpec(lb, vr.lspec) requires 1 <= pos < |lb.states| requires |hb.states| > 0 requires vr.at_new_instruction(last(hb.states)) requires BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos], hb.states, vr.relation, lh_map) requires AnnotatedBehaviorSatisfiesSpec(hb, vr.hspec) requires lb.states[pos-1] == vr.hider(last(hb.states)) requires vr.inv(last(hb.states)) ensures |hb'.states| > 0 ensures BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos+1], hb'.states, vr.relation, lh_map') ensures AnnotatedBehaviorSatisfiesSpec(hb', vr.hspec) ensures lb.states[pos] == vr.hider(last(hb'.states)) ensures vr.inv(last(hb'.states)) decreases vr.progress_measure(last(hb.states)), 0 { var hs := last(hb.states); var hstep := vr.next_step(hs); var hs_mid := vr.next_state(hs, hstep); assert vr.inv(hs_mid); assert vr.hider(hs) == vr.hider(hs_mid); assert ActionTuple(hs, hs_mid, hstep) in vr.hspec.next; assert 0 <= vr.progress_measure(hs_mid) < vr.progress_measure(hs); var htrace_mid := hb.trace + [hstep]; var hstates_mid := hb.states + [hs_mid]; var hb_mid := AnnotatedBehavior(hstates_mid, htrace_mid); var lh_map_mid := lh_map[|lh_map|-1 := last(lh_map).(last := |hb.states|)]; hb', lh_map' := lemma_PerformVarIntroOneStep(vr, lb, hb_mid, lh_map_mid, pos); } lemma lemma_PerformVarIntroOneStepCaseNotAtNewInstruction( vr:VarIntroRequest, lb:AnnotatedBehavior, hb:AnnotatedBehavior, lh_map:RefinementMap, pos:int ) returns ( hb':AnnotatedBehavior, lh_map':RefinementMap ) requires ValidVarIntroRequest(vr) requires AnnotatedBehaviorSatisfiesSpec(lb, vr.lspec) requires 1 <= pos < |lb.states| requires |hb.states| > 0 requires !vr.at_new_instruction(last(hb.states)) requires BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos], hb.states, vr.relation, lh_map) requires AnnotatedBehaviorSatisfiesSpec(hb, vr.hspec) requires lb.states[pos-1] == vr.hider(last(hb.states)) requires vr.inv(last(hb.states)) ensures |hb'.states| > 0 ensures BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos+1], hb'.states, vr.relation, lh_map') ensures AnnotatedBehaviorSatisfiesSpec(hb', vr.hspec) ensures lb.states[pos] == vr.hider(last(hb'.states)) ensures vr.inv(last(hb'.states)) decreases vr.progress_measure(last(hb.states)) { var prev := pos-1; var ls := lb.states[prev]; var ls' := lb.states[prev+1]; var lstep := lb.trace[prev]; assert ActionTuple(ls, ls', lstep) in vr.lspec.next; var hs := last(hb.states); var hstep := vr.step_mapper(lstep); var hs' := vr.next_state(hs, hstep); assert vr.inv(hs'); assert ls' == vr.hider(hs'); assert ActionTuple(hs, hs', hstep) in vr.hspec.next; var htrace' := hb.trace + [hstep]; var hstates' := hb.states + [hs']; lh_map' := lh_map + [RefinementRange(|hb.states|, |hb.states|)]; hb' := AnnotatedBehavior(hstates', htrace'); var lstates' := lb.states[..pos+1]; forall i, j {:trigger RefinementPair(lstates'[i], hstates'[j]) in vr.relation} | 0 <= i < |lstates'| && lh_map'[i].first <= j <= lh_map'[i].last ensures RefinementPair(lstates'[i], hstates'[j]) in vr.relation { } } lemma lemma_PerformVarIntroOneStep( vr:VarIntroRequest, lb:AnnotatedBehavior, hb:AnnotatedBehavior, lh_map:RefinementMap, pos:int ) returns ( hb':AnnotatedBehavior, lh_map':RefinementMap ) requires ValidVarIntroRequest(vr) requires AnnotatedBehaviorSatisfiesSpec(lb, vr.lspec) requires 1 <= pos < |lb.states| requires |hb.states| > 0 requires BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos], hb.states, vr.relation, lh_map) requires AnnotatedBehaviorSatisfiesSpec(hb, vr.hspec) requires lb.states[pos-1] == vr.hider(last(hb.states)) requires vr.inv(last(hb.states)) ensures |hb'.states| > 0 ensures BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos+1], hb'.states, vr.relation, lh_map') ensures AnnotatedBehaviorSatisfiesSpec(hb', vr.hspec) ensures lb.states[pos] == vr.hider(last(hb'.states)) ensures vr.inv(last(hb'.states)) decreases vr.progress_measure(last(hb.states)), 1 { var ls := lb.states[pos-1]; var ls' := lb.states[pos-1+1]; var hs := last(hb.states); if vr.at_new_instruction(hs) { hb', lh_map' := lemma_PerformVarIntroOneStepCaseAtNewInstruction(vr, lb, hb, lh_map, pos); } else { hb', lh_map' := lemma_PerformVarIntroOneStepCaseNotAtNewInstruction(vr, lb, hb, lh_map, pos); } } lemma lemma_PerformVarIntro( vr:VarIntroRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidVarIntroRequest(vr) requires AnnotatedBehaviorSatisfiesSpec(lb, vr.lspec) ensures BehaviorRefinesBehavior(lb.states, hb.states, vr.relation) ensures AnnotatedBehaviorSatisfiesSpec(hb, vr.hspec) { var ls := lb.states[0]; assert ls in vr.lspec.init; var hs := vr.revealer(ls); assert vr.hider(hs) == ls && vr.inv(hs) && hs in vr.hspec.init; var hstates := [hs]; var htrace := []; hb := AnnotatedBehavior(hstates, htrace); var pos := 1; var lh_map := [RefinementRange(0, 0)]; while pos < |lb.states| invariant 1 <= pos <= |lb.states| invariant |hb.states| > 0 invariant BehaviorRefinesBehaviorUsingRefinementMap(lb.states[..pos], hb.states, vr.relation, lh_map) invariant AnnotatedBehaviorSatisfiesSpec(hb, vr.hspec) invariant lb.states[pos-1] == vr.hider(last(hb.states)) invariant vr.inv(last(hb.states)) { hb, lh_map := lemma_PerformVarIntroOneStep(vr, lb, hb, lh_map, pos); pos := pos + 1; } assert BehaviorRefinesBehaviorUsingRefinementMap(lb.states, hb.states, vr.relation, lh_map); } } ================================================ FILE: Armada/strategies/varintro/VarIntroSpec.i.dfy ================================================ include "../refinement/AnnotatedBehavior.i.dfy" include "../invariants.i.dfy" include "../../spec/refinement.s.dfy" include "../../util/collections/maps.s.dfy" module VarIntroSpecModule { import opened util_collections_maps_s import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule datatype VarIntroRequest = VarIntroRequest( lspec:AnnotatedBehaviorSpec, hspec:AnnotatedBehaviorSpec, relation:RefinementRelation, inv:HState->bool, // invariant of the high-level state hider:HState->LState, // hides values of introduced variables revealer:LState->HState, // only matters for initial state, introduces values for introduced variables step_mapper:LStep->HStep, // maps a low-level step to a high-level one next_state:(HState,HStep)->HState, // computes next state given current state and next step at_new_instruction:HState->bool, // whether the PC is just before a new instruction (an assignment to an introduced variable) next_step:HState->HStep, // only matters when at_new_instruction, returns the step for that new instruction progress_measure:HState->int // only matters when at_new_instruction, must decrease by executing the new instruction ) predicate WhenAtNewInstructionNextStepMakesProgress( vr:VarIntroRequest) { forall hs :: vr.inv(hs) && vr.at_new_instruction(hs) ==> var hstep := vr.next_step(hs); var hs' := vr.next_state(hs, hstep); && vr.inv(hs') && vr.hider(hs) == vr.hider(hs') && ActionTuple(hs, hs', hstep) in vr.hspec.next && 0 <= vr.progress_measure(hs') < vr.progress_measure(hs) } predicate WhenNotAtNewInstructionActionIsLiftable( vr:VarIntroRequest) { forall ls, ls', hs, lstep :: && ActionTuple(ls, ls', lstep) in vr.lspec.next && vr.inv(hs) && ls == vr.hider(hs) && !vr.at_new_instruction(hs) ==> var hstep := vr.step_mapper(lstep); var hs' := vr.next_state(hs, hstep); && vr.inv(hs') && ls' == vr.hider(hs') && ActionTuple(hs, hs', hstep) in vr.hspec.next } predicate IntroSatisfiesRelation(vr:VarIntroRequest) { forall s :: vr.inv(s) ==> RefinementPair(vr.hider(s), s) in vr.relation } predicate IntroPreservesInit(vr:VarIntroRequest) { forall ls :: ls in vr.lspec.init ==> var hs := vr.revealer(ls); vr.hider(hs) == ls && vr.inv(hs) && hs in vr.hspec.init } predicate ValidVarIntroRequest(vr:VarIntroRequest) { && WhenAtNewInstructionNextStepMakesProgress(vr) && WhenNotAtNewInstructionActionIsLiftable(vr) && IntroSatisfiesRelation(vr) && IntroPreservesInit(vr) } } ================================================ FILE: Armada/strategies/weakening/Weakening.i.dfy ================================================ include "WeakeningSpec.i.dfy" include "../../util/collections/seqs.i.dfy" include "../../spec/refinement.s.dfy" module WeakeningModule { import opened WeakeningSpecModule import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule import opened util_collections_seqs_i import opened util_collections_seqs_s lemma lemma_LRefinesH ( lb:AnnotatedBehavior, hb:AnnotatedBehavior, wr:WeakeningRequest, lh_map:RefinementMap) requires ValidWeakeningRequest(wr) requires AnnotatedBehaviorSatisfiesSpec(lb, wr.lspec) requires hb.states == MapSeqToSeq(lb.states, wr.converter); requires |lh_map| == |lb.states| requires forall i :: 0 <= i < |lh_map| ==> lh_map[i] == RefinementRange(i, i) ensures BehaviorRefinesBehaviorUsingRefinementMap(lb.states, hb.states, wr.relation, lh_map) { assert hb.states == MapSeqToSeq(lb.states, wr.converter); lemma_InvariantHoldsAtStep(lb, 0, wr.lspec, wr.inv); var i := 0; while i < |lb.states| invariant 0 <= i <= |lb.states| invariant forall j :: 0 <= j < i ==> RefinementPair(lb.states[j], hb.states[j]) in wr.relation; { lemma_InvariantHoldsAtStep(lb, i, wr.lspec, wr.inv); assert RefinementPair(lb.states[i], hb.states[i]) in wr.relation; i := i + 1; } } lemma lemma_PerformWeakening( wr:WeakeningRequest, lb:AnnotatedBehavior ) returns ( hb:AnnotatedBehavior ) requires ValidWeakeningRequest(wr) requires AnnotatedBehaviorSatisfiesSpec(lb, wr.lspec) ensures hb.states == MapSeqToSeq(lb.states, wr.converter) ensures AnnotatedBehaviorSatisfiesSpec(hb, wr.hspec) ensures BehaviorRefinesBehavior(lb.states, hb.states, wr.relation) { var hstates := [wr.converter(lb.states[0])]; var htrace := []; var lh_map := [RefinementRange(0, 0)]; var pos := 0; lemma_InvariantHoldsAtStep(lb, 0, wr.lspec, wr.inv); while pos < |lb.states| - 1 invariant 0 <= pos < |lb.states| invariant |htrace| == |hstates|-1 invariant hstates[0] in wr.hspec.init invariant forall i :: 0 <= i < |htrace| ==> ActionTuple(hstates[i], hstates[i+1], htrace[i]) in wr.hspec.next invariant last(hstates) == wr.converter(lb.states[pos]) invariant hstates == MapSeqToSeq(lb.states[..pos + 1], wr.converter) invariant forall i :: 0 <= i < |lh_map| ==> lh_map[i] == RefinementRange(i, i) invariant |lh_map| == pos + 1 { lemma_InvariantHoldsAtStep(lb, pos, wr.lspec, wr.inv); lemma_InvariantHoldsAtStep(lb, pos+1, wr.lspec, wr.inv); var ls := lb.states[pos]; var ls' := lb.states[pos+1]; var lstep := lb.trace[pos]; assert ActionTuple(ls, ls', lstep) in wr.lspec.next; assert ls in wr.inv; var hstep := wr.step_refiner(lstep); htrace := htrace + [hstep]; hstates := hstates + [wr.converter(ls')]; assert hstates == MapSeqToSeq(lb.states[..pos + 2], wr.converter); lh_map := lh_map + [RefinementRange(|hstates|-1, |hstates|-1)]; pos := pos + 1; } hb := AnnotatedBehavior(hstates, htrace); lemma_LRefinesH(lb, hb, wr, lh_map); } } ================================================ FILE: Armada/strategies/weakening/WeakeningSpec.i.dfy ================================================ include "../refinement/AnnotatedBehavior.i.dfy" include "../../spec/refinement.s.dfy" include "../invariants.i.dfy" module WeakeningSpecModule { import opened AnnotatedBehaviorModule import opened InvariantsModule import opened GeneralRefinementModule datatype WeakeningRequest = WeakeningRequest( lspec:AnnotatedBehaviorSpec, hspec:AnnotatedBehaviorSpec, relation:RefinementRelation, inv:iset, converter:(LState)->HState, step_refiner:(LStep)->HStep ) predicate ConvertingSatisfiesRelation(wr:WeakeningRequest) { forall ls :: ls in wr.inv ==> RefinementPair(ls, wr.converter(ls)) in wr.relation } predicate AllActionsLiftableWeakened(wr:WeakeningRequest) { forall s, s', lstep :: && ActionTuple(s, s', lstep) in wr.lspec.next && s in wr.inv ==> ActionTuple(wr.converter(s), wr.converter(s'), wr.step_refiner(lstep)) in wr.hspec.next } predicate InitStatesEquivalent(wr:WeakeningRequest) { forall initial_ls | initial_ls in wr.lspec.init :: wr.converter(initial_ls) in wr.hspec.init } predicate ValidWeakeningRequest(wr:WeakeningRequest) { && InitStatesEquivalent(wr) && IsInvariantOfSpec(wr.inv, wr.lspec) && AllActionsLiftableWeakened(wr) && ConvertingSatisfiesRelation(wr) } } ================================================ FILE: Armada/util/collections/MapSum.i.dfy ================================================ include "maps.s.dfy" include "sets.i.dfy" module util_collections_MapSum_i { import opened util_collections_maps_s import opened util_collections_sets_i lemma MapNonEmptyDomainNonEmpty(m: map) requires |m| > 0 ensures |mapdomain(m)| > 0 { var k :| k in m; assert k in mapdomain(m); } function chooseone(m: set): T requires |m| > 0 { var k :| k in m; k } function MapSum(m: map, f:V->int): int { if |m| == 0 then 0 else MapNonEmptyDomainNonEmpty(m); var k := chooseone(mapdomain(m)); var m0 := mapremove(m, k); f(m[k]) + MapSum(m0, f) } lemma lemma_MapSumNonNegativeIfAllElementsNonNegative(m: map, f:V->int) requires forall k :: k in m ==> f(m[k]) >= 0 ensures MapSum(m, f) >= 0 { } lemma lemma_MapSumLessIfOneElementLessAndRestSame(m1:map, m2:map, k: K, f:V->int) requires forall k :: k in m1 <==> k in m2 requires k in m1 requires forall k0 :: k0 != k && k0 in m1 ==> m1[k0] == m2[k0] requires f(m1[k]) < f(m2[k]) ensures MapSum(m1, f) < MapSum(m2, f) { assert (mapdomain(m1) == mapdomain(m2)); if |m1| == 0 { } else { MapNonEmptyDomainNonEmpty(m1); MapNonEmptyDomainNonEmpty(m2); var k0 := chooseone(mapdomain(m1)); if k0 == k { assert mapremove(m1, k0) == mapremove(m2, k0); } else { lemma_MapSumLessIfOneElementLessAndRestSame(mapremove(m1, k0), mapremove(m2, k0), k, f); } } } lemma lemma_MapSumLessOrEqualIfOneElementLessOrEqualAndRestSame(m1:map, m2:map, k: K, f:V->int) requires forall k :: k in m1 <==> k in m2 requires k in m1 requires forall k0 :: k0 != k && k0 in m1 ==> m1[k0] == m2[k0] requires f(m1[k]) <= f(m2[k]) ensures MapSum(m1, f) <= MapSum(m2, f) { assert (mapdomain(m1) == mapdomain(m2)); if |m1| == 0 { } else { MapNonEmptyDomainNonEmpty(m1); MapNonEmptyDomainNonEmpty(m2); var k0 := chooseone(mapdomain(m1)); if k0 == k { assert mapremove(m1, k0) == mapremove(m2, k0); } else { lemma_MapSumLessOrEqualIfOneElementLessOrEqualAndRestSame(mapremove(m1, k0), mapremove(m2, k0), k, f); } } } } ================================================ FILE: Armada/util/collections/SeqSum.i.dfy ================================================ module util_collections_SeqSum_i { function SeqSum(s:seq) : int { if |s| == 0 then 0 else s[0] + SeqSum(s[1..]) } lemma lemma_SeqSumNonNegativeIfAllElementsNonNegative(s:seq) requires forall i :: 0 <= i < |s| ==> s[i] >= 0 ensures SeqSum(s) >= 0 { } lemma lemma_SeqSumLessIfOneElementLessAndRestSame(s1:seq, s2:seq, pos:int) requires |s1| == |s2| requires 0 <= pos < |s1| requires forall i :: 0 <= i < |s1| && i != pos ==> s1[i] == s2[i] requires s1[pos] < s2[pos] ensures SeqSum(s1) < SeqSum(s2) { if |s1| == 0 { } else if pos == 0 { assert s1[1..] == s2[1..]; } else { lemma_SeqSumLessIfOneElementLessAndRestSame(s1[1..], s2[1..], pos-1); } } } ================================================ FILE: Armada/util/collections/maps.i.dfy ================================================ include "sets.i.dfy" module util_collections_maps_i { import opened util_collections_sets_i function AddSetToMap(m: map, ks: set, v: V) : (m': map) // requires forall k :: k in ks ==> !(k in m) ensures forall k :: k in m ==> k in m' && m'[k] == m[k] ensures forall k :: k in ks && k !in m ==> k in m' && m'[k] == v { map k | k in ks + m.Keys :: if k in m then m[k] else v } function MapMapToMap_internal(m: map, f: V1->V2) : (m': map) ensures m'.Keys == m.Keys ensures forall k :: k in m' ==> m'[k] == f(m[k]) { map k | k in m :: f(m[k]) } lemma lemma_MapMapToMapMaintainsSize(m: map, f:V1->V2) ensures |MapMapToMap_internal(m, f)| == |m| { var m' := MapMapToMap_internal(m, f); var keys := set k | k in m; var keys' := set k | k in m'; assert keys' == keys; calc { |m'|; |keys'|; |keys|; |m|; } } function MapMapToMap(m:map, f:V1->V2) : (m':map) ensures m'.Keys == m.Keys ensures forall k :: k in m' ==> m'[k] == f(m[k]) ensures |m'| == |m| { lemma_MapMapToMapMaintainsSize(m, f); MapMapToMap_internal(m, f) } function Map2MapToMap_internal(m:map, fk:K1->K2, fk_inv:K2->K1, fv:V1->V2) : (m':map) requires Inverses(fk, fk_inv) { var keys' := MapSetToSet(m.Keys, fk); map k | k in keys' :: fv(m[fk_inv(k)]) } lemma lemma_SetExtensionality(s1:set, s2:set) requires forall x :: x in s1 <==> x in s2 ensures s1 == s2 { } lemma lemma_Map2MapToMap(m:map, fk:K1->K2, fk_inv:K2->K1, fv:V1->V2, m':map) requires Inverses(fk, fk_inv) requires m' == Map2MapToMap_internal(m, fk, fk_inv, fv) ensures m'.Keys == MapSetToSet(m.Keys, fk) ensures forall k :: k in m' ==> m'[k] == fv(m[fk_inv(k)]) { var keys' := MapSetToSet(m.Keys, fk); forall k | k in m' ensures k in keys' ensures m'[k] == fv(m[fk_inv(k)]) { } lemma_SetExtensionality(m'.Keys, keys'); } function Map2MapToMap(m:map, fk:K1->K2, fk_inv:K2->K1, fv:V1->V2) : (m':map) requires Inverses(fk, fk_inv) ensures m'.Keys == MapSetToSet(m.Keys, fk) ensures forall k :: k in m' ==> m'[k] == fv(m[fk_inv(k)]) { var m' := Map2MapToMap_internal(m, fk, fk_inv, fv); lemma_Map2MapToMap(m, fk, fk_inv, fv, m'); m' } predicate MapHasUniqueKey(m:map, k:T) { && k in m && (forall k' :: k' in m ==> k' == k) } lemma EmptyMapPredicateInvalid(m:map, P:T->bool) requires |set x | x in m && P(m[x])| == 0 ensures forall x :: x in m ==> ! P(m[x]) { forall x | x in m ensures ! P(m[x]) { if P(m[x]) { assert x in (set x | x in m && P(m[x])); lemma_EmptySetCantHaveMember(x, set x | x in m && P(m[x])); } } } lemma lemma_IfMapKeysMatchThenCardinalitiesMatch(m1:map, m2:map) requires forall k :: k in m1 <==> k in m2 ensures |m1| == |m2| { var keys1 := set k | k in m1; var keys2 := set k | k in m2; assert m1.Keys == keys1; assert m2.Keys == keys2; assert keys1 == keys2; calc { |m1|; |m1.Keys|; |keys1|; |keys2|; |m2.Keys|; |m2|; } } } ================================================ FILE: Armada/util/collections/maps.s.dfy ================================================ module util_collections_maps_s { function mapdomain(m:map) : set { set k | k in m :: k } function mapremove(m:map, k:KT) : map { map ki | ki in m && ki != k :: m[ki] } predicate maptotal(m:map) { forall k {:trigger m[k]}{:trigger k in m} :: k in m } predicate imaptotal(m:imap) { forall k {:trigger m[k]}{:trigger k in m} :: k in m } } ================================================ FILE: Armada/util/collections/seqs.i.dfy ================================================ include "../option.s.dfy" include "seqs.s.dfy" module util_collections_seqs_i { import opened util_option_s import opened util_collections_seqs_s function {:opaque} ConvertMapToSeq(n:int, m:map) : seq requires n >= 0; requires forall i {:trigger i in m} :: 0 <= i < n ==> i in m; ensures |ConvertMapToSeq(n, m)| == n; ensures var s := ConvertMapToSeq(n, m); forall i {:trigger s[i]} :: 0 <= i < n ==> s[i] == m[i]; { if n == 0 then [] else ConvertMapToSeq(n-1, m) + [m[n-1]] } function {:opaque} MapSeqToSeq(s:seq, f:T->U) : (s':seq) ensures |s'| == |s| ensures forall i {:trigger s'[i]} {:trigger f(s[i])} :: 0 <= i < |s| ==> s'[i] == f(s[i]) { if |s| == 0 then [] else [f(s[0])] + MapSeqToSeq(s[1..], f) } lemma lemma_MapSeqToSeqDrop(s:seq, f:T->U, n:int) requires 0 <= n <= |s| ensures MapSeqToSeq(s, f)[n..] == MapSeqToSeq(s[n..], f) { } lemma lemma_MapSeqToSeqTake(s:seq, f:T->U, n:int) requires 0 <= n <= |s| ensures MapSeqToSeq(s, f)[..n] == MapSeqToSeq(s[..n], f) { } function FilterSeqToSeq(s:seq, f:T->bool) : (s':seq) ensures forall x :: x in s' ==> f(x) { if |s| == 0 then [] else if f(s[0]) then [s[0]] + FilterSeqToSeq(s[1..], f) else FilterSeqToSeq(s[1..], f) } lemma FilterSeqToSeqDecreasesLength(s:seq, f:T->bool) ensures |FilterSeqToSeq(s, f)| <= |s| { } function FilterMapSeqToSeq(s:seq, f:T->Option) : (s':seq) { if |s| == 0 then [] else if f(s[0]).Some? then [f(s[0]).v] + FilterMapSeqToSeq(s[1..], f) else FilterMapSeqToSeq(s[1..], f) } lemma lemma_FilterMapSeqsToSeq(s1:seq, s2:seq, f:T->Option) ensures FilterMapSeqToSeq(s1, f) + FilterMapSeqToSeq(s2, f) == FilterMapSeqToSeq(s1 + s2, f) { if |s1| == 0 { assert s2 == s1 + s2; } else { assert s1 + s2 == [s1[0]] + (s1[1..] + s2); } } lemma FilterMapSeqToSeq_FilterEmpty(s: seq, f: T->Option) requires forall e :: e in s ==> f(e).None? ensures FilterMapSeqToSeq(s, f) == [] { if |s| == 0 { } else { if (f(s[0]).Some?) { } else { FilterMapSeqToSeq_FilterEmpty(s[1..], f); } } } function RepeatingValue(x:T, n:int) : (s:seq) requires n >= 0 ensures |s| == n ensures forall e :: e in s ==> e == x ensures forall i :: 0 <= i < |s| ==> s[i] == x { if n == 0 then [] else [x] + RepeatingValue(x, n-1) } ////////////////////////////// // UTILITY LEMMAS ////////////////////////////// lemma lemma_SequenceIsCarPlusCdr(s:seq) requires |s| > 0 ensures s == [s[0]] + s[1..]; { } lemma lemma_SequenceConcatenationAssociative(s1:seq, s2:seq, s3:seq) ensures s1 + (s2 + s3) == (s1 + s2) + s3; { } lemma lemma_DroppingHeadOfConcatenation(s1:seq, s2:seq) requires |s1| > 0 ensures s1[1..] + s2 == (s1 + s2)[1..] { } lemma lemma_IndexIntoDrop(s:seq, i:int, j:int) requires 0 <= i requires 0 <= j requires i + j < |s| ensures s[i..][j] == s[i+j] { } lemma lemma_IndexIntoConcatenation(s1:seq, s2:seq, i:int) requires 0 <= i < |s1| + |s2| ensures (s1 + s2)[i] == if i < |s1| then s1[i] else s2[i-|s1|] { } lemma lemma_LastOfConcatenationIsLastOfLatter(s1:seq, s2:seq) requires |s2| > 0 ensures last(s1 + s2) == last(s2) { } lemma lemma_AllButLastPlusLastIsSeq(s:seq) requires |s| > 0 ensures all_but_last(s) + [last(s)] == s { } lemma lemma_LastOfDropIsLast(s:seq, i:int) requires 0 <= i < |s| ensures last(s[i..]) == last(s) { } lemma lemma_LastOfAllButLast(s:seq) requires |s| > 1 ensures last(all_but_last(s)) == s[|s|-2] { } lemma lemma_TakePlusDropIsSeq(s:seq, i:int) requires 0 <= i <= |s| ensures s == s[..i] + s[i..] { } lemma lemma_SeqEqualsThreeWayConcatentation(s:seq, i:int, j:int) requires 0 <= i <= j <= |s| ensures s == s[..i] + s[i..j] + s[j..] { } lemma lemma_DropMapSeqToSeq(s:seq, f:T->U, i:int) requires |s| >= i >= 0 ensures MapSeqToSeq(s[i..], f) == MapSeqToSeq(s, f)[i..] decreases i { var s1 := MapSeqToSeq(s[i..], f); var s2 := MapSeqToSeq(s, f)[i..]; assert |s1| == |s2|; forall j | 0 <= j < |s1| ensures s1[j] == s2[j] { calc { s1[j]; f(s[i..][j]); { lemma_IndexIntoDrop(s, i, j); } f(s[i+j]); MapSeqToSeq(s, f)[i+j]; { lemma_IndexIntoDrop(MapSeqToSeq(s, f), i, j); } MapSeqToSeq(s, f)[i..][j]; s2[j]; } } } } ================================================ FILE: Armada/util/collections/seqs.s.dfy ================================================ module util_collections_seqs_s { function last(s:seq) : T requires |s| > 0; { s[|s|-1] } function all_but_last(s:seq) : seq requires |s| > 0; { s[..|s|-1] } } ================================================ FILE: Armada/util/collections/sets.i.dfy ================================================ module util_collections_sets_i { lemma ThingsIKnowAboutSubset(x:set, y:set) requires x(x:set, y:set) ensures x |x|<|y|; ensures x<=y ==> |x|<=|y|; { if (x(foo:set, x:T) requires foo=={x}; ensures |foo|==1; { } lemma ThingsIKnowAboutASingletonSet(foo:set, x:T, y:T) requires |foo|==1; requires x in foo; requires y in foo; ensures x==y; { if (x!=y) { assert {x} < foo; ThingsIKnowAboutSubset({x}, foo); assert |{x}| < |foo|; assert |foo|>1; assert false; } } predicate Injective(f:X->Y) { forall x1, x2 :: f(x1) == f(x2) ==> x1 == x2 } predicate InjectiveOver(xs:set, ys:set, f:X->Y) { forall x1, x2 :: x1 in xs && x2 in xs && f(x1) in ys && f(x2) in ys && f(x1) == f(x2) ==> x1 == x2 } predicate InjectiveOverSeq(xs:seq, ys:set, f:X->Y) { forall x1, x2 :: x1 in xs && x2 in xs && f(x1) in ys && f(x2) in ys && f(x1) == f(x2) ==> x1 == x2 } predicate Inverses(f1:X->Y, f2:Y->X) { && forall x :: f2(f1(x)) == x && forall y :: f1(f2(y)) == y } lemma lemma_MapSetCardinality(xs:set, ys:set, f:X->Y) requires Injective(f); requires forall x :: x in xs <==> f(x) in ys; requires forall y :: y in ys ==> exists x :: x in xs && y == f(x); ensures |xs| == |ys|; { if (xs != {}) { var x :| x in xs; var xs' := xs - {x}; var ys' := ys - {f(x)}; lemma_MapSetCardinality(xs', ys', f); } } lemma lemma_MapSetCardinalityOver(xs:set, ys:set, f:X->Y) requires InjectiveOver(xs, ys, f); requires forall x :: x in xs ==> f(x) in ys; requires forall y :: y in ys ==> exists x :: x in xs && y == f(x); ensures |xs| == |ys|; { if (xs != {}) { var x :| x in xs; var xs' := xs - {x}; var ys' := ys - {f(x)}; lemma_MapSetCardinalityOver(xs', ys', f); } } lemma lemma_MapSubsetCardinalityOver(xs:set, ys:set, f:X->Y) requires InjectiveOver(xs, ys, f); requires forall x :: x in xs ==> f(x) in ys; ensures |xs| <= |ys|; { if (xs != {}) { var x :| x in xs; var xs' := xs - {x}; var ys' := ys - {f(x)}; lemma_MapSubsetCardinalityOver(xs', ys', f); } } lemma lemma_MapSubseqCardinalityOver(xs:seq, ys:set, f:X->Y) requires forall i, j :: 0 <= i < |xs| && 0 <= j < |xs| && i != j ==> xs[i] != xs[j]; requires InjectiveOverSeq(xs, ys, f); requires forall x :: x in xs ==> f(x) in ys; ensures |xs| <= |ys|; { if (xs != []) { var x := xs[0]; var xs' := xs[1..]; var ys' := ys - {f(x)}; forall x' | x' in xs' ensures f(x') in ys'; { assert x' in xs; assert f(x') in ys; if f(x') == f(x) { assert x in xs && x' in xs && f(x) in ys && f(x') in ys && f(x') == f(x); assert x' == x; } } forall x1, x2 | x1 in xs' && x2 in xs' && f(x1) in ys' && f(x2) in ys' && f(x1) == f(x2) ensures x1 == x2; { assert x1 in xs && x2 in xs && f(x1) in ys && f(x2) in ys'; } lemma_MapSubseqCardinalityOver(xs', ys', f); } } function MapSetToSet(xs:set, f:X->Y) : (ys:set) requires Injective(f); ensures forall x :: x in xs <==> f(x) in ys; ensures |xs| == |ys|; { var ys := set x | x in xs :: f(x); lemma_MapSetCardinality(xs, ys, f); ys } function MapSetToSetBijective(xs:set, f:X->Y, f_inv:Y->X):(ys:set) requires Inverses(f, f_inv) ensures forall x :: x in xs <==> f(x) in ys ensures forall y :: y in ys <==> f_inv(y) in xs ensures |xs| == |ys| { var ys := set x | x in xs :: f(x); lemma_MapSetCardinality(xs, ys, f); ys } function MapSetToSetOver(xs:set, f:X->Y):set requires InjectiveOver(xs, set x | x in xs :: f(x), f); ensures forall x :: x in xs ==> f(x) in MapSetToSetOver(xs, f); ensures |xs| == |MapSetToSetOver(xs, f)|; { var ys := set x | x in xs :: f(x); lemma_MapSetCardinalityOver(xs, ys, f); ys } function MapSeqToSet(xs:seq, f:X->Y):set requires Injective(f); ensures forall x :: x in xs <==> f(x) in MapSeqToSet(xs, f); { set x | x in xs :: f(x) } lemma lemma_SubsetCardinality(xs:set, ys:set, f:X->bool) requires forall x :: x in ys ==> x in xs && f(x); ensures |ys| <= |xs|; { if (ys != {}) { var y :| y in ys; var xs' := xs - {y}; var ys' := ys - {y}; lemma_SubsetCardinality(xs', ys', f); } } function/*TODO:{:opaque}*/ MakeSubset(xs:set, f:X->bool):set ensures forall x :: x in MakeSubset(xs, f) <==> x in xs && f(x); ensures |MakeSubset(xs, f)| <= |xs|; { var ys := set x | x in xs && f(x); lemma_SubsetCardinality(xs, ys, f); ys } /* examples: function{:opaque} setAdd1(xs:set):set ensures forall x :: x in xs <==> x + 1 in setAdd1(xs); ensures |xs| == |setAdd1(xs)|; { MapSetToSet(xs, x => x + 1) } function{:opaque} setPos(xs:set):set ensures forall x :: x in setPos(xs) <==> x in xs && x > 0; { MakeSubset(xs, x => x > 0) } */ lemma lemma_UnionCardinality(xs:set, ys:set, us:set) requires us==xs+ys; ensures |us| >= |xs|; decreases ys; { if (ys=={}) { } else { var y :| y in ys; if (y in xs) { var xr := xs - {y}; var yr := ys - {y}; var ur := us - {y}; lemma_UnionCardinality(xr, yr, ur); } else { var ur := us - {y}; var yr := ys - {y}; lemma_UnionCardinality(xs, yr, ur); } } } function SetOfNumbersInRightExclusiveRange(a:int, b:int):set requires a <= b; ensures forall opn :: a <= opn < b ==> opn in SetOfNumbersInRightExclusiveRange(a, b); ensures forall opn :: opn in SetOfNumbersInRightExclusiveRange(a, b) ==> a <= opn < b; ensures |SetOfNumbersInRightExclusiveRange(a, b)| == b-a; decreases b-a; { if a == b then {} else {a} + SetOfNumbersInRightExclusiveRange(a+1, b) } lemma lemma_CardinalityOfBoundedSet(s:set, a:int, b:int) requires forall opn :: opn in s ==> a <= opn < b; requires a <= b; ensures |s| <= b-a; { var range := SetOfNumbersInRightExclusiveRange(a, b); forall i | i in s ensures i in range; { } assert s <= range; SubsetCardinality(s, range); } function intsetmax(s:set):int requires |s| > 0; ensures var m := intsetmax(s); m in s && forall i :: i in s ==> m >= i; { var x :| x in s; if |s| == 1 then assert |s - {x}| == 0; x else var sy := s - {x}; var y := intsetmax(sy); assert forall i :: i in s ==> i in sy || i == x; if x > y then x else y } lemma lemma_EmptySetCantHaveMember(x:T, s:set) requires x in s requires |s| == 0 || s == {} ensures false { } } ================================================ FILE: Armada/util/functions.i.dfy ================================================ module util_functions_i { predicate RelationSatisfiableAt(relation:(T,U)->bool, x:T) { exists y:U :: relation(x, y) } predicate RelationAlwaysSatisfiable(relation:(T,U)->bool) { forall x:T :: RelationSatisfiableAt(relation, x) } predicate FunctionSatisfiesRelation(f:T->U, relation:(T,U)->bool) { forall t:T :: relation(t, f(t)) } function FunctionSatisfyingRelation(relation:(T,U)->bool) : (T->U) requires RelationAlwaysSatisfiable(relation) { (x:T) => var y :| assert RelationSatisfiableAt(relation, x); relation(x, y); y } /* Example use: predicate MyRelation(x:int, y:int) lemma SatisfyMyRelation(x:int) returns (y:int) ensures MyRelation(x, y) lemma ConstructFunctionSatisfyingMyRelation() returns (f:int->int) ensures FunctionSatisfiesRelation(f, MyRelation) { forall x:int ensures RelationSatisfiableAt(MyRelation, x) { var y := SatisfyMyRelation(x); } f := FunctionSatisfyingRelation(MyRelation); } */ } ================================================ FILE: Armada/util/math/.gitignore ================================================ *.bpl *.log ================================================ FILE: Armada/util/math/div.i.dfy ================================================ include "power.i.dfy" include "mul.i.dfy" include "div_def.i.dfy" include "div_boogie.i.dfy" include "div_nonlinear.i.dfy" include "div_auto.i.dfy" include "mod_auto.i.dfy" include "mul_auto.i.dfy" include "powers.i.dfy" include "mul_nonlinear.i.dfy" module Math__div_i { import opened Math__power_i import opened Math__mul_i import opened Math__div_def_i import opened Math__div_boogie_i import opened Math__div_nonlinear_i import opened Math__div_auto_i import opened Math__mod_auto_i import opened Math__mul_auto_i import opened Math__power_s import opened Math__mul_nonlinear_i //-//////////////////////////////////////////////////////////////////////////// //- //- Core div lemmas, with named arguments. //- //-//////////////////////////////////////////////////////////////////////////// lemma lemma_div_by_one_is_identity(x:int) {} lemma lemma_div_basics(x:int) ensures x != 0 ==> 0 / x == 0; ensures x / 1 == x; ensures x!=0 ==> x / x == 1; { if (x != 0) { lemma_div_by_self(x); lemma_div_of_0(x); } } lemma lemma_small_div_converse() ensures forall x, d {:trigger x/d} :: 0<=x && 0 x < d; { forall x, d | 0<=x && 0 u < d); } } lemma lemma_div_is_ordered_by_denominator(x:int, y:int, z:int) requires x >= 0; requires 1 <= y <= z; ensures x / y >= x / z; decreases x; { lemma_div_is_div_recursive_forall(); assert forall u:int, d:int {:trigger u / d}{:trigger my_div_recursive(u, d)} :: d > 0 ==> my_div_recursive(u, d) == u / d; if (x < z) { lemma_div_is_ordered(0, x, y); } else { lemma_div_is_ordered(x-z, x-y, y); lemma_div_is_ordered_by_denominator(x-z, y, z); } } lemma lemma_div_is_strictly_ordered_by_denominator(x:int, d:int) requires 0 < x; requires 1 < d; ensures x/d < x; decreases x; { lemma_div_auto_induction(d, x, imap u :: 0 < u ==> u / d < u); } lemma lemma_dividing_sums(a:int, b:int, d:int, R:int) requires 0 { a%d + b%d == R + (a+b)%d; (a+b)-(a+b)%d - R == a-(a%d) + b - (b%d); { lemma_fundamental_div_mod(a+b,d); lemma_fundamental_div_mod(a,d); lemma_fundamental_div_mod(b,d); } d*((a+b)/d) - R == d*(a/d) + d*(b/d); } } //-static lemma lemma_negative_divisor(x:int, d:int) //- requires d < 0; //- ensures x / (-1*d) == -1*(x / d); //-{ //- var q := x / (-1*d); //- var r := x % (-1*d); //- //- calc { //- x; //- { lemma_fundamental_div_mod(x, -1*d); } //- q * (-1*d) + r; //- { lemma_mul_is_associative(q, -1, d); } //- (q*-1)*d + r; //- { lemma_mul_is_commutative(q, -1); } //- (-q) * d + r; //- } //- lemma_mod_range(x, -1*d); //- lemma_fundamental_div_mod_converse(x, d, -q, r); //- assert x / d == -q; //- assert -1*(x/d) == q; //-} lemma lemma_div_pos_is_pos(x:int, divisor:int) requires 0 <= x; requires 0 < divisor; ensures x / divisor >= 0; { lemma_div_auto_induction(divisor, x, imap u :: 0 <= u ==> u / divisor >= 0); } //-//////////////////////////////////////////////////////////////////////////// //- //- Forall lemmas: these restate the core lemmas with foralls, //- so callers needn't name the specific expressions to manipulate. //- //- These are all boilerplate transformations of args/requires/ensures //- into forall args :: requires ==> ensures, with a correpsonding //- mechanically generated forall proof that invokes the core lemma. // So don't bother reading them. //- //-//////////////////////////////////////////////////////////////////////////// lemma lemma_div_basics_forall() ensures forall x {:trigger 0 / x} :: x != 0 ==> 0 / x == 0; ensures forall x {:trigger x / 1} :: x / 1 == x; ensures forall x, y {:trigger x/y} :: x >= 0 && y > 0 ==> x/y >= 0; ensures forall x, y {:trigger x/y} :: x >= 0 && y > 0 ==> x/y <= x; { forall (x:int) ensures x != 0 ==> 0 / x == 0; ensures x / 1 == x; { lemma_div_basics(x); } forall x:int, y:int | x >= 0 && y > 0 ensures x / y >= 0; ensures x / y <= x; { lemma_div_pos_is_pos(x, y); lemma_div_is_ordered_by_denominator(x, 1, y); } } ////////////////////////////////////////////////////////////////////////////// // // I'm not sure how useful this is. I wrote it to try to bring // negative numerators to the positive side for trying to prove // the negative half of lemma_fundamental_div_mod. That turned out // to be the wrong idea, so maybe these are just useless. lemma lemma_div_neg_neg(x:int, d:int) requires d > 0; ensures x/d == -((-x+d-1)/d); { lemma_div_auto(d); } //- //-//////////////////////////////////////////////////////////////////////////// /******************************* * Useful lemmas about mod * *******************************/ //////////////////////////////////////////////// // No longer needed. Here for legacy reasons //////////////////////////////////////////////// lemma lemma_mod_2(x:int) {} lemma lemma_mod2_plus(x:int) {} lemma lemma_mod2_plus2(x:int) {} lemma lemma_mod32(x:int) {} lemma lemma_mod_remainder_neg_specific(x:int, m:int) {} lemma lemma_mod_remainder_neg() {} lemma lemma_mod_remainder_pos_specific(x:int, m:int) {} lemma lemma_mod_remainder_pos() {} lemma lemma_mod_remainder_specific(x:int, m:int) {} lemma lemma_mod_remainder() {} //////////////////////////////////////////////// // Actual useful lemmas //////////////////////////////////////////////// lemma lemma_mod_basics() ensures forall m:int {:trigger m % m} :: m > 0 ==> m % m == 0; ensures forall x:int, m:int {:trigger (x%m) % m} :: m > 0 ==> (x%m) % m == x%m; { forall m:int | m > 0 ensures m % m == 0; { lemma_mod_auto(m); } forall x:int, m:int | m > 0 ensures (x % m) % m == x % m; { lemma_mod_auto(m); } } lemma lemma_mod_properties() ensures forall m:int {:trigger m % m} :: m > 0 ==> m % m == 0; ensures forall x:int, m:int {:trigger (x%m) % m} :: m > 0 ==> (x%m) % m == x%m; ensures forall x:int, m:int {:trigger x%m} :: m > 0 ==> 0 <= x%m < m; { lemma_mod_basics(); forall x:int, m:int | m > 0 ensures m > 0 ==> 0 <= x%m < m; { lemma_mod_auto(m); } } lemma lemma_mod_decreases(x:nat, d:nat) requires 00 then a else -a; requires 0 < m; ensures (m*a + b) % m == b % m; { lemma_mod_auto(m); lemma_mul_auto_induction(a, imap u :: (m*u + b) % m == b % m); } lemma lemma_add_mod_noop(x:int, y:int, m:int) requires 0 < m; ensures ((x % m) + (y % m)) % m == (x+y) % m; { lemma_mod_auto(m); } lemma lemma_add_mod_noop_right(x:int, y:int, m:int) requires 0 < m; ensures (x + (y % m)) % m == (x+y) % m; { lemma_mod_auto(m); } lemma lemma_mod_equivalence(x:int, y:int, m:int) requires 0 < m; ensures x % m == y % m <==> (x - y) % m == 0; { lemma_mod_auto(m); } lemma lemma_sub_mod_noop(x:int, y:int, m:int) requires 0 < m; ensures ((x % m) - (y % m)) % m == (x-y) % m; { lemma_mod_auto(m); } lemma lemma_sub_mod_noop_right(x:int, y:int, m:int) requires 0 < m; ensures (x - (y % m)) % m == (x-y) % m; { lemma_mod_auto(m); } lemma lemma_mod_adds(a:int, b:int, d:int) requires 0 a%d + b%d == (a+b)%d; { lemma_mul_auto(); lemma_div_auto(d); } lemma {:timeLimitMultiplier 2} lemma_mod_neg_neg(x:int, d:int) requires d > 0; ensures x%d == (x*(1-d))%d; { forall ensures (x - x * d) % d == x % d; { lemma_mod_auto(d); var f := imap i :: (x - i * d) % d == x % d; assert MulAuto() ==> f[0] && (forall i {:trigger TMulAutoLe(0, i)} :: TMulAutoLe(0, i) && f[i] ==> f[i + 1]) && (forall i {:trigger TMulAutoLe(i, 0)} :: TMulAutoLe(i, 0) && f[i] ==> f[i - 1]); lemma_mul_auto_induction(x, imap i :: (x - i * d) % d == x % d); } lemma_mul_auto(); } lemma lemma_fundamental_div_mod_converse(x:int, d:int, q:int, r:int) requires d != 0; requires 0 <= r < d; requires x == q * d + r; ensures q == x/d; ensures r == x%d; { lemma_div_auto(d); lemma_mul_auto_induction(q, imap u :: u == (u * d + r) / d); lemma_mul_auto_induction(q, imap u :: r == (u * d + r) % d); // REVIEW: this is a plausible alternative, but it times out: // lemma_mul_auto(); // lemma_div_auto_induction(d, x, imap x :: x == q * d + r ==> q == x/d && r == x%d); } lemma lemma_mod_pos_bound(x:int, m:int) decreases x; requires 0 <= x; requires 0 < m; ensures 0 <= x%m < m; { lemma_mod_auto(m); } lemma lemma_mul_mod_noop_left(x:int, y:int, m:int) requires 0 < m; ensures (x % m)*y % m == x*y % m; { lemma_mod_auto(m); lemma_mul_auto_induction(y, imap u :: (x % m)*u % m == x*u % m); } lemma lemma_mul_mod_noop_right(x:int, y:int, m:int) requires 0 < m; ensures x*(y % m) % m == (x*y) % m; { lemma_mod_auto(m); lemma_mul_auto_induction(x, imap u :: u*(y % m) % m == (u*y) % m); } lemma lemma_mul_mod_noop_general(x:int, y:int, m:int) requires 0 < m; ensures ((x % m) * y ) % m == (x * y) % m; ensures ( x * (y % m)) % m == (x * y) % m; ensures ((x % m) * (y % m)) % m == (x * y) % m; { lemma_mod_properties(); lemma_mul_mod_noop_left(x, y, m); lemma_mul_mod_noop_right(x, y, m); lemma_mul_mod_noop_right(x % m, y, m); } lemma lemma_mul_mod_noop(x:int, y:int, m:int) requires 0 < m; ensures (x % m) * (y % m) % m == (x*y) % m; { lemma_mul_mod_noop_general(x, y, m); } lemma lemma_power_mod_noop(b:int, e:nat, m:int) decreases e; requires 0 < m; ensures power(b % m, e) % m == power(b, e) % m; { reveal_power(); lemma_mod_properties(); if (e > 0) { calc { power(b % m, e) % m; ((b % m) * power(b % m, e - 1)) % m; { lemma_mul_mod_noop_general(b, power(b % m, e - 1), m); } ((b % m) * (power(b % m, e - 1) % m) % m) % m; { lemma_power_mod_noop(b, e - 1, m); } ((b % m) * (power(b, e - 1) % m) % m) % m; { lemma_mul_mod_noop_general(b, power(b, e - 1), m); } (b * (power(b, e - 1)) % m) % m; (b * (power(b, e - 1))) % m; power(b, e) % m; } } } lemma lemma_mod_subtraction(x:nat, s:nat, d:nat) requires 0 0; ensures (x * m) % m == 0; { lemma_mod_auto(m); lemma_mul_auto_induction(x, imap u :: (u * m) % m == 0); } //- //-//////////////////////////////////////////////////////////////////////////// /************************************************************ * Lemmas that depend on properties of both div and mod * ************************************************************/ //-///////////////////////////////////////////////////// //- Proof that div is recursive div //-///////////////////////////////////////////////////// lemma lemma_div_plus_one(x:int, d:int) requires d > 0; //-requires x >= 0; ensures 1 + x / d == (d + x) / d; { lemma_div_auto(d); } lemma lemma_div_minus_one(x:int, d:int) requires d > 0; ensures -1 + x / d == (-d + x) / d; { lemma_div_auto(d); } lemma lemma_mod_mod(x:int, a:int, b:int) requires 0 0; ensures my_div_recursive(x, d) == x / d; { lemma_div_auto_induction(d, x, imap u :: my_div_recursive(u, d) == u / d); // Omitted rather than prove lemma_negative_divisor // //- if d > 0 { //- lemma_div_is_div_pos(x, d); //- } else { //- calc { //- my_div_recursive(x, d); //- -1 * my_div_pos(x, -1*d); //- { lemma_div_is_div_pos(x, -1*d); } //- -1 * (x / (-1*d)); //- { lemma_negative_divisor(x, d); } //- x / d; //- } //- } } lemma lemma_div_is_div_recursive_forall() ensures forall x:int, d:int :: d > 0 ==> my_div_recursive(x, d) == x / d; { forall x:int, d:int | d > 0 ensures my_div_recursive(x, d) == x / d; { lemma_div_is_div_recursive(x, d); } } //-///////////////////////////////////////////////////// //-///////////////////////////////////////////////////// //- Proof that mod is recursive mod //-///////////////////////////////////////////////////// lemma lemma_mod_is_mod_recursive(x:int, m:int) requires m > 0; ensures my_mod_recursive(x, m) == x % m; decreases if x < 0 then -x + m else x; { if x < 0 { calc { my_mod_recursive(x, m); my_mod_recursive(x + m, m); { lemma_mod_is_mod_recursive(x + m, m); } (x + m) % m; { lemma_add_mod_noop(x, m, m); } ((x % m) + (m % m)) % m; { lemma_mod_basics(); } (x % m) % m; { lemma_mod_basics(); } x % m; } } else if x < m { lemma_small_mod(x, m); } else { calc { my_mod_recursive(x, m); my_mod_recursive(x - m, m); { lemma_mod_is_mod_recursive(x - m, m); } (x - m) % m; { lemma_sub_mod_noop(x, m, m); } ((x % m) - (m % m)) % m; { lemma_mod_basics(); } (x % m) % m; { lemma_mod_basics(); } x % m; } } } lemma lemma_mod_is_mod_recursive_forall() ensures forall x:int, d:int :: d > 0 ==> my_mod_recursive(x, d) == x % d; { forall x:int, d:int | d > 0 ensures my_mod_recursive(x, d) == x % d; { lemma_mod_is_mod_recursive(x, d); } } //-///////////////////////////////////////////////////// lemma lemma_basic_div(d:int) requires d > 0; ensures forall x {:trigger x / d} :: 0 <= x < d ==> x / d == 0; { lemma_div_auto(d); } lemma lemma_div_is_ordered(x:int, y:int, z:int) requires x <= y; requires z > 0; ensures x / z <= y / z; { lemma_div_auto_induction(z, x - y, imap xy :: xy <= 0 ==> (xy + y) / z <= y / z); } lemma lemma_div_decreases(x:int, d:int) requires 0 u/d < u); } lemma lemma_div_nonincreasing(x:int, d:int) requires 0<=x; requires 0 u/d <= u); } lemma lemma_breakdown(a:int, b:int, c:int) requires 0<=a; requires 0 u - divisor < u / divisor * divisor); } lemma lemma_remainder_lower(x:int, divisor:int) requires 0 <= x; requires 0 < divisor; ensures x >= x / divisor * divisor; { lemma_mul_auto(); lemma_div_auto_induction(divisor, x, imap u :: 0 <= u ==> u >= u / divisor * divisor); } lemma lemma_remainder(x:int, divisor:int) requires 0 <= x; requires 0 < divisor; ensures 0 <= x - x / divisor * divisor < divisor; { lemma_mul_auto(); lemma_div_auto_induction(divisor, x, imap u :: 0 <= u - u / divisor * divisor < divisor); } lemma lemma_div_denominator(x:int,c:nat,d:nat) requires 0 <= x; requires 0= d) { lemma_fundamental_div_mod(R, c); //- assert R >= c*(R/c); lemma_mul_inequality(d, R/c, c); lemma_mul_is_commutative_forall(); //- assert c*(R/c) >= c*d; //- assert R >= c*d; assert false; } assert R/c < d; lemma_mul_basics_forall(); lemma_fundamental_div_mod_converse(R/c, d, 0, R/c); assert (R/c) % d == R/c; lemma_fundamental_div_mod(R, c); assert c*(R/c) + R%c == R; assert c*((R/c) % d) + R%c == R; var k := x/(c*d); lemma_fundamental_div_mod(x, c*d); assert x == (c*d)*(x/(c*d)) + x % (c*d); assert R == x - (c*d)*(x/(c*d)); assert R == x - (c*d)*k; calc { c*((x/c)%d) + x%c; { lemma_mod_multiples_vanish(-k, x/c, d); lemma_mul_is_commutative_forall(); } c*((x/c+(-k)*d) % d) + x%c; { lemma_hoist_over_denominator(x, (-k)*d, c); } c*(((x+(((-k)*d)*c))/c) % d) + x%c; { lemma_mul_is_associative(-k,d,c); } c*(((x+((-k)*(d*c)))/c) % d) + x%c; { lemma_mul_unary_negation(k,d*c); } c*(((x+(-(k*(d*c))))/c) % d) + x%c; { lemma_mul_is_associative(k,d,c); } c*(((x+(-(k*d*c)))/c) % d) + x%c; c*(((x-k*d*c)/c) % d) + x%c; { lemma_mul_is_associative_forall(); lemma_mul_is_commutative_forall(); } c*((R/c) % d) + x%c; c*(R/c) + x%c; { lemma_fundamental_div_mod(R,c); assert R == c*(R/c) + R % c; lemma_mod_mod(x,c,d); assert R%c == x%c; } R; { lemma_mod_is_mod_recursive_forall(); } R%(c*d); (x-(c*d)*k) % (c*d); { lemma_mul_unary_negation(c*d,k); } (x+(c*d)*(-k)) % (c*d); { lemma_mod_multiples_vanish(-k, x, c*d); } x % (c*d); } calc ==> { c*(x/c) + x%c - R == c*(x/c) - c*((x/c)%d); { lemma_fundamental_div_mod(x,c); } x - R == c*(x/c) - c*((x/c)%d); } calc ==> { true; { lemma_fundamental_div_mod(x/c,d); } d*((x/c)/d) == x/c - ((x/c)%d); c*(d*((x/c)/d)) == c*(x/c - ((x/c)%d)); { lemma_mul_is_associative_forall(); } (c*d)*((x/c)/d) == c*(x/c - ((x/c)%d)); { lemma_mul_is_distributive_forall(); } (c*d)*((x/c)/d) == c*(x/c) - c*((x/c)%d); (c*d)*((x/c)/d) == x - R; { lemma_fundamental_div_mod(x, c*d); } (c*d)*((x/c)/d) == (c*d)*(x/(c*d)) + x%(c*d) - R; (c*d)*((x/c)/d) == (c*d)*(x/(c*d)); { lemma_mul_one_to_one(c*d, (x/c)/d, x/(c*d)); } (x/c)/d == x/(c*d); } } lemma lemma_mul_hoist_inequality(x:int, y:int, z:int) requires 0 <= x; requires 0 < z; ensures x*(y/z) <= (x*y)/z; { calc { (x*y)/z; { lemma_fundamental_div_mod(y, z); } (x*(z*(y/z)+y%z))/z; { lemma_mul_is_distributive_forall(); } (x*(z*(y/z))+x*(y%z))/z; >= { lemma_mod_properties(); lemma_mul_nonnegative(x, y%z); lemma_div_is_ordered(x*(z*(y/z)), x*(z*(y/z))+x*(y%z), z); } (x*(z*(y/z)))/z; { lemma_mul_is_associative_forall(); lemma_mul_is_commutative_forall(); } (z*(x*(y/z)))/z; { lemma_div_multiples_vanish(x*(y/z), z); } x*(y/z); } } lemma lemma_indistinguishable_quotients(a:int, b:int, d:int) requires 0 u/d == b/d); } lemma lemma_truncate_middle(x:int, b:int, c:int) requires 0<=x; requires 0 { true; { lemma_fundamental_div_mod(x,c); } x == c*(x/c) + x%c; b*x == b*(c*(x/c) + x%c); { lemma_mul_is_distributive_forall(); } b*x == b*(c*(x/c)) + b*(x%c); { lemma_mul_is_associative_forall(); } b*x == (b*c)*(x/c) + b*(x%c); } } lemma lemma_div_multiples_vanish_quotient(x:int, a:int, d:int) requires 0 u==d*((u+r)/d)); } lemma lemma_div_multiples_vanish_fancy(x:int, b:int, d:int) requires 0 0; ensures x / z < y / z; { lemma_mod_multiples_basic(m, z); lemma_div_auto_induction(z, y - x, imap yx :: var u := yx + x; x < u && u % z == 0 ==> x / z < u / z); } lemma lemma_multiply_divide_le(a:int, b:int, c:int) requires 0 < b; requires a <= b * c; ensures a / b <= c; { lemma_mod_multiples_basic(c, b); lemma_div_auto_induction(b, b * c - a, imap i :: 0 <= i && (i + a) % b == 0 ==> a / b <= (i + a) / b); lemma_div_multiples_vanish(c, b); } lemma lemma_multiply_divide_lt(a:int, b:int, c:int) requires 0 < b; requires a < b * c; ensures a / b < c; { lemma_mod_multiples_basic(c, b); lemma_div_auto_induction(b, b * c - a, imap i :: 0 < i && (i + a) % b == 0 ==> a / b < (i + a) / b); lemma_div_multiples_vanish(c, b); } lemma lemma_hoist_over_denominator(x:int, j:int, d:nat) requires 0 { true; { lemma_mod_properties(); } b*(a/b) % (b*c) < b*c; b*((a/b) - (c*((b*(a/b))/(b*c)))) < b*c; { lemma_mul_is_commutative_forall(); lemma_mul_strict_inequality_converse_forall(); } ((a/b) - (c*((b*(a/b))/(b*c)))) < c; ((a/b) - (c*((b*(a/b))/(b*c)))) <= c-1; { lemma_mul_is_commutative_forall(); lemma_mul_inequality_forall(); } b*((a/b) - (c*((b*(a/b))/(b*c)))) <= b*(c-1); b*(a/b) % (b*c) <= b*(c-1); } } lemma lemma_part_bound2(a:int, b:int, c:int) requires 0<=a; requires 0 c * d != 0; ensures forall x:int,c:nat,d:nat {:trigger (x/c)/d} :: 0 <= x && 0 < c && 0 < d ==> (x/c)/d == x/(c*d); { lemma_mul_nonzero_forall(); forall (x:int,c:nat,d:nat | 0 <= x && 0 < c && 0 < d) ensures c * d != 0; ensures (x/c)/d == x/(c*d); { lemma_div_denominator(x,c,d); } } } ================================================ FILE: Armada/util/math/div_auto.i.dfy ================================================ include "mod_auto.i.dfy" include "div_auto_proofs.i.dfy" include "mod_auto_proofs.i.dfy" module Math__div_auto_i { import opened Math__mod_auto_i import opened Math__div_auto_proofs_i import opened Math__mod_auto_proofs_i predicate DivAuto(n:int) requires n > 0; // TODO: allow n < 0 { ModAuto(n) && (n / n == -((-n) / n) == 1) && (forall x:int {:trigger x / n} :: 0 <= x < n <==> x / n == 0) && (forall x:int, y:int {:trigger (x + y) / n} :: (var z := (x % n) + (y % n); ( (0 <= z < n && (x + y) / n == x / n + y / n) || (n <= z < n + n && (x + y) / n == x / n + y / n + 1)))) && (forall x:int, y:int {:trigger (x - y) / n} :: (var z := (x % n) - (y % n); ( (0 <= z < n && (x - y) / n == x / n - y / n) || (-n <= z < 0 && (x - y) / n == x / n - y / n - 1)))) } lemma lemma_div_auto(n:int) requires n > 0; ensures DivAuto(n); { lemma_mod_auto(n); lemma_div_auto_basics(n); assert (0 + n) / n == 1; assert (0 - n) / n == -1; forall x:int, y:int {:trigger (x + y) / n} ensures (var z := (x % n) + (y % n); ( (0 <= z < n && (x + y) / n == x / n + y / n) || (n <= z < 2 * n && (x + y) / n == x / n + y / n + 1))); { var f := imap xy:(int, int) :: (var z := (xy.0 % n) + (xy.1 % n); ( (0 <= z < n && (xy.0 + xy.1) / n == xy.0 / n + xy.1 / n) || (n <= z < 2 * n && (xy.0 + xy.1) / n == xy.0 / n + xy.1 / n + 1))); forall i, j ensures j >= 0 && f[(i, j)] ==> f[(i, j + n)]; ensures i < n && f[(i, j)] ==> f[(i - n, j)]; ensures j < n && f[(i, j)] ==> f[(i, j - n)]; ensures i >= 0 && f[(i, j)] ==> f[(i + n, j)]; { assert ((i + n) + j) / n == ((i + j) + n) / n; assert (i + (j + n)) / n == ((i + j) + n) / n; assert ((i - n) + j) / n == ((i + j) - n) / n; assert (i + (j - n)) / n == ((i + j) - n) / n; } forall i, j ensures 0 <= i < n && 0 <= j < n ==> f[(i, j)]; { assert ((i + n) + j) / n == ((i + j) + n) / n; assert (i + (j + n)) / n == ((i + j) + n) / n; assert ((i - n) + j) / n == ((i + j) - n) / n; assert (i + (j - n)) / n == ((i + j) - n) / n; } lemma_mod_induction_forall2(n, f); assert f[(x, y)]; } forall x:int, y:int {:trigger (x - y) / n} ensures (var z := (x % n) - (y % n); ( (0 <= z < n && (x - y) / n == x / n - y / n) || (-n <= z < 0 && (x - y) / n == x / n - y / n - 1))); { var f := imap xy:(int, int) :: (var z := (xy.0 % n) - (xy.1 % n); ( (0 <= z < n && (xy.0 - xy.1) / n == xy.0 / n - xy.1 / n) || (-n <= z < 0 && (xy.0 - xy.1) / n == xy.0 / n - xy.1 / n - 1))); forall i, j ensures j >= 0 && f[(i, j)] ==> f[(i, j + n)]; ensures i < n && f[(i, j)] ==> f[(i - n, j)]; ensures j < n && f[(i, j)] ==> f[(i, j - n)]; ensures i >= 0 && f[(i, j)] ==> f[(i + n, j)]; { assert ((i + n) - j) / n == ((i - j) + n) / n; assert (i - (j - n)) / n == ((i - j) + n) / n; assert ((i - n) - j) / n == ((i - j) - n) / n; assert (i - (j + n)) / n == ((i - j) - n) / n; } forall i, j ensures 0 <= i < n && 0 <= j < n ==> f[(i, j)]; { assert ((i + n) - j) / n == ((i - j) + n) / n; assert (i - (j - n)) / n == ((i - j) + n) / n; assert ((i - n) - j) / n == ((i - j) - n) / n; assert (i - (j + n)) / n == ((i - j) - n) / n; } lemma_mod_induction_forall2(n, f); assert f[(x, y)]; } } predicate TDivAutoLe(x:int, y:int) { x <= y } lemma lemma_div_auto_induction(n:int, x:int, f:imap) requires n > 0; requires forall i :: i in f; requires DivAuto(n) ==> (forall i {:trigger TDivAutoLe(0, i)} :: TDivAutoLe(0, i) && i < n ==> f[i]) && (forall i {:trigger TDivAutoLe(0, i)} :: TDivAutoLe(0, i) && f[i] ==> f[i + n]) && (forall i {:trigger TDivAutoLe(i + 1, n)} :: TDivAutoLe(i + 1, n) && f[i] ==> f[i - n]); ensures DivAuto(n); ensures f[x]; { lemma_div_auto(n); assert forall i :: TDivAutoLe(0, i) && i < n ==> f[i]; assert forall i {:trigger f[i], f[i + n]} :: TDivAutoLe(0, i) && f[i] ==> f[i + n]; assert forall i {:trigger f[i], f[i - n]} :: TDivAutoLe(i + 1, n) && f[i] ==> f[i - n]; lemma_mod_induction_forall(n, f); assert f[x]; } lemma lemma_div_auto_induction_forall(n:int, f:imap) requires n > 0; requires forall i :: i in f; requires DivAuto(n) ==> (forall i {:trigger TDivAutoLe(0, i)} :: TDivAutoLe(0, i) && i < n ==> f[i]) && (forall i {:trigger TDivAutoLe(0, i)} :: TDivAutoLe(0, i) && f[i] ==> f[i + n]) && (forall i {:trigger TDivAutoLe(i + 1, n)} :: TDivAutoLe(i + 1, n) && f[i] ==> f[i - n]); ensures DivAuto(n); ensures forall i {:trigger f[i]} :: f[i]; { lemma_div_auto(n); assert forall i :: TDivAutoLe(0, i) && i < n ==> f[i]; assert forall i {:trigger f[i], f[i + n]} :: TDivAutoLe(0, i) && f[i] ==> f[i + n]; assert forall i {:trigger f[i], f[i - n]} :: TDivAutoLe(i + 1, n) && f[i] ==> f[i - n]; lemma_mod_induction_forall(n, f); } } ================================================ FILE: Armada/util/math/div_auto_proofs.i.dfy ================================================ include "mod_auto.i.dfy" include "mod_auto_proofs.i.dfy" module Math__div_auto_proofs_i { import opened Math__mod_auto_i import opened Math__mod_auto_proofs_i lemma lemma_div_auto_basics(n:int) requires n > 0; ensures (n / n == -((-n) / n) == 1) ensures (forall x:int {:trigger x / n} :: 0 <= x < n <==> x / n == 0) ensures forall x:int {:trigger (x + n) / n} :: (x + n) / n == x / n + 1; ensures forall x:int {:trigger (x - n) / n} :: (x - n) / n == x / n - 1; { lemma_mod_auto(n); lemma_mod_auto_basics(n); lemma_small_div(); lemma_div_by_self(n); forall x:int | x / n == 0 ensures 0 <= x < n; { lemma_fundamental_div_mod(x, n); } } } ================================================ FILE: Armada/util/math/div_boogie.i.dfy ================================================ include "div_def.i.dfy" include "mul.i.dfy" module Math__div_boogie_i { import opened Math__div_def_i import opened Math__mul_i lemma lemma_div_is_div_boogie(x:int, d:int) requires d != 0; //- ensures INTERNAL_div(x, d) == INTERNAL_div_boogie(x, d); { } lemma lemma_mod_is_mod_boogie(x:int, d:int) requires d > 0; //-ensures INTERNAL_mod(x, d) == INTERNAL_mod_boogie(x, d); { } //-static lemma lemma_div_is_div_boogie_at_least_for_2(x:int) //- ensures INTERNAL_div(x, 2) == INTERNAL_div_boogie(x,2); //-{ //-} //- //-static lemma lemma_div_is_div_boogie_for_4_which_is_also_a_number(x:int) //- ensures INTERNAL_div(x, 4) == INTERNAL_div_boogie(x,4); //-{ //-} //- //-static lemma lemma_div_is_div_boogie_for_8_which_is_also_a_number(x:int) //- ensures INTERNAL_div(x, 8) == INTERNAL_div_boogie(x,8); //-{ //-} //- //-static lemma lemma_div_is_div_boogie_for_16_which_is_also_a_number(x:int) //- ensures INTERNAL_div(x, 16) == INTERNAL_div_boogie(x,16); //-{ //-} //- // Copy-pasta from lemma_div_is_div_boogie_at_least_for_2 //-static lemma lemma_div_is_div_boogie_for_256_which_is_also_a_number(x:int) //- ensures INTERNAL_div(x, 256) == INTERNAL_div_boogie(x,256); //-{ //-} //- //-static lemma lemma_div_is_div_boogie_for_65536_which_is_also_a_number(x:int) //- ensures INTERNAL_div(x, 65536) == INTERNAL_div_boogie(x,65536); //-{ //-} //- //-static lemma lemma_div_is_div_boogie_for_16777216_which_is_also_a_number(x:int) //- ensures INTERNAL_div(x, 16777216) == INTERNAL_div_boogie(x,16777216); //-{ //-} //- //-static lemma lemma_mod_is_mod_boogie_for_2_which_is_also_a_number(x:int) //- ensures INTERNAL_mod(x, 2) == INTERNAL_mod_boogie(x,2); //-{ //-} //- //-static lemma lemma_mod_is_mod_boogie_for_4_which_is_also_a_number(x:int) //- ensures INTERNAL_mod(x, 4) == INTERNAL_mod_boogie(x,4); //-{ //-} //- //-static lemma lemma_mod_is_mod_boogie_for_16_which_is_also_a_number(x:int) //- ensures INTERNAL_mod(x, 16) == INTERNAL_mod_boogie(x,16); //-{ //-} //- //-static lemma lemma_mod_is_mod_boogie_for_256_which_is_also_a_number(x:int) //- ensures INTERNAL_mod(x, 256) == INTERNAL_mod_boogie(x,256); //-{ //-} //- //-static lemma lemma_mod_is_mod_boogie_for_65536_which_is_also_a_number(x:int) //- ensures INTERNAL_mod(x, 65536) == INTERNAL_mod_boogie(x,65536); //-{ //-} } ================================================ FILE: Armada/util/math/div_def.i.dfy ================================================ //- Specs/implements mathematical div and mod, not the C version. //- This may produce "surprising" results for negative values //- For example, -3 div 5 is -1 and -3 mod 5 is 2. //- Note this is consistent: -3 * -1 + 2 == 5 module Math__div_def_i { /* function mod(x:int, m:int) : int requires m > 0; decreases if x < 0 then (m - x) else x; { if x < 0 then mod(m + x, m) else if x < m then x else mod(x - m, m) } */ function div(x:int, d:int) : int requires d != 0; { x/d } function mod(x:int, d:int) : int requires d != 0; { x%d } function div_recursive(x:int, d:int) : int requires d != 0; { INTERNAL_div_recursive(x,d) } function mod_recursive(x:int, d:int) : int requires d > 0; { INTERNAL_mod_recursive(x,d) } function mod_boogie(x:int, y:int) : int requires y != 0; { x % y } //- INTERNAL_mod_boogie(x,y) } function div_boogie(x:int, y:int) : int requires y != 0; { x / y } //-{ INTERNAL_div_boogie(x,y) } function my_div_recursive(x:int, d:int) : int requires d != 0; { if d > 0 then my_div_pos(x, d) else -1 * my_div_pos(x, -1*d) } function my_div_pos(x:int, d:int) : int requires d > 0; decreases if x < 0 then (d - x) else x; { if x < 0 then -1 + my_div_pos(x+d, d) else if x < d then 0 else 1 + my_div_pos(x-d, d) } function my_mod_recursive(x:int, m:int) : int requires m > 0; decreases if x < 0 then (m - x) else x; { if x < 0 then my_mod_recursive(m + x, m) else if x < m then x else my_mod_recursive(x - m, m) } //- Kept for legacy reasons: //-static function INTERNAL_mod_boogie(x:int, m:int) : int { x % y } function INTERNAL_mod_recursive(x:int, m:int) : int requires m > 0; { my_mod_recursive(x, m) } //-static function INTERNAL_div_boogie(x:int, m:int) : int { x / m } function INTERNAL_div_recursive(x:int, d:int) : int requires d != 0; { my_div_recursive(x, d) } /* ghost method mod_test() { assert -3 % 5 == 2; assert 10 % -5 == 0; assert 1 % -5 == 1; assert -3 / 5 == -1; } */ } ================================================ FILE: Armada/util/math/div_nonlinear.i.dfy ================================================ //- //- WARNING: In general, you shouldn't need to call these directly. Try //- to use the ones in div.i.dfy instead. They're more full-featured anyway. module Math__div_nonlinear_i { // WARNING: Think three times before adding anything to this file! // Nonlinear verification is highly unstable, so even if it appears to work, // it may cause problems down the road. Thus, we want to keep this file as // small and simple as possible. Instead of adding code here, try proving // it in div.i.dfy using the connection to the recursive definition lemma lemma_div_of_0(d:int) requires d != 0; ensures 0/d == 0; { } lemma lemma_div_by_self(d:int) requires d != 0; ensures d/d == 1; { } lemma lemma_small_div() ensures forall x, d {:trigger x / d} :: 0 <= x < d && d > 0 ==> x / d == 0; { } lemma lemma_mod_of_zero_is_zero(m:int) requires 0 < m; ensures 0 % m == 0; { } lemma lemma_fundamental_div_mod(x:int, d:int) requires d != 0; ensures x == d * (x/d) + (x%d); { } lemma lemma_0_mod_anything() ensures forall m:int {:trigger 0 % m} :: m > 0 ==> 0 % m == 0; { } lemma lemma_mod_yourself(m:int) ensures m > 0 ==> m % m == 0; { } lemma lemma_small_mod(x:nat, m:nat) requires x 0; ensures 0 <= x % m < m; { } lemma lemma_real_div_gt(x:real, y:real) requires x > y; requires x >= 0.0; requires y > 0.0; ensures x / y > 1 as real; { } } ================================================ FILE: Armada/util/math/mod_auto.i.dfy ================================================ include "mod_auto_proofs.i.dfy" module Math__mod_auto_i { import opened Math__mod_auto_proofs_i predicate eq_mod(x:int, y:int, n:int) requires n > 0; { (x - y) % n == 0 // same as x % n == y % n, but easier to do induction on x - y than x and y separately } predicate ModAuto(n:int) requires n > 0; { (n % n == (-n) % n == 0) && (forall x:int {:trigger (x % n) % n} :: (x % n) % n == x % n) && (forall x:int {:trigger x % n} :: 0 <= x < n <==> x % n == x) && (forall x:int, y:int {:trigger (x + y) % n} :: (var z := (x % n) + (y % n); ( (0 <= z < n && (x + y) % n == z) || (n <= z < n + n && (x + y) % n == z - n)))) && (forall x:int, y:int {:trigger (x - y) % n} :: (var z := (x % n) - (y % n); ( (0 <= z < n && (x - y) % n == z) || (-n <= z < 0 && (x - y) % n == z + n)))) } lemma lemma_QuotientAndRemainderUnique(x:int, q:int, r:int, n:int) requires n > 0 requires 0 <= r < n requires x == q * n + r ensures q == x / n ensures r == x % n decreases if q > 0 then q else -q { lemma_mod_auto_basics(n); if q > 0 { assert q * n + r == (q - 1) * n + r + n; lemma_QuotientAndRemainderUnique(x - n, q - 1, r, n); } else if q < 0 { assert q * n + r == (q + 1) * n + r - n; lemma_QuotientAndRemainderUnique(x + n, q + 1, r, n); } } lemma lemma_mod_auto(n:int) requires n > 0; ensures ModAuto(n); { lemma_mod_auto_basics(n); forall x:int, y:int {:trigger (x + y) % n} ensures var z := (x % n) + (y % n); (0 <= z < n && (x + y) % n == z) || (n <= z < 2 * n && (x + y) % n == z - n) { var xq, xr := x / n, x % n; assert x == xq * n + xr; var yq, yr := y / n, y % n; assert y == yq * n + yr; if xr + yr < n { lemma_QuotientAndRemainderUnique(x + y, xq + yq, xr + yr, n); } else { lemma_QuotientAndRemainderUnique(x + y, xq + yq + 1, xr + yr - n, n); } } forall x:int, y:int {:trigger (x - y) % n} ensures var z := (x % n) - (y % n); (0 <= z < n && (x - y) % n == z) || (-n <= z < 0 && (x - y) % n == z + n) { var xq, xr := x / n, x % n; assert x == xq * n + xr; var yq, yr := y / n, y % n; assert y == yq * n + yr; if xr - yr >= 0 { lemma_QuotientAndRemainderUnique(x - y, xq - yq, xr - yr, n); } else { lemma_QuotientAndRemainderUnique(x - y, xq - yq - 1, xr - yr + n, n); } } } predicate TModAutoLe(x:int, y:int) { x <= y } lemma lemma_mod_auto_induction(n:int, x:int, f:imap) requires n > 0; requires forall i :: i in f; requires ModAuto(n) ==> (forall i {:trigger TModAutoLe(0, i)} :: TModAutoLe(0, i) && i < n ==> f[i]) && (forall i {:trigger TModAutoLe(0, i)} :: TModAutoLe(0, i) && f[i] ==> f[i + n]) && (forall i {:trigger TModAutoLe(i + 1, n)} :: TModAutoLe(i + 1, n) && f[i] ==> f[i - n]); ensures ModAuto(n); ensures f[x]; { lemma_mod_auto(n); assert forall i :: TModAutoLe(0, i) && i < n ==> f[i]; assert forall i {:trigger f[i], f[i + n]} :: TModAutoLe(0, i) && f[i] ==> f[i + n]; assert forall i {:trigger f[i], f[i - n]} :: TModAutoLe(i + 1, n) && f[i] ==> f[i - n]; lemma_mod_induction_forall(n, f); assert f[x]; } lemma lemma_mod_auto_induction_forall(n:int, f:imap) requires n > 0; requires forall i :: i in f; requires ModAuto(n) ==> (forall i {:trigger TModAutoLe(0, i)} :: TModAutoLe(0, i) && i < n ==> f[i]) && (forall i {:trigger TModAutoLe(0, i)} :: TModAutoLe(0, i) && f[i] ==> f[i + n]) && (forall i {:trigger TModAutoLe(i + 1, n)} :: TModAutoLe(i + 1, n) && f[i] ==> f[i - n]); ensures ModAuto(n); ensures forall i {:trigger f[i]} :: f[i]; { lemma_mod_auto(n); assert forall i :: TModAutoLe(0, i) && i < n ==> f[i]; assert forall i {:trigger f[i], f[i + n]} :: TModAutoLe(0, i) && f[i] ==> f[i + n]; assert forall i {:trigger f[i], f[i - n]} :: TModAutoLe(i + 1, n) && f[i] ==> f[i - n]; lemma_mod_induction_forall(n, f); } /* TODO: if we need these at all, they should have better triggers to protect call sites lemma lemma_mod_auto_induction2(x:int, y:int, n:int, f:imap<(int,int),bool>) requires n > 0; requires forall i, j :: (i, j) in f; requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: 0 <= i < n && 0 <= j < n ==> f[(i, j)]); requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: i >= 0 && f[(i, j)] ==> f[(i + n, j)]); requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: j >= 0 && f[(i, j)] ==> f[(i, j + n)]); requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: i < n && f[(i, j)] ==> f[(i - n, j)]); requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: j < n && f[(i, j)] ==> f[(i, j - n)]); ensures ModAuto(n); ensures f[(x, y)]; { lemma_mod_auto(n); lemma_mod_induction_forall2(n, f); assert f[(x, y)]; } lemma lemma_mod_auto_induction_forall2(n:int, f:imap<(int,int),bool>) requires n > 0; requires forall i, j :: (i, j) in f; requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: 0 <= i < n && 0 <= j < n ==> f[(i, j)]); requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: i >= 0 && f[(i, j)] ==> f[(i + n, j)]); requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: j >= 0 && f[(i, j)] ==> f[(i, j + n)]); requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: i < n && f[(i, j)] ==> f[(i - n, j)]); requires ModAuto(n) ==> (forall i, j {:trigger f[(i, j)]} :: j < n && f[(i, j)] ==> f[(i, j - n)]); ensures ModAuto(n); ensures forall i, j {:trigger f[(i, j)]} :: f[(i, j)]; { lemma_mod_auto(n); lemma_mod_induction_forall2(n, f); } */ } ================================================ FILE: Armada/util/math/mod_auto_proofs.i.dfy ================================================ include "mul_auto.i.dfy" include "mul.i.dfy" include "div_nonlinear.i.dfy" module Math__mod_auto_proofs_i { import opened Math__mul_auto_i import opened Math__mul_i import opened Math__div_nonlinear_i lemma lemma_mod_induction_helper(n:int, f:imap, x:int) requires n > 0; requires forall i :: i in f; requires forall i :: 0 <= i < n ==> f[i]; requires forall i {:trigger f[i], f[i + n]} :: i >= 0 && f[i] ==> f[i + n]; requires forall i {:trigger f[i], f[i - n]} :: i < n && f[i] ==> f[i - n]; ensures f[x]; decreases if x >= n then x else -x; { if (x >= n) { lemma_mod_induction_helper(n, f, x - n); assert f[(x - n) + n]; } else if (x < 0) { lemma_mod_induction_helper(n, f, x + n); assert f[(x + n) - n]; } } lemma lemma_mod_induction_forall(n:int, f:imap) requires n > 0; requires forall i :: i in f; requires forall i :: 0 <= i < n ==> f[i]; requires forall i {:trigger f[i], f[i + n]} :: i >= 0 && f[i] ==> f[i + n]; requires forall i {:trigger f[i], f[i - n]} :: i < n && f[i] ==> f[i - n]; ensures forall i :: f[i]; { forall i ensures f[i] { lemma_mod_induction_helper(n, f, i); } } lemma lemma_mod_induction_forall2(n:int, f:imap<(int,int),bool>) requires n > 0; requires forall i, j :: (i, j) in f; requires forall i, j :: 0 <= i < n && 0 <= j < n ==> f[(i, j)]; requires forall i, j {:trigger f[(i, j)], f[(i + n, j)]} :: i >= 0 && f[(i, j)] ==> f[(i + n, j)]; requires forall i, j {:trigger f[(i, j)], f[(i, j + n)]} :: j >= 0 && f[(i, j)] ==> f[(i, j + n)]; requires forall i, j {:trigger f[(i, j)], f[(i - n, j)]} :: i < n && f[(i, j)] ==> f[(i - n, j)]; requires forall i, j {:trigger f[(i, j)], f[(i, j - n)]} :: j < n && f[(i, j)] ==> f[(i, j - n)]; ensures forall i, j :: f[(i, j)]; { forall x, y ensures f[(x, y)]; { forall i | 0 <= i < n ensures f[(i, y)]; { var fj := imap j :: f[(i, j)]; lemma_mod_induction_forall(n, fj); assert fj[y]; } var fi := imap i :: f[(i, y)]; lemma_mod_induction_forall(n, fi); assert fi[x]; } } lemma lemma_mod_auto_basics(n:int) requires n > 0; ensures forall x:int {:trigger (x + n) % n} :: (x + n) % n == x % n; ensures forall x:int {:trigger (x - n) % n} :: (x - n) % n == x % n; ensures forall x:int {:trigger (x + n) / n} :: (x + n) / n == x / n + 1; ensures forall x:int {:trigger (x - n) / n} :: (x - n) / n == x / n - 1; ensures forall x:int {:trigger x % n} :: 0 <= x < n <==> x % n == x; { forall x:int ensures 0 <= x < n <==> x % n == x; { if (0 <= x < n) { lemma_small_mod(x, n); } lemma_mod_range(x, n); } forall x:int ensures (x + n) % n == x % n; ensures (x - n) % n == x % n; ensures (x + n) / n == x / n + 1; ensures (x - n) / n == x / n - 1; { lemma_fundamental_div_mod(x, n); lemma_fundamental_div_mod(x + n, n); lemma_fundamental_div_mod(x - n, n); lemma_mod_range(x, n); lemma_mod_range(x + n, n); lemma_mod_range(x - n, n); var zp := (x + n) / n - x / n - 1; var zm := (x - n) / n - x / n + 1; forall ensures 0 == n * zp + ((x + n) % n) - (x % n) { lemma_mul_auto(); } forall ensures 0 == n * zm + ((x - n) % n) - (x % n) { lemma_mul_auto(); } if (zp > 0) { lemma_mul_inequality(1, zp, n); } if (zp < 0) { lemma_mul_inequality(zp, -1, n); } if (zm > 0) { lemma_mul_inequality(1, zm, n); } if (zm < 0) { lemma_mul_inequality(zm, -1, n); } } } } ================================================ FILE: Armada/util/math/mul.i.dfy ================================================ include "mul_nonlinear.i.dfy" include "mul_auto.i.dfy" module Math__mul_i { import opened Math__mul_nonlinear_i import opened Math__mul_auto_i // TODO_MODULE: module Math__mul_i { // TODO_MODULE: import opened Math__mul_nonlinear_i function mul(x:int, y:int) : int { x*y } //-//////////////////////////////////////////////////////////// //- Recursive definitions that can be handy for proving //- properties we can't or don't want to rely on nonlinear for //-//////////////////////////////////////////////////////////// function mul_recursive(x:int, y:int) : int { if x >= 0 then mul_pos(x, y) else -1*mul_pos(-1*x, y) } function{:opaque} mul_pos(x:int, y:int) : int requires x >= 0; { if x == 0 then 0 else y + mul_pos(x - 1, y) } lemma lemma_mul_is_mul_recursive(x:int, y:int) ensures x * y == mul_recursive(x, y); { if (x >= 0) { lemma_mul_is_mul_pos(x, y); } if (x <= 0) { lemma_mul_is_mul_pos(-x, y); } lemma_mul_auto(); } lemma lemma_mul_is_mul_pos(x:int, y:int) requires x >= 0; ensures x * y == mul_pos(x, y); { reveal_mul_pos(); lemma_mul_auto_induction(x, imap u :: u >= 0 ==> u * y == mul_pos(u, y)); } //-//////////////////////////////////////////////////////////////////////////// //- //- Core lemmas, with named arguments. //- //-//////////////////////////////////////////////////////////////////////////// lemma lemma_mul_basics(x:int) ensures 0*x == 0; ensures x*0 == 0; ensures 1*x == x; ensures x*1 == x; { } lemma lemma_mul_is_commutative(x:int, y:int) ensures x*y == y*x; { } lemma lemma_mul_ordering_general() ensures forall x:int, y:int {:trigger x*y} :: (0 < x && 0 < y && 0 <= x*y) ==> x <= x*y && y <= x*y; { forall x:int, y:int | 0 < x && 0 < y && 0 <= x*y ensures x <= x*y && y <= x*y; { lemma_mul_ordering(x, y); } } lemma lemma_mul_is_mul_boogie(x:int, y:int) { } lemma lemma_mul_inequality(x:int, y:int, z:int) requires x <= y; requires z >= 0; ensures x*z <= y*z; { lemma_mul_auto_induction(z, imap u :: u >= 0 ==> x * u <= y * u); } lemma lemma_mul_upper_bound(x:int, x_bound:int, y:int, y_bound:int) requires x <= x_bound; requires y <= y_bound; requires 0<=x; requires 0<=y; ensures x*y <= x_bound * y_bound; { lemma_mul_inequality(x, x_bound, y); lemma_mul_inequality(y, y_bound, x_bound); } //- This lemma is less precise than the non-strict version, since //- it uses two < facts to achieve only one < result. Thus, use it with //- caution -- it may be throwing away precision you'll require later. lemma lemma_mul_strict_upper_bound(x:int, x_bound:int, y:int, y_bound:int) requires x < x_bound; requires y < y_bound; requires 0<=x; requires 0<=y; ensures x*y < x_bound * y_bound; { lemma_mul_auto_induction(x, imap u :: 0 <= u ==> u * y <= u * y_bound); lemma_mul_auto_induction(y_bound, imap u :: 1 <= u ==> x * u < x_bound * u); } lemma lemma_mul_left_inequality(x:int, y:int, z:int) requires x > 0; ensures y <= z ==> x*y <= x*z; ensures y < z ==> x*y < x*z; { lemma_mul_auto_induction(x, imap u :: u > 0 ==> y <= z ==> u*y <= u*z); lemma_mul_auto_induction(x, imap u :: u > 0 ==> y < z ==> u*y < u*z); } lemma lemma_mul_strict_inequality_converse(x:int, y:int, z:int) requires x*z < y*z; requires z >= 0; ensures x < y; { lemma_mul_auto_induction(z, imap u :: x * u < y * u && u >= 0 ==> x < y); } lemma lemma_mul_inequality_converse(x:int, y:int, z:int) requires x*z <= y*z; requires z > 0; ensures x <= y; { lemma_mul_auto_induction(z, imap u :: x * u <= y * u && u > 0 ==> x <= y); } lemma lemma_mul_equality_converse(x:int, y:int, z:int) requires x*z == y*z; requires 0 y && 0 < u ==> x * u > y * u); lemma_mul_auto_induction(z, imap u :: x < y && 0 < u ==> x * u < y * u); } lemma lemma_mul_is_distributive_add_other_way(x:int, y:int, z:int) ensures (y + z)*x == y*x + z*x; { lemma_mul_auto(); } lemma lemma_mul_is_distributive_sub(x:int, y:int, z:int) ensures x*(y - z) == x*y - x*z; { lemma_mul_auto(); } lemma lemma_mul_is_distributive(x:int, y:int, z:int) ensures x*(y + z) == x*y + x*z; ensures x*(y - z) == x*y - x*z; ensures (y + z)*x == y*x + z*x; ensures (y - z)*x == y*x - z*x; ensures x*(y + z) == (y + z)*x; ensures x*(y - z) == (y - z)*x; ensures x*y == y*x; ensures x*z == z*x; { lemma_mul_auto(); } lemma lemma_mul_strictly_increases(x:int, y:int) requires 1 < x; requires 0 < y; ensures y < x*y; { lemma_mul_auto_induction(x, imap u :: 1 < u ==> y < u * y); } lemma lemma_mul_increases(x:int, y:int) requires 0 y <= u * y); } lemma lemma_mul_nonnegative(x:int, y:int) requires 0 <= x; requires 0 <= y; ensures 0 <= x*y; { lemma_mul_auto_induction(x, imap u :: 0 <= u ==> 0 <= u * y); } lemma lemma_mul_unary_negation(x:int, y:int) ensures (-x)*y == -(x*y) == x*(-y); { lemma_mul_auto_induction(x, imap u :: (-u)*y == -(u*y) == u*(-y)); } // TODO: delete lemma_mul_one_to_one_pos; use lemma_mul_one_to_one instead lemma lemma_mul_one_to_one_pos(m:int, x:int, y:int) requires 0 y && 0 < u ==> x * u > y * u); lemma_mul_auto_induction(m, imap u :: x < y && 0 < u ==> x * u < y * u); } lemma lemma_mul_one_to_one(m:int, x:int, y:int) requires m!=0; requires m*x == m*y; ensures x == y; { lemma_mul_auto_induction(m, imap u :: x > y && 0 < u ==> x * u > y * u); lemma_mul_auto_induction(m, imap u :: x > y && 0 > u ==> x * u < y * u); lemma_mul_auto_induction(m, imap u :: x < y && 0 < u ==> x * u < y * u); lemma_mul_auto_induction(m, imap u :: x < y && 0 > u ==> x * u > y * u); } //-//////////////////////////////////////////////////////////////////////////// //- //- Forall lemmas: these restate the core lemmas with foralls, //- so callers needn't name the specific expressions to manipulate. //- //- These are all boilerplate transformations of args/requires/ensures //- into forall args :: requires ==> ensures, with a correpsonding //- mechanically generated forall proof that invokes the core lemma. // So don't bother reading them. //- //-//////////////////////////////////////////////////////////////////////////// lemma lemma_mul_is_mul_recursive_forall() ensures forall x:int, y:int :: x * y == mul_recursive(x, y); { forall x:int, y:int ensures x * y == mul_recursive(x, y); { lemma_mul_is_mul_recursive(x, y); } } lemma lemma_mul_basics_forall() ensures forall x:int {:trigger 0*x} :: 0*x == 0; ensures forall x:int {:trigger x*0} :: x*0 == 0; ensures forall x:int {:trigger 1*x} :: 1*x == x; ensures forall x:int {:trigger x*1} :: x*1 == x; { } lemma lemma_mul_is_commutative_forall() ensures forall x:int, y:int {:trigger x*y} :: x*y == y*x; { } lemma lemma_mul_ordering_forall() ensures forall x:int, y:int {:trigger x*y} :: 0 < x && 0 < y && 0 <= x*y ==> x <= x*y && y <= x*y; { forall x:int, y:int | 0 < x && 0 < y && 0 <= x*y ensures x <= x*y && y <= x*y; { lemma_mul_ordering(x,y); } } lemma lemma_mul_strict_inequality_forall() ensures forall x:int, y:int, z:int {:trigger x*z, y*z} :: x < y && z>0 ==> x*z < y*z; { forall (x:int, y:int, z:int | x < y && z>0) ensures x*z < y*z; { lemma_mul_strict_inequality(x, y, z); } } lemma lemma_mul_inequality_forall() ensures forall x:int, y:int, z:int {:trigger x*z, y*z} :: x <= y && z>=0 ==> x*z <= y*z; { forall (x:int, y:int, z:int | x <= y && z>=0) ensures x*z <= y*z; { lemma_mul_inequality(x, y, z); } } lemma lemma_mul_strict_inequality_converse_forall() ensures forall x:int, y:int, z:int {:trigger x*z, y*z} :: x*z < y*z && z>=0 ==> x < y; { forall (x:int, y:int, z:int | x*z < y*z && z>=0) ensures x < y; { lemma_mul_strict_inequality_converse(x,y,z); } } lemma lemma_mul_inequality_converse_forall() ensures forall x:int, y:int, z:int {:trigger x*z, y*z} :: x*z <= y*z && z>0 ==> x <= y; { forall (x:int, y:int, z:int | x*z <= y*z && z>0) ensures x <= y; { lemma_mul_inequality_converse(x,y,z); } } lemma lemma_mul_is_distributive_add_forall() ensures forall x:int, y:int, z:int {:trigger x*(y + z)} :: x*(y + z) == x*y + x*z; { forall (x:int, y:int, z:int) ensures x*(y + z) == x*y + x*z; { lemma_mul_is_distributive_add(x,y,z); } } lemma lemma_mul_is_distributive_sub_forall() ensures forall x:int, y:int, z:int {:trigger x*(y - z)} :: x*(y - z) == x*y - x*z; { forall (x:int, y:int, z:int) ensures x*(y - z) == x*y - x*z; { lemma_mul_is_distributive_sub(x,y,z); } } lemma lemma_mul_is_distributive_forall() ensures forall x:int, y:int, z:int {:trigger x*(y + z)} :: x*(y + z) == x*y + x*z; ensures forall x:int, y:int, z:int {:trigger x*(y - z)} :: x*(y - z) == x*y - x*z; ensures forall x:int, y:int, z:int {:trigger (y + z)*x} :: (y + z)*x == y*x + z*x; ensures forall x:int, y:int, z:int {:trigger (y - z)*x} :: (y - z)*x == y*x - z*x; { lemma_mul_is_distributive_add_forall(); lemma_mul_is_distributive_sub_forall(); lemma_mul_is_commutative_forall(); } lemma lemma_mul_is_associative_forall() ensures forall x:int, y:int, z:int {:trigger x * (y * z)}{:trigger (x * y) * z} :: x * (y * z) == (x * y) * z; { forall (x:int, y:int, z:int) ensures x * (y * z) == (x * y) * z; { lemma_mul_is_associative(x,y,z); } } lemma lemma_mul_nonzero_forall() ensures forall x:int, y:int {:trigger x*y} :: x*y != 0 <==> x != 0 && y != 0; { forall (x:int, y:int) ensures x*y != 0 <==> x != 0 && y != 0; { lemma_mul_nonzero(x,y); } } lemma lemma_mul_nonnegative_forall() ensures forall x:int, y:int {:trigger x*y} :: 0 <= x && 0 <= y ==> 0 <= x*y; { forall (x:int, y:int | 0 <= x && 0 <= y) ensures 0 <= x*y; { lemma_mul_nonnegative(x,y); } } lemma lemma_mul_unary_negation_forall() ensures forall x:int, y:int {:trigger (-x)*y}{:trigger x*(-y)} :: (-x)*y == -(x*y) == x*(-y); { forall (x:int, y:int) ensures (-x)*y == -(x*y) == x*(-y); { lemma_mul_unary_negation(x,y); } } lemma lemma_mul_strictly_increases_forall() ensures forall x:int, y:int {:trigger x*y} :: (1 < x && 0 < y) ==> (y < x*y); { forall (x:int, y:int | 1 < x && 0 < y) ensures y < x*y; { lemma_mul_strictly_increases(x,y); } } lemma lemma_mul_increases_forall() ensures forall x:int, y:int {:trigger x*y} :: (0 < x && 0 < y) ==> (y <= x*y); { forall (x:int, y:int | 0 < x && 0 < y) ensures y <= x*y; { lemma_mul_increases(x,y); } } lemma lemma_mul_strictly_positive_forall() ensures forall x:int, y:int {:trigger x*y} :: (0 < x && 0 < y) ==> (0 < x*y); { forall (x:int, y:int | 0 < x && 0 < y) ensures 0 < x*y; { lemma_mul_strictly_positive(x,y); } } lemma lemma_mul_one_to_one_forall() ensures forall m:int, x:int, y:int {:trigger m*x, m*y} :: (m!=0 && m*x == m*y) ==> x==y; { forall (m:int, x:int, y:int | m!=0 && m*x == m*y) ensures x==y; { lemma_mul_one_to_one(m, x, y); } } ////////////////////////////////////////////////////////////////////////////// // // The big properties bundle. This can be a little dangerous, because // it may produce a trigger storm. Whether it does seems to depend on // how complex the expressions being mul'ed are. If that happens, // fall back on specifying an individiual _forall lemma or use // lemma_mul_auto/lemma_mul_auto_induction. // ////////////////////////////////////////////////////////////////////////////// lemma lemma_mul_properties() ensures forall x:int, y:int {:trigger x*y} :: x*y == y*x; // ensures forall x:int {:trigger x*0}{:trigger 0*x} :: x*0 == 0*x == 0; // ensures forall x:int {:trigger x*1}{:trigger 1*x} :: x*1 == 1*x == x; ensures forall x:int, y:int, z:int {:trigger x*z, y*z} :: x < y && z > 0 ==> x*z < y*z; ensures forall x:int, y:int, z:int {:trigger x*z, y*z} :: x <= y && z >= 0 ==> x*z <= y*z; ensures forall x:int, y:int, z:int {:trigger x*(y + z)} :: x*(y + z) == x*y + x*z; ensures forall x:int, y:int, z:int {:trigger x*(y - z)} :: x*(y - z) == x*y - x*z; ensures forall x:int, y:int, z:int {:trigger (y + z)*x} :: (y + z)*x == y*x + z*x; ensures forall x:int, y:int, z:int {:trigger (y - z)*x} :: (y - z)*x == y*x - z*x; ensures forall x:int, y:int, z:int {:trigger x*(y*z)}{:trigger (x*y)*z} :: x*(y*z) == (x*y)*z; ensures forall x:int, y:int {:trigger x*y} :: x*y != 0 <==> x != 0 && y != 0; ensures forall x:int, y:int {:trigger x*y} :: 0 <= x && 0 <= y ==> 0 <= x*y; // ensures forall x:int, y:int {:trigger x*y} :: 0 < x && 0 < y && 0 <= x*y ==> x <= x*y && y <= x*y; // ensures forall x:int, y:int {:trigger x*y} :: (1 < x && 0 < y) ==> (y < x*y); // ensures forall x:int, y:int {:trigger x*y} :: (0 < x && 0 < y) ==> (y <= x*y); // ensures forall x:int, y:int {:trigger x*y} :: (0 < x && 0 < y) ==> (0 < x*y); { lemma_mul_strict_inequality_forall(); lemma_mul_inequality_forall(); lemma_mul_is_distributive_forall(); lemma_mul_is_associative_forall(); lemma_mul_ordering_forall(); lemma_mul_nonzero_forall(); lemma_mul_nonnegative_forall(); lemma_mul_strictly_increases_forall(); lemma_mul_increases_forall(); } lemma lemma_mul_cancels_negatives(a:int, b:int) ensures a*b == (-a)*(-b); { lemma_mul_auto_induction(a, imap u :: u*b == (-u)*(-b)); } //- Kept for legacy reasons: function INTERNAL_mul_recursive(x:int, y:int) : int { mul_recursive(x, y) } // TODO_MODULE: } import opened Math__mul_i_ = Math__mul_i } ================================================ FILE: Armada/util/math/mul_auto.i.dfy ================================================ include "mul_auto_proofs.i.dfy" module Math__mul_auto_i { import opened Math__mul_auto_proofs_i predicate MulAuto() { (forall x:int, y:int {:trigger x * y} :: x * y == y * x) && (forall x:int, y:int, z:int {:trigger (x + y) * z} :: (x + y) * z == x * z + y * z) && (forall x:int, y:int, z:int {:trigger (x - y) * z} :: (x - y) * z == x * z - y * z) } lemma lemma_mul_auto() ensures MulAuto(); { lemma_mul_auto_commutes(); lemma_mul_auto_distributes(); } predicate TMulAutoLe(x:int, y:int) { x <= y } lemma lemma_mul_auto_induction(x:int, f:imap) requires forall i :: i in f; requires MulAuto() ==> f[0] && (forall i {:trigger TMulAutoLe(0, i)} :: TMulAutoLe(0, i) && f[i] ==> f[i + 1]) && (forall i {:trigger TMulAutoLe(i, 0)} :: TMulAutoLe(i, 0) && f[i] ==> f[i - 1]); ensures MulAuto(); ensures f[x]; { lemma_mul_auto_commutes(); lemma_mul_auto_distributes(); assert forall i {:trigger f[i]} :: TMulAutoLe(0, i) && f[i] ==> f[i + 1]; assert forall i {:trigger f[i]} :: TMulAutoLe(i, 0) && f[i] ==> f[i - 1]; lemma_mul_induction_forall(f); assert f[x]; } lemma lemma_mul_auto_induction_forall(f:imap) requires forall i :: i in f; requires MulAuto() ==> f[0] && (forall i {:trigger TMulAutoLe(0, i)} :: TMulAutoLe(0, i) && f[i] ==> f[i + 1]) && (forall i {:trigger TMulAutoLe(i, 0)} :: TMulAutoLe(i, 0) && f[i] ==> f[i - 1]); ensures MulAuto(); ensures forall i {:trigger f[i]} :: f[i]; { lemma_mul_auto_commutes(); lemma_mul_auto_distributes(); assert forall i {:trigger f[i]} :: TMulAutoLe(0, i) && f[i] ==> f[i + 1]; assert forall i {:trigger f[i]} :: TMulAutoLe(i, 0) && f[i] ==> f[i - 1]; lemma_mul_induction_forall(f); } } ================================================ FILE: Armada/util/math/mul_auto_proofs.i.dfy ================================================ include "mul_nonlinear.i.dfy" module Math__mul_auto_proofs_i { import opened Math__mul_nonlinear_i lemma lemma_mul_induction_helper(f:imap, x:int) requires forall i :: i in f; requires f[0]; requires forall i {:trigger f[i], f[i + 1]} :: i >= 0 && f[i] ==> f[i + 1]; requires forall i {:trigger f[i], f[i - 1]} :: i <= 0 && f[i] ==> f[i - 1]; ensures f[x]; decreases if x >= 0 then x else -x; { if (x > 0) { lemma_mul_induction_helper(f, x - 1); assert f[(x - 1) + 1]; } else if (x < 0) { lemma_mul_induction_helper(f, x + 1); assert f[(x + 1) - 1]; } } lemma lemma_mul_induction_forall(f:imap) requires forall i :: i in f; requires f[0]; requires forall i {:trigger f[i], f[i + 1]} :: i >= 0 && f[i] ==> f[i + 1]; requires forall i {:trigger f[i], f[i - 1]} :: i <= 0 && f[i] ==> f[i - 1]; ensures forall i :: f[i]; { forall i ensures f[i] { lemma_mul_induction_helper(f, i); } } lemma lemma_mul_auto_commutes() ensures forall x:int, y:int {:trigger x * y} :: x * y == y * x; { forall x:int, y:int ensures x * y == y * x; { lemma_mul_induction_forall(imap i :: x * i == i * x); } } lemma lemma_mul_auto_succ() ensures forall x:int, y:int {:trigger (x + 1) * y} :: (x + 1) * y == x * y + y; ensures forall x:int, y:int {:trigger (x - 1) * y} :: (x - 1) * y == x * y - y; { lemma_mul_auto_commutes(); forall x:int, y:int ensures (x + 1) * y == x * y + y; ensures (x - 1) * y == x * y - y; { lemma_mul_is_distributive_add(y, x, 1); lemma_mul_is_distributive_add(y, x, -1); } } lemma lemma_mul_auto_distributes() ensures forall x:int, y:int, z:int {:trigger (x + y) * z} :: (x + y) * z == x * z + y * z; ensures forall x:int, y:int, z:int {:trigger (x - y) * z} :: (x - y) * z == x * z - y * z; { lemma_mul_auto_succ(); forall x:int, y:int, z:int ensures (x + y) * z == x * z + y * z; ensures (x - y) * z == x * z - y * z; { var f1 := imap i :: (x + i) * z == x * z + i * z; var f2 := imap i :: (x - i) * z == x * z - i * z; assert forall i {:trigger (x + (i + 1)) * z} :: (x + (i + 1)) * z == ((x + i) + 1) * z == (x + i) * z + z; assert forall i {:trigger (x + (i - 1)) * z} :: (x + (i - 1)) * z == ((x + i) - 1) * z == (x + i) * z - z; assert forall i {:trigger (x - (i + 1)) * z} :: (x - (i + 1)) * z == ((x - i) - 1) * z == (x - i) * z - z; assert forall i {:trigger (x - (i - 1)) * z} :: (x - (i - 1)) * z == ((x - i) + 1) * z == (x - i) * z + z; lemma_mul_induction_forall(f1); lemma_mul_induction_forall(f2); assert f1[y]; assert f2[y]; } } } ================================================ FILE: Armada/util/math/mul_nonlinear.i.dfy ================================================ //- //- WARNING: In general, you shouldn't need to call these directly. Try //- to use the ones in mul.i.dfy instead. They're more full-featured anyway. module Math__mul_nonlinear_i { // TODO_MODULE: module Math__mul_nonlinear_i { // WARNING: Think three times before adding anything to this file! // Nonlinear verification is highly unstable, so even if it appears to work, // it may cause problems down the road. Thus, we want to keep this file as // small and simple as possible. Instead of adding code here, try proving // it in div.i.dfy using the connection to the recursive definition lemma lemma_mul_strictly_positive(x:int, y:int) ensures (0 < x && 0 < y) ==> (0 < x*y); {} lemma lemma_mul_nonzero(x:int, y:int) ensures x*y != 0 <==> x != 0 && y != 0; {} lemma lemma_mul_is_associative(x:int, y:int, z:int) ensures x * (y * z) == (x * y) * z; {} lemma lemma_mul_is_distributive_add(x:int, y:int, z:int) ensures x*(y + z) == x*y + x*z; {} lemma lemma_mul_ordering(x:int, y:int) requires 0 < x; requires 0 < y; requires 0 <= x*y; ensures x <= x*y && y <= x*y; { } lemma lemma_mul_strict_inequality(x:int, y:int, z:int) requires x < y; requires z > 0; ensures x*z < y*z; {} // TODO_MODULE: } import opened Math__mul_nonlinear_i_ = Math__mul_nonlinear_i } ================================================ FILE: Armada/util/math/power.i.dfy ================================================ include "powers.i.dfy" include "mul.i.dfy" include "mul_auto.i.dfy" module Math__power_i { import opened Math__power_s import opened Math__mul_i import opened Math__mul_auto_i //-lemma lemma_mul_passes_harmlessly_through_mod( //- ensures mul(x,y) % m == mul(x lemma lemma_power_0(b:int) ensures power(b,0) == 1; { reveal_power(); } lemma lemma_power_1(b:int) ensures power(b,1) == b; { calc { power(b,1); { reveal_power(); } b*power(b,0); { lemma_power_0(b); } b*1; { lemma_mul_basics_forall(); } b; } } lemma lemma_0_power(e:nat) requires e > 0; ensures power(0,e) == 0; { reveal_power(); lemma_mul_basics_forall(); if (e != 1) { lemma_0_power(e - 1); } } lemma lemma_1_power(e:nat) ensures power(1,e) == 1; { reveal_power(); lemma_mul_basics_forall(); if (e != 0) { lemma_1_power(e - 1); } } lemma lemma_power_adds(b:int, e1:nat, e2:nat) decreases e1; ensures power(b,e1)*power(b,e2) == power(b,e1+e2); { if (e1==0) { calc { power(b,e1)*power(b,e2); { lemma_power_0(b); } 1*power(b,e2); { lemma_mul_basics_forall(); } power(b,0+e2); } } else { calc { power(b,e1)*power(b,e2); { reveal_power(); } (b*power(b,e1-1))*power(b,e2); { lemma_mul_is_associative_forall(); } b*(power(b,e1-1)*power(b,e2)); { lemma_power_adds(b, e1-1, e2); } b*power(b,e1-1+e2); { reveal_power(); } power(b,e1+e2); } } } lemma lemma_power_multiplies(a:int,b:nat,c:nat) decreases c; ensures 0<=b*c; ensures power(a,b*c) == power(power(a,b),c); { lemma_mul_nonnegative(b,c); if (0==c) { lemma_mul_basics_forall(); calc { power(a,b*c); { lemma_power_0(a); } 1; { lemma_power_0(power(a,b)); } power(power(a,b),c); } } else { calc { b*c - b; { lemma_mul_basics_forall(); } b*c - mul(b,1); { lemma_mul_is_distributive_forall(); } b*(c-1); } lemma_mul_nonnegative(b,c-1); assert 0 <= b*c-b; calc { power(a,b*c); power(a,b+b*c-b); { lemma_power_adds(a,b,b*c-b); } power(a,b)*power(a,b*c-b); power(a,b)*power(a,b*(c-1)); { lemma_power_multiplies(a,b,c-1); } power(a,b)*power(power(a,b),c-1); { reveal_power(); } power(power(a,b),c); } } } lemma lemma_power_distributes(a:int, b:int, e:nat) decreases e; ensures power(a*b, e) == power(a, e) * power(b, e); { reveal_power(); lemma_mul_basics_forall(); if (e > 0) { calc { power(a*b, e); (a*b) * power(a*b, e - 1); { lemma_power_distributes(a, b, e - 1); } (a*b) * (power(a, e - 1) * power(b, e - 1)); { lemma_mul_is_associative_forall(); lemma_mul_is_commutative_forall(); } (a*power(a, e - 1)) * (b*power(b, e - 1)); power(a,e) * power(b,e); } lemma_mul_is_distributive_forall(); } } lemma lemma_power_auto() ensures forall x:int {:trigger power(x, 0)} :: power(x, 0) == 1; ensures forall x:int {:trigger power(x, 1)} :: power(x, 1) == x; ensures forall x:int, y:int {:trigger power(x, y)} :: y == 0 ==> power(x, y) == 1; // REVIEW: because of Dafny's LitInt special treatment, these are not the same as the two ensures above ensures forall x:int, y:int {:trigger power(x, y)} :: y == 1 ==> power(x, y) == x; // ... ensures forall x:int, y:int {:trigger x * y} :: 0 < x && 0 < y ==> x <= x * y; ensures forall x:int, y:int {:trigger x * y} :: 0 < x && 1 < y ==> x < x * y; ensures forall x:int, y:nat, z:nat {:trigger power(x, y + z)} :: power(x, y + z) == power(x, y) * power(x, z); ensures forall x:int, y:nat, z:nat {:trigger power(x, y - z)} :: y >= z ==> power(x, y - z) * power(x, z) == power(x, y); ensures forall x:int, y:int, z:nat {:trigger power(x * y, z)} :: power(x * y, z) == power(x, z) * power(y, z); { forall x:int ensures power(x, 0) == 1; ensures power(x, 1) == x; { lemma_power_0(x); lemma_power_1(x); } forall x:int, y:int, z:nat ensures power(x * y, z) == power(x, z) * power(y, z); { lemma_power_distributes(x, y, z); } forall x:int, y:nat, z:nat ensures power(x, y + z) == power(x, y) * power(x, z); { lemma_power_adds(x, y, z); } lemma_mul_auto(); lemma_mul_increases_forall(); lemma_mul_strictly_increases_forall(); } lemma lemma_power_positive(b:int, e:nat) requires 0 0 < power(b, u)); } lemma lemma_power_increases(b:nat,e1:nat,e2:nat) requires 0 power(b, e1) <= power(b, e1 + e)); } lemma lemma_power_strictly_increases(b:nat,e1:nat,e2:nat) requires 1 power(b, e1) < power(b, e1 + e)); } lemma lemma_square_is_power_2(x:nat) ensures power(x,2) == x*x; { reveal_power(); } } ================================================ FILE: Armada/util/math/powers.i.dfy ================================================ module Math__power_s { // TODO_MODULE: module Math__power_s { function {:opaque} power(b:int, e:nat) : int decreases e; { if (e==0) then 1 else b*power(b,e-1) } // TODO_MODULE: } import opened Math__power_s_ = Math__power_s } ================================================ FILE: Armada/util/option.s.dfy ================================================ module util_option_s { datatype Option = Some(v:T) | None() } ================================================ FILE: Armada/util/types.s.dfy ================================================ module util_types_s { newtype byte = x:int | 0 <= x < 0x100 newtype uint = x:int | 0 <= x < 0x1_0000_0000 } ================================================ FILE: BUILD.md ================================================ Building on Linux === These instructions are adapted from the [Dafny Wiki](https://github.com/dafny-lang/dafny/wiki/INSTALL). They assume that `BASE-DIRECTORY` is the base directory in which you want to build Armada. 1. Install .NET 5.0 following the instructions [here](https://docs.microsoft.com/en-us/dotnet/core/install/). 2. Create the base directory with ``` mkdir BASE-DIRECTORY ``` 3. Download Dafny v3.0.0 with ``` cd BASE-DIRECTORY wget https://github.com/dafny-lang/dafny/releases/download/v3.0.0/dafny-3.0.0-x64-debian-8.11.zip unzip dafny-3.0.0-x64-debian-8.11.zip ``` 4. Download and build Armada with: ``` cd BASE-DIRECTORY git clone https://github.com/microsoft/armada dotnet build armada/Source/Armada.sln ``` You should then find an executable named `Armada.dll` in the directory `armada/Binaries/`. 5. To run the tests, you'll also need to install [scons](https://scons.org). Building on Windows/Mac OS === Building on other platforms is similar to building on Linux. You will need to download the correct Dafny binaries for your platform in step 3. On the Windows platform, the produced binary is `Armada.exe` instead of `Armada.dll`. ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Microsoft Open Source Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). Resources: - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns ================================================ FILE: LICENSE ================================================ Armada Copyright (c) Microsoft Corporation; Jacob R. Lorch, Microsoft Corporation; Yixuan Chen, University of Michigan and Yale University; Seyed Armin Vakil Ghahani, University of Michigan; Yuchen Jiang, University of Michigan; Manos Kapritsos, University of Michigan; Haojun Ma, University of Michigan; Bryan Parno, Carnegie Mellon University; Upamanyu Sharma, University of Michigan; James R. Wilcox, Microsoft Corporation and University of Washington; Yanlong Yao, University of Michigan; Xueyuan Zhao, Carnegie Mellon University MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE ================================================ FILE: NOTICE.txt ================================================ This project uses third party material from the projects listed below. The original copyright notice and the license under which Microsoft received such third party material are set forth below. Microsoft reserves all other rights not expressly granted, whether by implication, estoppel or otherwise. ---------------------------------------------------------------------- Dafny Copyright (c) Microsoft Corporation All rights reserved. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------- Compiler Generator Coco/R, Copyright (c) 1990, 2005 Hanspeter Moessenboeck, University of Linz extended by M. Loeberbauer & A. Woess, Univ. of Linz with improvements by Pat Terry, Rhodes University This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. As an exception, it is allowed to write an extension of Coco/R that is used as a plugin in non-free software. If not otherwise stated, any source code generated by Coco/R (other than Coco/R itself) does not fall under the GNU General Public License. ---------------------------------------------------------------------- liblfds You are free to use this library in any way. Go forth and create wealth! If for legal reasons a custom licence is required, the license of your choice will be granted, and license is hereby granted up front for a range of popular licenses : the MIT license, the BSD license, the Apache license, the GPL and LPGL (all versions thereof) and the Creative Commons licenses (all of them). Additionally, everything is also placed in the public domain. ================================================ FILE: README.md ================================================ # Overview Safely writing high-performance concurrent programs is notoriously difficult. To aid developers, we introduce Armada, a language and tool designed to formally verify such programs with relatively little effort. Via a C-like language that compiles to the C subset ClightTSO and a small-step, state-machine-based semantics, Armada gives developers the flexibility to choose arbitrary memory layout and synchronization primitives so they are never constrained in their pursuit of performance. To reduce developer effort, Armada leverages SMT-powered automation and a library of powerful reasoning techniques, including rely-guarantee, TSO elimination, reduction, and alias analysis. All these techniques are proven sound, and Armada can be soundly extended with additional strategies over time. Using Armada, we verify four concurrent case studies and show that we can achieve performance equivalent to that of unverified code. You can read more about Armada in our PLDI '20 paper, which ACM will publish on June 17, 2020: Jacob R. Lorch, Yixuan Chen, Manos Kapritsos, Bryan Parno, Shaz Qadeer, Upamanyu Sharma, James R. Wilcox, and Xueyuan Zhao. 2020. Armada: Low-Effort Verification of High-Performance Concurrent Programs. In Proceedings of the 41st ACM SIGPLAN International Conference on Programming Language Design and Implementation (PLDI '20), June 15-20, 2020, London, UK. ACM, New York, NY, USA, 14 pages. https://doi.org/10.1145/3385412.3385971 # Getting started To build Armada, please follow the detailed instructions documented in [BUILD.md](BUILD.md). To use Armada, you'll need the following tools: * .NET 5.0 (runtime for both Armada and Dafny) * pip (needed for installing scons) * scons (installable by running `pip install scons`) * Dafny v3.2.0 (available at https://github.com/dafny-lang/dafny) # Generating and testing proofs To use the Armada tool to generate proofs for all the included test Armada files (`Test/*/*.arm`), run, from the Armada top-level directory, `scons -j -f SConstruct1` where `` is the number of threads you want scons to use. To verify all the generated proofs, run, from the same directory, `scons -j -f SConstruct2 --DAFNYPATH=` where `` is the directory containing the `Dafny.exe`/`Dafny.dll` binary you installed. If this second scons finishes without printing an error message, this means that everything worked. If it reports an error, this is likely due to running on a machine without enough memory, so try again with fewer threads. # Compilation, performance evaluation, and graph generation To build the queue benchmarks, run them, and generate the performance graphs, run `python3 run_benchmarks.py` in the `Test/qbss_benchmark/` directory. This will produce a file `qbss_performance_graph.pdf` with the performance graph. # Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ================================================ FILE: SConstruct1 ================================================ # -*- python -*- import re import sys import os, os.path import subprocess import traceback import pdb import SCons.Util import atexit import platform Import("*") env = Environment() if sys.platform != 'win32' and sys.platform != 'cygwin': env['DOTNET'] = 'dotnet' # Retrieve tool-specific command overrides passed in by the user # We don't really need --DAFNYPATH, but it's OK if the user # specifies it. AddOption('--DAFNYPATH', dest='dafny_path', type='string', default=None, action='store', help='Specify the path to Dafny tool binaries (not needed)') pipeline_dll = os.path.abspath('Binaries/ArmadaPipeline.dll') if sys.platform == "win32" or sys.platform == "cygwin": armada_exe = os.path.abspath('Binaries/Armada.exe') else: armada_exe = os.path.abspath('Binaries/Armada.dll') #################################################################### # # Define Armada transformation Builders # #################################################################### def generate_armada_actions(source, target, env, for_signature): # print("Target ", [File(t).path for t in target], " has dependencies ", [File(s).path for s in source]) source_path = File(source[0]).path source_dir = os.path.dirname(source_path) source_name = os.path.basename(source_path) num_slashes = source_path.count("/") if num_slashes > 0: armada_path = "../" * (num_slashes-1) + ".." else: armada_path = "." return "cd %s; $DOTNET %s /armadaPath:%s %s" % (source_dir, armada_exe, armada_path, source_name) def add_armada(env): armada = Builder(generator=generate_armada_actions) env.Append(BUILDERS = {'Armada' : armada}) #################################################################### # # Put it all together # #################################################################### add_armada(env) #################################################################### # # Create dependencies # #################################################################### def run_directory(dir_name, entries): for (armada_name, dafny_files, proof_dirs) in entries: source = "Test/%s/%s.arm" % (dir_name, armada_name) targets = ["Test/%s/%s.dfy" % (dir_name, dafny_file) for dafny_file in dafny_files] cmd = env.Armada(targets, [source, armada_exe, pipeline_dll]) for proof_dir in proof_dirs: subdir = "Test/%s/%s" % (dir_name, proof_dir) if os.path.isdir(subdir): for filename in os.listdir(subdir): if os.path.splitext(filename)[1] == '.dfy': Clean(cmd, "%s/%s" % (subdir, filename)) Clean(cmd, subdir) run_directory("assume-intro", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ("test2", ["SharedStructs2", "C", "D", "CD"], ["CD"]), ("test3", ["SharedStructs3", "E", "F", "EF"], ["EF"]), ]) run_directory("barrier", [ ("barrier", ["SharedStructs", "Impl", "L1", "L2", "ImplRefinesL1", "L1RefinesL2"], ["ImplRefinesL1", "L1RefinesL2"]), ]) run_directory("tsoelim", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ("test2", ["SharedStructs2", "C", "D", "CD"], ["CD"]), ("test3", ["SharedStructs3", "E", "F", "EF"], ["EF"]), ("test4", ["SharedStructs4", "G", "H", "GH"], ["GH"]), ]) run_directory("reduction", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ("test2", ["SharedStructs2", "C", "D", "CD"], ["CD"]), ]) run_directory("combining", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ]) run_directory("starweakening", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ("test2", ["SharedStructs2", "C", "D", "CD"], ["CD"]), ("test3", ["SharedStructs3", "E", "F", "EF"], ["EF"]), ("test4", ["SharedStructs4", "G", "H", "GH"], ["GH"]), ]) run_directory("varhiding", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ("test2", ["SharedStructs2", "C", "D", "CD"], ["CD"]), ("test3", ["SharedStructs3", "E", "F", "EF"], ["EF"]), ("test4", ["SharedStructs4", "G", "H", "GH"], ["GH"]), ("test5", ["SharedStructs5", "I", "J", "IJ"], ["IJ"]), ]) run_directory("varintro", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ("test2", ["SharedStructs2", "C", "D", "CD"], ["CD"]), ("test3", ["SharedStructs3", "E", "F", "EF"], ["EF"]), ("test4", ["SharedStructs4", "G", "H", "GH"], ["GH"]), ]) run_directory("weakening", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ("test2", ["SharedStructs2", "C", "D", "CD"], ["CD"]), ("test3", ["SharedStructs3", "E", "F", "EF"], ["EF"]), ]) run_directory("counter", [ ("SharedStructs", ["SharedStructs"], []), ("A", ["A"], []), ("B", ["B"], []), ("C", ["C"], []), ("D", ["D"], []), ("E", ["E"], []), ("F", ["F"], []), ("G", ["G"], []), ("I", ["I"], []), ("J", ["J"], []), ("AB", ["AB"], ["AB"]), ("BC", ["BC"], ["BC"]), ("CD", ["CD"], ["CD"]), ("DE", ["DE"], ["DE"]), ("EF", ["EF"], ["EF"]), ("FG", ["FG"], ["FG"]), ("GI", ["GI"], ["GI"]), ("IJ", ["IJ"], ["IJ"]), ]) run_directory("bitvector", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ]) run_directory("regions", [ ("test", ["SharedStructs", "A", "B", "AB"], ["AB"]), ("test2", ["SharedStructs2", "C", "D", "CD"], ["CD"]), ("test3", ["SharedStructs3", "E", "F", "EF"], ["EF"]), ("test4", ["SharedStructs4", "G", "H", "GH"], ["GH"]), ("pointers", ["SharedStructsPointers", "level0", "level1", "level01proof"], ["level01proof"]), ]) run_directory("armada-parser", [ ("test", ["SharedStructs", "A"], []), ("test2", ["SharedStructs2", "B"], []), ("test3", ["SharedStructs3", "C"], []), ("test4", ["SharedStructs4", "D"], []), ("test5", ["SharedStructs5", "E"], []), ("test6", ["SharedStructs6", "F"], []), ("test7", ["SharedStructs7", "G"], []) ]) run_directory("mcslock", [ ("lock-array", ["MCSLock", "pseudo_impl", "L1", "L2", "L3", "L4", "L5", "L6", "pseudo_impl_L1", "L1_L2", "L2_L3", "L3_L4", "L4_L5", "L5_L6"], ["pseudo_impl_L1", "L1_L2", "L2_L3", "L3_L4", "L4_L5", "L5_L6"]) ]) run_directory("qbss", [ ("queue", [ "QueueBSSNoTSO", "QueueBSSNoTSO_AbstractQueueIntroduced", "QueueBSSNoTSO_AbstractQueueIntroduced_CombinedEnqueue", "QueueBSSNoTSO_WithAbstractQueue", "QueueBSSNoTSO_AbstractLogs", "QueueBSSNoTSO_AbstractLogsStarweakened", "QueueBSSNoTSO_HiddenEnqLocals", "QueueBSSNoTSO_HiddenEnqDeqLocals", "QueueBSSNoTSO_HiddenImpl", ], [ "NoTSOIntroduceAbstractQueue", "CombineEnqueueAtomicAbstractQueue", "CombineDequeueAtomicAbstractQueue", "NoTSOUseAbstractQueueForLog", "NoTSOStarWeaken", "HideEnqueueLocals", "HideDequeueLocals", "HideGlobal", ]) ]) ================================================ FILE: SConstruct2 ================================================ # -*- python -*- import re import sys import os, os.path import subprocess import traceback import pdb import random import SCons.Util import atexit import platform Import("*") env = Environment() if sys.platform != 'win32' and sys.platform != 'cygwin': env['MONO'] = 'dotnet' # Retrieve tool-specific command overrides passed in by the user AddOption('--DAFNYPATH', dest='dafny_path', type='string', default=None, action='store', help='Specify the path to Dafny tool binaries') AddOption('--ONLYFILENAMES', dest='onlyfilenames', action='store_true', default=False, help='Only print out the file paths of dafny files that would be verified') onlyfilenames = GetOption('onlyfilenames') AddOption('--TIMELIMIT', dest='time_limit', type='int', default=120, action='store', help='Specify the time limit to use for each verification') dafny_path = GetOption('dafny_path') if dafny_path is None: sys.stderr.write("ERROR: Missing --DAFNYPATH on command line\n") exit(-1) if sys.platform == "win32" or sys.platform == "cygwin": dafny_exe = os.path.join(dafny_path, 'Dafny.exe') if not os.path.exists(dafny_exe): print("ERROR: Could not find Dafny executable in " + dafny_path) exit(-1) dafny_invocation = [dafny_exe] else: dafny_exe = os.path.join(dafny_path, 'Dafny.dll') if not os.path.exists(dafny_exe): dafny_exe = os.path.join(dafny_path, 'dafny.dll') if not os.path.exists(dafny_exe): print("ERROR: Could not find Dafny executable in " + dafny_path) exit(-1) dafny_invocation = ["dotnet", dafny_exe] # Useful Dafny command lines dafny_basic_args = ['/compile:0', '/timeLimit:' + str(GetOption('time_limit')), '/trace'] dafny_default_args = dafny_basic_args + ['/arith:5', '/noCheating:1'] dafny_args_nlarith = dafny_basic_args + ['/arith:2', '/noCheating:1'] dafny_spec_args = dafny_basic_args #################################################################### # # General routines # #################################################################### def recursive_glob(env, pattern, strings=False): matches = [] split = os.path.split(pattern) # [0] is the directory, [1] is the actual pattern platform_directory = split[0] #os.path.normpath(split[0]) for d in os.listdir(platform_directory): if os.path.isdir(os.path.join(platform_directory, d)): newpattern = os.path.join(split[0], d, split[1]) matches.append(recursive_glob(env, newpattern, strings)) files = env.Glob(pattern, strings=strings) matches.append(files) return Flatten(matches) #################################################################### # # Make table of special cases requiring non-default arguments # #################################################################### source_to_args = [ ('.*test\/weakening\/ArithmeticFacts\.dfy', dafny_args_nlarith), ('.*Armada\/util\/math\/[^\.]+nonlinear\.i\.dfy', dafny_args_nlarith), ('.*Armada\/util\/math\/[^\.]+auto[^\.]*\.i\.dfy', dafny_args_nlarith), ('.*Armada\/util\/math\/mul\.i\.dfy', dafny_args_nlarith), ('.*\.dfy', dafny_default_args), ] #################################################################### # # Dafny-specific utilities # #################################################################### dafny_include_re = re.compile(r'include\s+"(\S+)"', re.M) single_line_comments_re = re.compile(r'//.*\n') multiline_comments_re = re.compile(r'/\*(([^/\*])|(\*[^/])|(/[^\*]))*\*/') def remove_dafny_comments(contents): # Strip out multi-line comments, using a loop to deal with nested comments while True: (contents, substitutions_made) = re.subn(multiline_comments_re, ' ', contents) if substitutions_made == 0: break # Strip out single-line comments contents = re.sub(single_line_comments_re, '\n', contents) return contents # helper to look up Dafny command-line arguments matching a srcpath, from the # source_to_args[] dictionary, dealing with POSIX and Windows pathnames, and # falling back on a default if no specific override is present. def get_dafny_command_line_args(srcpath): srcpath = os.path.normpath(srcpath) # normalize the path, which, on Windows, switches to \\ separators srcpath = srcpath.replace('\\', '/') # switch to posix path separators for entry in source_to_args: pattern, args = entry if re.search(pattern, srcpath, flags=re.IGNORECASE): return args return dafny_default_args dependencies_by_file = dict() already_verified_files = set() already_printed_files = set() # Scan a .dfy file to discover its transitive dependencies, and store a # list of them in dependencies_by_file[fullpath]. def recursively_scan_for_dependencies(fullpath): if fullpath in dependencies_by_file: return contents = File(fullpath).get_text_contents() dirname = os.path.dirname(fullpath) filename = os.path.basename(fullpath) contents = remove_dafny_comments(contents) includes = dafny_include_re.findall(contents) extra_files = [os.path.abspath(os.path.join(dirname, i)) for i in includes] transitive_dependencies = set(extra_files) for srcpath in extra_files: recursively_scan_for_dependencies(srcpath) transitive_dependencies.update(dependencies_by_file[srcpath]) all_dependencies = sorted(list(transitive_dependencies)) dependencies_by_file[fullpath] = all_dependencies # Scan a .dfy file to discover its dependencies, and add .vdfy targets for each. def scan_for_more_targets(target, source, env): node = source[0] fullpath = str(node) recursively_scan_for_dependencies(fullpath) dependencies = dependencies_by_file[fullpath] for srcpath in dependencies: if srcpath not in already_verified_files: f = os.path.splitext(srcpath)[0] + '.vdfy' env.DafnyVerify(f, [srcpath, dafny_exe]) already_verified_files.add(srcpath) return target, source + dependencies #################################################################### # # Dafny routines # #################################################################### def check_dafny(lines): for line in lines: if re.search("[Oo]ut of resource", line): sys.stderr.write("Dafny reported an out-of-resource error\n") raise Exception() if re.search("proof obligations\]\s+errors", line): sys.stderr.write("Dafny reported errors not in summary\n") raise Exception() def check_and_print_tail(filename): with open(filename, 'r') as fh: lines = fh.readlines() check_dafny(lines) sys.stdout.write(lines[-1]) sys.stdout.write('Full check of Dafny output succeeded\n') CheckAndPrintTail = SCons.Action.ActionFactory(check_and_print_tail, lambda x: "Checking " + x) def generate_dafny_verifier_actions(source, target, env, for_signature): abs_source = File(source[0]).abspath abs_target = File(target[0]).abspath source_name = str(source[0]) temp_target_file = re.sub(r'\.dfy$', '.tmp', source_name) args = get_dafny_command_line_args(abs_source) return [ dafny_invocation + args + [source_name, ">", temp_target_file], CheckAndPrintTail(temp_target_file), Move(abs_target, temp_target_file) ] # Add env.DafnyVerify(), to generate Dafny verifier actions def add_dafny_verifier_builder(env): dafny_verifier = Builder(generator = generate_dafny_verifier_actions, suffix = '.vdfy', src_suffix = '.dfy', chdir=0, emitter = scan_for_more_targets, ) env.Append(BUILDERS = {'DafnyVerify' : dafny_verifier}) # Verify a set of Dafny files by creating verification targets for each, # which in turn causes a dependency scan to verify all of their dependencies. def verify_dafny_files(env, files): for f in files: target = os.path.splitext(f)[0] + '.vdfy' env.DafnyVerify(target, [f, dafny_exe]) # Verify *.dfy files in a list of directories. This enumerates # all files in those trees, and creates verification targets for each, # which in turn causes a dependency scan to verify all of their dependencies. def verify_files_in(env, directories): for d in directories: files = recursive_glob(env, d+'/*.dfy', strings=True) verify_dafny_files(env, files) #################################################################### # # Extract verification failure information # #################################################################### # extract a string filename out of a build failure def bf_to_filename(bf): import SCons.Errors if bf is None: # unknown targets product None in list return '(unknown tgt)' elif isinstance(bf, SCons.Errors.StopError): return str(bf) elif bf.node: return str(bf.node) elif bf.filename: return bf.filename return '(unknown failure)' def report_verification_failures(): from SCons.Script import GetBuildFailures bf = GetBuildFailures() if bf: # bf is normally a list of build failures; if an element is None, # it's because of a target that scons doesn't know anything about. for x in bf: if x is not None: filename = bf_to_filename(x) if filename.endswith('.vdfy'): file_to_print = os.path.splitext(filename)[0] + '.tmp' if os.path.isfile(file_to_print): sys.stdout.write('\n##### Verification error. Printing contents of ' + file_to_print + ' #####\n\n') with open (file_to_print, 'r') as myfile: sys.stdout.write(myfile.read()) else: print("ERROR: Verification error, but cannot print output since file %s doesn't exist" % (file_to_print)) else: print("Build failure for %s" % (filename)) def display_build_status(): report_verification_failures() #################################################################### # # Put it all together # #################################################################### add_dafny_verifier_builder(env) env.AddMethod(verify_files_in, "VerifyFilesIn") env.AddMethod(verify_dafny_files, "VerifyDafnyFiles") atexit.register(display_build_status) #################################################################### # # Create dependencies # #################################################################### def run_directory(dir_name, dafny_files): for dafny_file in dafny_files: source = "Test/%s/%s.dfy" % (dir_name, dafny_file) target = "Test/%s/%s.vdfy" % (dir_name, dafny_file) env.DafnyVerify(target, [source, dafny_exe]) run_directory("assume-intro", ["AB", "CD", "EF"]) run_directory("barrier", ["ImplRefinesL1", "L1RefinesL2"]) run_directory("tsoelim", ["AB", "CD", "EF", "GH"]) run_directory("reduction", ["AB", "CD"]) run_directory("combining", ["AB"]) run_directory("varhiding", ["AB", "CD", "EF", "GH", "IJ"]) run_directory("varintro", ["AB", "CD", "EF", "GH"]) run_directory("weakening", ["AB", "CD", "EF"]) run_directory("counter", ["AB", "BC", "CD", "DE", "EF", "FG", "GI", "IJ"]) run_directory("bitvector", ["AB"]) run_directory("regions", ["AB", "CD", "EF", "GH", "level01proof"]) run_directory("starweakening", ["AB", "CD", "EF", "GH"]) run_directory("armada-parser", ["A", "B", "C", "D", "E", "F"]) run_directory("mcslock", ["pseudo_impl_L1", "L1_L2", "L2_L3", "L3_L4", "L4_L5", "L5_L6"]) run_directory("qbss", ["NoTSOIntroduceAbstractQueue", "CombineEnqueueAtomicAbstractQueue", "CombineDequeueAtomicAbstractQueue", "NoTSOUseAbstractQueueForLog", "NoTSOStarWeaken", "HideEnqueueLocals", "HideDequeueLocals", "HideGlobal", ]) ================================================ FILE: SECURITY.md ================================================ ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. ## Preferred Languages We prefer all communications to be in English. ## Policy Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). ================================================ FILE: Source/Armada/AbstractProofGenerator.cs ================================================ using System; using System.Numerics; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Linq; using System.Text; using System.Text.RegularExpressions; using Token = Microsoft.Boogie.Token; namespace Microsoft.Armada { public class AuxiliaryInfo { public readonly string FieldName; public readonly string TypeName; public readonly string InitName; public readonly string NextName; public AuxiliaryInfo(string i_FieldName, string i_TypeName, string i_InitName, string i_NextName) { FieldName = i_FieldName; TypeName = i_TypeName; InitName = i_InitName; NextName = i_NextName; } } public abstract class InvariantInfo { protected string key; protected string name; protected List dependencies; protected string initLemmaName; protected string nextLemmaName; protected string nextStepBody; protected bool opaque; public InvariantInfo(string i_key, string i_name, List i_dependencies, string i_nextStepBody, bool i_opaque) { key = i_key; name = i_name; dependencies = i_dependencies; initLemmaName = null; nextLemmaName = null; nextStepBody = i_nextStepBody; opaque = i_opaque; } public virtual string Key { get { return key; } } public virtual string Name { get { return name; } } public virtual List Dependencies { get { return dependencies; } } public virtual string InitLemmaName { get { return initLemmaName; } } public virtual string NextLemmaName { get { return nextLemmaName; } } private string FindMatchingDependency(string dependency, IEnumerable allInvariants) { foreach (var inv in allInvariants) { if (inv.Key == dependency || inv.Key == $"UserInv_{dependency}") { return inv.Name; } } return dependency; } private string GetRevelations(IEnumerable allInvariants) { string revelations = opaque ? $"reveal {Name}();\n" : ""; foreach (var dependency in Dependencies) { foreach (var inv in allInvariants) { if (inv.Key == dependency || inv.Key == $"UserInv_{dependency}") { if (inv.opaque) { revelations += $"reveal {inv.Name}();\n"; break; } } } } return revelations; } public virtual void GenerateInitLemma(ProofGenerationParams pgp) { initLemmaName = $"lemma_InvariantPredicateImpliedByInit_{Key}"; var revelation = opaque ? $"reveal {Name}();" : ""; string str = $@" lemma {initLemmaName}(s:LPlusState) requires LPlus_Init(s) ensures {Name}(s) {{ {revelation} }} "; pgp.AddLemma(str, "invariants"); } public virtual string GenerateSpecificNextLemma(ProofGenerationParams pgp, AtomicPath atomicPath, IEnumerable allInvariants, AtomicSpec atomicSpec, bool onlyNonstoppingPaths) { string nameSuffix = atomicPath.Name; string specificPathLemmaName = $"lemma_InvariantPredicateMaintainedByPath_{Key}_{nameSuffix}"; var pr = new PathPrinter(atomicSpec); string str = $@" lemma {specificPathLemmaName}(s: LPlusState, s': LPlusState, path: {atomicSpec.Prefix}_Path, tid: Armada_ThreadHandle) requires {Name}(s) "; str += String.Concat(Dependencies.Select(dependency => $" requires {FindMatchingDependency(dependency, allInvariants)}(s);\n")); if (onlyNonstoppingPaths) { str += $@" requires s'.s.stop_reason.Armada_NotStopped? "; } str += $@" requires path.{atomicSpec.Prefix}_Path_{nameSuffix}? requires {atomicSpec.Prefix}_ValidPath(s, path, tid) requires s' == {atomicSpec.Prefix}_GetStateAfterPath(s, path, tid) ensures {Name}(s') {{ { pr.GetOpenValidPathInvocation(atomicPath) } { GetRevelations(allInvariants) } ProofCustomizationGoesHere(); { nextStepBody } assert {Name}(s'); }} "; pgp.AddLemma(str, "invariants"); return specificPathLemmaName; } public virtual void GenerateAtomicNextLemma(ProofGenerationParams pgp, IEnumerable allInvariants, AtomicSpec atomicSpec, bool onlyNonstoppingPaths) { string finalCases = ""; foreach (var atomicPath in atomicSpec.AtomicPaths) { string specificPathLemma = GenerateSpecificNextLemma(pgp, atomicPath, allInvariants, atomicSpec, onlyNonstoppingPaths); finalCases += $" case {atomicSpec.Prefix}_Path_{atomicPath.Name}(_) => {specificPathLemma}(s, s', path, tid);\n"; } nextLemmaName = $"lemma_InvariantPredicateMaintainedByPath_{Key}"; string str = $@" lemma {nextLemmaName}(s: LPlusState, s': LPlusState, path: {atomicSpec.Prefix}_Path, tid: Armada_ThreadHandle) requires {Name}(s) "; foreach (var dependency in Dependencies) { str += $" requires {FindMatchingDependency(dependency, allInvariants)}(s);\n"; } if (onlyNonstoppingPaths) { str += @" requires s'.s.stop_reason.Armada_NotStopped? "; } str += $@" requires {atomicSpec.Prefix}_ValidPath(s, path, tid) requires s' == {atomicSpec.Prefix}_GetStateAfterPath(s, path, tid) ensures {Name}(s') {{ { GetRevelations(allInvariants) } match path {{ { finalCases } }} }} "; pgp.AddLemma(str, "invariants"); } public virtual void GenerateProofs(ProofGenerationParams pgp, IEnumerable allInvariants, AtomicSpec atomicSpec, bool onlyNonstoppingPaths) { GenerateInitLemma(pgp); GenerateAtomicNextLemma(pgp, allInvariants, atomicSpec, onlyNonstoppingPaths); } } public class UserInvariantInfo : InvariantInfo { public UserInvariantInfo(string i_key, string i_name, List i_dependencies, bool i_opaque) : base(i_key, i_name, i_dependencies, "", i_opaque) { } } public class InternalInvariantInfo : InvariantInfo { public InternalInvariantInfo(string i_key, string i_name, List i_dependencies, string nextStepBody="") : base(i_key, i_name, i_dependencies, nextStepBody, false /* opaque: no */) { } } public abstract class AbstractProofGenerator { protected ProofGenerationParams pgp; protected bool stateDependentConvertStep; protected Dictionary pcMap; protected Dictionary nextRoutineMap; protected List importedFiles; protected List importedModules; protected List invariants; protected List auxiliaries; protected AtomicSpec lAtomic, hAtomic; protected Dictionary pathMap; // To prevent duplicate lemmas when there are duplicate calls private bool calledGenerateAppendStoreBufferOtherWay = false; public AbstractProofGenerator(ProofGenerationParams i_pgp, bool i_stateDependentConvertStep = false) { pgp = i_pgp; stateDependentConvertStep = i_stateDependentConvertStep; nextRoutineMap = null; importedFiles = new List(); importedModules = new List(); invariants = new List(); auxiliaries = new List(); lAtomic = null; hAtomic = null; pathMap = null; var revelationsFile = pgp.proofFiles.CreateAuxiliaryProofFile("revelations", false); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("revelations"); var specFile = pgp.proofFiles.CreateAuxiliaryProofFile("specs", false); specFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/option.s.dfy", "util_option_s"); specFile.AddInclude(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/GenericArmadaLemmas.i.dfy"); specFile.AddImport("GenericArmadaSpecModule"); specFile.AddImport("GenericArmadaLemmasModule"); specFile.IncludeAndImportGeneratedFile("revelations"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("specs"); var plusFile = pgp.proofFiles.CreateAuxiliaryProofFile("PlusLemmas"); plusFile.IncludeAndImportGeneratedFile("specs"); plusFile.AddInclude(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/GenericArmadaPlus.i.dfy"); plusFile.AddImport("GenericArmadaPlusModule"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("PlusLemmas"); var effectFile = pgp.proofFiles.CreateAuxiliaryProofFile("pceffect"); effectFile.IncludeAndImportGeneratedFile("specs"); effectFile.IncludeAndImportGeneratedFile("revelations"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("pceffect"); var latomicFile = pgp.proofFiles.CreateAuxiliaryProofFile("latomic"); latomicFile.IncludeAndImportGeneratedFile("specs"); latomicFile.IncludeAndImportGeneratedFile("revelations"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("latomic"); var hatomicFile = pgp.proofFiles.CreateAuxiliaryProofFile("hatomic"); hatomicFile.IncludeAndImportGeneratedFile("specs"); hatomicFile.IncludeAndImportGeneratedFile("revelations"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("hatomic"); var convertFile = pgp.proofFiles.CreateAuxiliaryProofFile("convert"); convertFile.IncludeAndImportGeneratedFile("specs"); convertFile.IncludeAndImportGeneratedFile("pceffect"); convertFile.IncludeAndImportGeneratedFile("latomic"); convertFile.IncludeAndImportGeneratedFile("hatomic"); convertFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/seqs.i.dfy", "util_collections_seqs_i"); convertFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/maps.i.dfy", "util_collections_maps_i"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("convert"); var defsFile = pgp.proofFiles.CreateAuxiliaryProofFile("defs"); defsFile.IncludeAndImportGeneratedFile("specs"); defsFile.IncludeAndImportGeneratedFile("convert"); defsFile.IncludeAndImportGeneratedFile("latomic"); defsFile.IncludeAndImportGeneratedFile("hatomic"); defsFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/option.s.dfy", "util_option_s"); defsFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/maps.i.dfy", "util_collections_maps_i"); defsFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/seqs.i.dfy", "util_collections_seqs_i"); defsFile.AddImport("util_collections_seqs_s"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("defs"); var utilityFile = pgp.proofFiles.CreateAuxiliaryProofFile("utility"); utilityFile.IncludeAndImportGeneratedFile("specs"); utilityFile.IncludeAndImportGeneratedFile("convert"); utilityFile.IncludeAndImportGeneratedFile("defs"); utilityFile.IncludeAndImportGeneratedFile("latomic"); utilityFile.IncludeAndImportGeneratedFile("hatomic"); utilityFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/seqs.s.dfy", "util_collections_seqs_s"); utilityFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/seqs.i.dfy", "util_collections_seqs_i"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("utility"); var invFile = pgp.proofFiles.CreateAuxiliaryProofFile("invariants"); invFile.IncludeAndImportGeneratedFile("specs"); invFile.IncludeAndImportGeneratedFile("convert"); invFile.IncludeAndImportGeneratedFile("revelations"); invFile.IncludeAndImportGeneratedFile("utility"); invFile.IncludeAndImportGeneratedFile("defs"); invFile.IncludeAndImportGeneratedFile("latomic"); invFile.IncludeAndImportGeneratedFile("hatomic"); invFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/invariants.i.dfy", "InvariantsModule"); invFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/GenericArmadaSpec.i.dfy", "GenericArmadaSpecModule"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("invariants"); var liftFile = pgp.proofFiles.CreateAuxiliaryProofFile("lift"); liftFile.IncludeAndImportGeneratedFile("specs"); liftFile.IncludeAndImportGeneratedFile("convert"); liftFile.IncludeAndImportGeneratedFile("defs"); liftFile.IncludeAndImportGeneratedFile("invariants"); liftFile.IncludeAndImportGeneratedFile("utility"); liftFile.IncludeAndImportGeneratedFile("latomic"); liftFile.IncludeAndImportGeneratedFile("hatomic"); liftFile.IncludeAndImportGeneratedFile("revelations"); liftFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/LiftAtomicToAtomic.i.dfy", "LiftAtomicToAtomicModule"); liftFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/GenericArmadaAtomic.i.dfy", "GenericArmadaAtomicModule"); liftFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/seqs.s.dfy", "util_collections_seqs_s"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("lift"); var str = @" predicate InductiveInvBasic(s: LPlusState) { 0 !in s.s.threads } "; pgp.AddPredicate(str, "defs"); AddInvariant(new InternalInvariantInfo("InductiveInvBasic", "InductiveInvBasic", new List())); var atomicModulePath = ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/GenericArmadaAtomic.i.dfy"; convertFile.AddIncludeImport(atomicModulePath, "GenericArmadaAtomicModule"); invFile.AddIncludeImport(atomicModulePath, "GenericArmadaAtomicModule"); pgp.proofFiles.MainProof.AddIncludeImport(atomicModulePath, "GenericArmadaAtomicModule"); } public abstract void GenerateProof(); //////////////////////////////////////////////////////////////////////// /// Checking that the layers are similar enough to generate a proof //////////////////////////////////////////////////////////////////////// protected virtual bool CheckEquivalence() { return CheckStructsEquivalence() && CheckGlobalsEquivalence() && CheckMethodsEquivalence(); } protected virtual bool CheckStructsEquivalence() { if (pgp.symbolsLow.StructsModuleName != pgp.symbolsHigh.StructsModuleName) { AH.PrintError(pgp.prog, $"Levels {pgp.mLow.Name} and {pgp.mHigh.Name} don't use the same structs module"); return false; } return true; } protected bool CheckGlobalVariableEquivalence(string name, ArmadaVariable v_l, ArmadaVariable v_h) { if (v_l is AddressableArmadaVariable && !(v_h is AddressableArmadaVariable)) { AH.PrintError(pgp.prog, $"Global variable {name} is addressable in level {pgp.mLow.Name} but not in level {pgp.mHigh.Name}"); return false; } if (!(v_l is AddressableArmadaVariable) && (v_h is AddressableArmadaVariable)) { AH.PrintError(pgp.prog, $"Global variable {name} is addressable in level {pgp.mHigh.Name} but not in level {pgp.mLow.Name}"); return false; } if (v_l.NoTSO() && !v_h.NoTSO()) { AH.PrintError(pgp.prog, $"Global variable {name} is ghost in level {pgp.mLow.Name} but not in level {pgp.mHigh.Name}"); return false; } if (!v_l.NoTSO() && v_h.NoTSO()) { AH.PrintError(pgp.prog, $"Global variable {name} is ghost in level {pgp.mHigh.Name} but not in level {pgp.mLow.Name}"); return false; } if (!AH.TypesMatch(v_l.ty, v_h.ty)) { AH.PrintError(pgp.prog, $"Global variable {name} has type {v_l.ty} in level {pgp.mLow.Name} but type {v_h.ty} in level {pgp.mHigh.Name}"); return false; } if (v_l is GlobalUnaddressableArmadaVariable && v_h is GlobalUnaddressableArmadaVariable && ((GlobalUnaddressableArmadaVariable)v_l).initialValue == null && ((GlobalUnaddressableArmadaVariable)v_h).initialValue != null) { AH.PrintError(pgp.prog, $"Global variable {name} has an initial value in level {pgp.mHigh.Name} but no initial value in level {pgp.mLow.Name}"); return false; } return true; } protected virtual bool CheckGlobalsEquivalence() { return CheckGlobalsEquivalenceAbstract(); } protected bool CheckGlobalsEquivalenceAbstract() { var globalVarsLow = pgp.symbolsLow.Globals.VariableNames.ToArray(); var globalVarsHigh = pgp.symbolsHigh.Globals.VariableNames.ToArray(); if (globalVarsLow.Length != globalVarsHigh.Length) { AH.PrintError(pgp.prog, $"There are {globalVarsLow.Length} global variables in level {pgp.mLow.Name} but {globalVarsHigh.Length} in level {pgp.mHigh.Name}"); return false; } for (int i = 0; i < globalVarsLow.Length; ++i) { if (globalVarsLow[i] != globalVarsHigh[i]) { AH.PrintError(pgp.prog, $"Global variable number {i+1} in level {pgp.mLow.Name} is {globalVarsLow[i]}, which doesn't match global variable number {i+1} in level {pgp.mHigh.Name} which is {globalVarsHigh[i]}"); return false; } var name = globalVarsLow[i]; if (!CheckGlobalVariableEquivalence(name, pgp.symbolsLow.Globals.Lookup(name), pgp.symbolsHigh.Globals.Lookup(name))) { return false; } } return true; } protected virtual bool CheckVariableNameListEquivalence(IEnumerable varNames_l, IEnumerable varNames_h, ArmadaSingleMethodSymbolTable s_l, ArmadaSingleMethodSymbolTable s_h, string methodName, string descriptor) { var vars_l = varNames_l.ToArray(); var vars_h = varNames_h.ToArray(); if (vars_l.Length != vars_h.Length) { AH.PrintError(pgp.prog, $"Method {methodName} has {vars_l.Length} {descriptor} variables in level {pgp.mLow.Name} but {vars_h.Length} of them in level {pgp.mHigh.Name}"); return false; } for (int i = 0; i < vars_l.Length; ++i) { var name_l = vars_l[i]; var name_h = vars_h[i]; if (name_l != name_h) { AH.PrintError(pgp.prog, $"In method {methodName}, {descriptor} variable number {i+1} is named {name_l} in level {pgp.mLow.Name} but named {name_h} in level {pgp.mHigh.Name}"); return false; } var v_l = s_l.LookupVariable(name_l); var v_h = s_h.LookupVariable(name_h); if (!AH.TypesMatch(v_l.ty, v_h.ty)) { AH.PrintError(pgp.prog, $"In method {methodName}, the {descriptor} variable named {name_l} has type {v_l.ty} in level {pgp.mLow.Name} but type {v_h.ty} in level {pgp.mHigh.Name}"); return false; } } return true; } protected virtual bool CheckMethodsEquivalence() { foreach (var methodName in pgp.symbolsLow.MethodNames) { if (!pgp.symbolsHigh.DoesMethodNameExist(methodName)) { AH.PrintError(pgp.prog, $"Method {methodName} is in level {pgp.mLow.Name} but not in level {pgp.mHigh.Name}"); return false; } } foreach (var methodName in pgp.symbolsHigh.MethodNames) { if (!pgp.symbolsLow.DoesMethodNameExist(methodName)) { AH.PrintError(pgp.prog, $"Method {methodName} is in level {pgp.mHigh.Name} but not in level {pgp.mLow.Name}"); return false; } } foreach (var methodName in pgp.symbolsLow.MethodNames) { var s_l = pgp.symbolsLow.GetMethodSymbolTable(methodName); var s_h = pgp.symbolsHigh.GetMethodSymbolTable(methodName); if (s_l.IsExternal && !s_h.IsExternal) { AH.PrintError(pgp.prog, $"Method {methodName} is external in level {pgp.mLow.Name} but not external in level {pgp.mHigh.Name}"); return false; } if (!s_l.IsExternal && s_h.IsExternal) { AH.PrintError(pgp.prog, $"Method {methodName} is external in level {pgp.mHigh.Name} but not external in level {pgp.mLow.Name}"); return false; } if (!CheckVariableNameListEquivalence(s_l.InputVariableNames, s_h.InputVariableNames, s_l, s_h, methodName, "input")) { return false; } if (!CheckVariableNameListEquivalence(s_l.OutputVariableNames, s_h.OutputVariableNames, s_l, s_h, methodName, "output")) { return false; } if (!CheckVariableNameListEquivalence(s_l.AllVariableNamesInOrder, s_h.AllVariableNamesInOrder, s_l, s_h, methodName, "")) { return false; } } return true; } //////////////////////////////////////////////////////////////////////// /// Generate PC-effect elements //////////////////////////////////////////////////////////////////////// protected void GeneratePCEffectLemmas() { GeneratePCEffectLemmas("L", pgp.symbolsLow); GeneratePCEffectLemmas("H", pgp.symbolsHigh); } protected void GeneratePCEffectLemmas(string m, ArmadaSymbolTable symbols) { string str; var pr = new ModuleStepPrinter(m); foreach (var nextRoutine in symbols.NextRoutines) { str = $@" lemma lemma_PCEffectOfStep_{m}_{nextRoutine.NameSuffix}( s: {m}.Armada_TotalState, step: {m}.Armada_Step, tid: Armada_ThreadHandle ) requires {m}.Armada_ValidStep(s, step, tid) requires step.Armada_Step_{nextRoutine.NameSuffix}? "; if (nextRoutine.Tau) { str += $@" ensures var s' := {m}.Armada_GetNextState(s, step, tid); s'.stop_reason.Armada_NotStopped? && tid in s'.threads && s'.threads[tid].pc == s.threads[tid].pc"; } else { str += $"ensures s.threads[tid].pc.{nextRoutine.startPC}?\n"; if (nextRoutine.Stopping) { str += $"ensures var s' := {m}.Armada_GetNextState(s, step, tid); !s'.stop_reason.Armada_NotStopped?\n"; } else if (nextRoutine.endPC == null) { str += $"ensures var s' := {m}.Armada_GetNextState(s, step, tid); s'.stop_reason.Armada_NotStopped? && tid !in s'.threads\n"; } else { str += $@" ensures var s' := {m}.Armada_GetNextState(s, step, tid); s'.stop_reason.Armada_NotStopped? && tid in s'.threads && s'.threads[tid].pc.{nextRoutine.endPC}? "; } } str += $@" {{ { pr.GetOpenValidStepInvocation(nextRoutine) } }} "; pgp.AddLemma(str, "pceffect"); } var pcToNextRoutines = new Dictionary>(); foreach (var nextRoutine in symbols.NextRoutines.Where(r => !r.Tau)) { if (pcToNextRoutines.ContainsKey(nextRoutine.startPC)) { pcToNextRoutines[nextRoutine.startPC].Add(nextRoutine); } else { pcToNextRoutines[nextRoutine.startPC] = new List{ nextRoutine }; } } var pcs = new List(); symbols.AllMethods.AppendAllPCs(pcs); foreach (var pc in pcs) { str = $@" lemma lemma_PossibleStepsFromPC_{m}_{pc}(s: {m}.Armada_TotalState, step: {m}.Armada_Step, tid: Armada_ThreadHandle) requires {m}.Armada_ValidStep(s, step, tid) requires tid in s.threads requires s.threads[tid].pc.{pc}? ensures step.Armada_Step_Tau?"; var nextRoutines = pcToNextRoutines.ContainsKey(pc) ? pcToNextRoutines[pc] : new List(); str += String.Concat(nextRoutines.Select(r => $" || step.Armada_Step_{r.NameSuffix}?")); str += @" { match step { "; foreach (var nextRoutine in symbols.NextRoutines) { str += $"{pr.CaseEntry(nextRoutine)} =>\n"; if (!nextRoutine.Tau && !nextRoutine.startPC.Equals(pc)) { str += $"lemma_PCEffectOfStep_{m}_{nextRoutine.NameSuffix}(s, step, tid);\n"; } } str += "} }"; pgp.AddLemma(str, "pceffect"); } } //////////////////////////////////////////////////////////////////////// /// PC map //////////////////////////////////////////////////////////////////////// protected void MakeTrivialPCMap() { pcMap = new Dictionary(); var pcs = new List(); pgp.symbolsLow.AllMethods.AppendAllPCs(pcs); foreach (var pc in pcs) { pcMap[pc] = pc.CloneWithNewSymbolTable(pgp.symbolsHigh); } } protected virtual void ExtendPCMapWithExternalAndStructsMethods() { foreach (var methodName in pgp.symbolsLow.MethodNames) { var st = pgp.symbolsLow.GetMethodSymbolTable(methodName); if (st.IsExternal || st.IsFromStructsModule) { List allPCs = new List(); pgp.symbolsLow.AllMethods.LookupMethod(methodName).AppendAllPCs(allPCs); foreach (var pc in allPCs) { pcMap[pc] = pc.CloneWithNewSymbolTable(pgp.symbolsHigh); } } } } protected ArmadaPC LiftPC(ArmadaPC pc) { if (pc == null) { return null; } return pcMap[pc]; } protected virtual String TranslateFormalNameUsingPcMap(NextFormal formal, ArmadaPC lpc, Dictionary pcMap) { var lpcName = lpc.ToString(); if (formal.GloballyUniqueVarName.Contains(lpcName)) { return formal.GloballyUniqueVarName.Replace(lpcName, pcMap[lpc].ToString()); } else { return formal.GloballyUniqueVarName; } } protected virtual void GenerateNextRoutineMap(bool warnOnMissingRoutines = true) { nextRoutineMap = new Dictionary(); var hmap = new Dictionary, NextRoutine>(); foreach (var nextRoutine in pgp.symbolsHigh.NextRoutines) { var t = new Tuple(nextRoutine.startPC, nextRoutine.endPC, nextRoutine.UndefinedBehavior, nextRoutine.BranchOutcome); if (hmap.ContainsKey(t)) { AH.PrintError(pgp.prog, $"More than one next routine from PC {nextRoutine.startPC} to {nextRoutine.endPC} in level {pgp.mHigh.Name}"); hmap.Remove(t); } else { hmap[t] = nextRoutine; } } foreach (var nextRoutine in pgp.symbolsLow.NextRoutines) { var startPC = LiftPC(nextRoutine.startPC); var endPC = LiftPC(nextRoutine.endPC); var t = new Tuple(startPC, endPC, nextRoutine.UndefinedBehavior, nextRoutine.BranchOutcome); if (hmap.ContainsKey(t)) { nextRoutineMap[nextRoutine] = hmap[t]; } else if (warnOnMissingRoutines) { AH.PrintWarning(pgp.prog, $"No next routine found in high level from {startPC} to {endPC}"); } } } protected virtual NextRoutine LiftNextRoutine(NextRoutine lNextRoutine) { NextRoutine hNextRoutine; nextRoutineMap.TryGetValue(lNextRoutine, out hNextRoutine); return hNextRoutine; } protected virtual AtomicPath LiftAtomicPath(AtomicPath lPath) { AtomicPath hPath = null; pathMap.TryGetValue(lPath, out hPath); return hPath; } //////////////////////////////////////////////////////////////////////// /// Includes and imports //////////////////////////////////////////////////////////////////////// private void AddInductiveInvariant(InductiveInvariantArmadaProofDecl d) { string invKey = d.InvariantName; string invName, str; var userInvariants = pgp.symbolsLow.GlobalInvariants; var attrs = d.CanBeRevealed() ? "{:opaque}" : ""; if (d.Code != null) { invName = $"UserInv_{invKey}"; str = $@" predicate {attrs} {invName}(s:LPlusState) {{ var threads := s.s.threads; var globals := s.s.mem.globals; var ghosts := s.s.ghosts; var tid_init := s.config.tid_init; { d.Code } }} "; pgp.AddPredicate(str, "defs"); if (d.CanBeRevealed()) { pgp.AddOpaqueUserDef(invName); } } else if (userInvariants.ContainsKey(invKey)) { var inv = userInvariants[invKey]; invName = $"UserInv_{invKey}"; str = $@" predicate {attrs} {invName}(splus:LPlusState) {{ L.{inv.TranslatedName}(splus.s) }} "; pgp.AddPredicate(str, "defs"); if (d.CanBeRevealed()) { pgp.AddOpaqueUserDef(invName); } } else { AH.PrintError(pgp.prog, d.tok, $"No invariant named {invKey} found among the invariants"); return; } var dependencies = new List(d.Dependencies); invariants.Add(new UserInvariantInfo(invKey, invName, dependencies, d.CanBeRevealed())); } private void ParseImports() { foreach (var topDecl in pgp.MainProof.Module.TopLevelDecls) { if (topDecl is ImportFileArmadaProofDecl) { var ifd = (ImportFileArmadaProofDecl)topDecl; importedFiles.Add(ifd); } else if (topDecl is ImportModuleArmadaProofDecl) { var imd = (ImportModuleArmadaProofDecl)topDecl; importedModules.Add(imd); } else if (topDecl is InductiveInvariantArmadaProofDecl) { var iid = (InductiveInvariantArmadaProofDecl)topDecl; AddInductiveInvariant(iid); } else if (topDecl is ExtraMaterialArmadaProofDecl) { var emapd = (ExtraMaterialArmadaProofDecl)topDecl; pgp.AddExtraMaterial(emapd.Loc, emapd.Contents); } else if (topDecl is UseRegionsArmadaProofDecl) { GenerateRegionInvariant(); } else if (topDecl is UseAddressInvariantArmadaProofDecl) { GenerateAddressableInvariant(); } else if (topDecl is AuxiliaryArmadaProofDecl) { var iad = (AuxiliaryArmadaProofDecl)topDecl; if (iad.TypeDefinitionCode.Length > 0) { var d = AH.ParseTopLevelDecl(pgp.prog, iad.TypeName, iad.TypeDefinitionCode); if (d is DatatypeDecl) { pgp.AddTopLevelDecl((DatatypeDecl)d, "specs"); } else if (d is TypeSynonymDecl) { pgp.AddTopLevelDecl((TypeSynonymDecl)d, "specs"); } else { AH.PrintError(pgp.prog, d.tok, "Type definition code is neither a datatype declaration nor a type declaration"); continue; } } string str; string auxInitName = $"AuxInit_{iad.FieldName}"; str = $@" function {auxInitName}(s:LState, config:Armada_Config) : {iad.TypeName} {{ {iad.InitCode} }} "; pgp.AddFunction(str, "specs"); string auxNextName = $"AuxNext_{iad.FieldName}"; str = $@" function {auxNextName}(s: LPlusState, s': LState, step: L.Armada_Step, tid: Armada_ThreadHandle) : {iad.TypeName} {{ {iad.NextCode} }} "; pgp.AddFunction(str, "specs"); var auxiliaryInfo = new AuxiliaryInfo(iad.FieldName, iad.TypeName, auxInitName, auxNextName); auxiliaries.Add(auxiliaryInfo); } } } protected virtual void AddIncludesAndImports() { ParseImports(); } //////////////////////////////////////////////////////////////////////// /// Revelations //////////////////////////////////////////////////////////////////////// protected void GenerateRevelationLemmas() { GenerateRevelationLemmas("L", pgp.symbolsLow); GenerateRevelationLemmas("H", pgp.symbolsHigh); } protected void GenerateRevelationLemmas(string moduleName, ArmadaSymbolTable symbols) { string str; var pr = new ModuleStepPrinter(moduleName); foreach (var nextRoutine in symbols.NextRoutines) { str = $@" lemma lemma_OpenStep_{moduleName}_{nextRoutine.NameSuffix}( s: {moduleName}.Armada_TotalState, step: {moduleName}.Armada_Step, tid: Armada_ThreadHandle ) requires step.Armada_Step_{nextRoutine.NameSuffix}? ensures {moduleName}.Armada_ValidStep(s, step, tid) <==> tid in s.threads && s.stop_reason.Armada_NotStopped? && {pr.ValidStepInvocation(nextRoutine)} ensures {moduleName}.Armada_ValidStep(s, step, tid) ==> {moduleName}.Armada_GetNextState(s, step, tid) == {pr.GetNextStateInvocation(nextRoutine)} {{ reveal {moduleName}.Armada_ValidStepCases(); reveal {moduleName}.Armada_GetNextState(); }} "; pgp.AddLemma(str, "revelations"); } } //////////////////////////////////////////////////////////////////////// /// Atomic specs //////////////////////////////////////////////////////////////////////// protected void GenerateAtomicSpecs(bool avoidFinalUnmappedStoppingStep = true, HashSet lExtraRecurrentPCs = null, HashSet hExtraRecurrentPCs = null) { GeneratePCEffectLemmas(); lAtomic = new AtomicSpec(pgp, pgp.symbolsLow, "latomic", "LAtomic", true, "L", "LPlusState", "LPlus_GetSpecFunctions()", "GetLPlusSpec()", "LPlus_ValidStep", "LPlus_GetNextState"); lAtomic.MakeSpec(lExtraRecurrentPCs); GenerateLiftToAtomicLemma(); hAtomic = new AtomicSpec(pgp, pgp.symbolsHigh, "hatomic", "HAtomic", false, "H", "HState", "H.Armada_GetSpecFunctions()", "GetHSpec()", "H.Armada_ValidStep", "H.Armada_GetNextState"); hAtomic.MakeSpec(hExtraRecurrentPCs); GenerateLiftFromAtomicLemma(); if (nextRoutineMap == null) { pathMap = lAtomic.CreatePathMap(hAtomic); } else { pathMap = lAtomic.CreatePathMap(hAtomic, nextRoutineMap, avoidFinalUnmappedStoppingStep); } } //////////////////////////////////////////////////////////////////////// /// LiftToAtomic //////////////////////////////////////////////////////////////////////// public void GenerateLiftToAtomicSimpleRequirementsLemma() { string str = @" lemma lemma_LiftToAtomicSimpleRequirements() ensures var lasf := LPlus_GetSpecFunctions(); var hasf := LAtomic_GetSpecFunctions(); && LInitImpliesHInit(lasf, hasf) && StateOKsMatch(lasf, hasf) && NonyieldingPCsMatch(lasf, hasf) && ThreadPCsMatch(lasf, hasf) { } "; pgp.AddLemma(str, "LiftToAtomic"); } public void GenerateTausLiftableLemma() { var stepPr = new LPlusStepPrinter(); stepPr.Step = "lstep"; var pathPr = new PathPrinter(lAtomic); pathPr.State = "s"; pathPr.States = "hstates"; pathPr.Steps = "hsteps"; pathPr.Path = "hpath"; string str = $@" lemma lemma_TausLiftable() ensures var lasf := LPlus_GetSpecFunctions(); var hasf := LAtomic_GetSpecFunctions(); TausLiftable(lasf, hasf) {{ reveal_LAtomic_ValidPath(); reveal_LAtomic_GetStateAfterPath(); var lasf := LPlus_GetSpecFunctions(); var hasf := LAtomic_GetSpecFunctions(); forall s, s', lstep, tid {{:trigger TausLiftableConditions(lasf, s, s', lstep, tid)}} | TausLiftableConditions(lasf, s, s', lstep, tid) ensures exists hpath :: && hasf.path_valid(s, hpath, tid) && hasf.path_type(hpath).AtomicPathType_Tau? && s' == hasf.path_next(s, hpath, tid) {{ { stepPr.GetOpenValidStepInvocation(pgp.symbolsLow.TauNextRoutine) } var hpath := LAtomic_Path_Tau(LAtomic_PathSteps_Tau(lstep)); { pathPr.GetOpenPathInvocation(lAtomic.TauPath) } ProofCustomizationGoesHere(); assert && hasf.path_valid(s, hpath, tid) && hasf.path_type(hpath).AtomicPathType_Tau? && s' == hasf.path_next(s, hpath, tid); }} }} "; pgp.AddLemma(str, "LiftToAtomic"); } public string GenerateCompressStepSequenceBodyForPathPrefix(AtomicPathPrefix pathPrefix) { var pos = pathPrefix.NumNextRoutines - 1; var nextRoutine = pathPrefix.LastNextRoutine; string str = ""; var stepsRemaining = (pos == 0) ? "steps" : $"steps[{pos}..]"; str += $@" if steps[{pos}].Armada_Step_{nextRoutine.NameSuffix}? {{ var s{pos + 1} := asf.step_next(s{pos}, steps[{pos}], tid); assert steps[{pos+1}..] == {stepsRemaining}[1..]; assert StepsStartNonyieldingNonrecurrent(asf, LAtomic_IsRecurrentPC, s{pos + 1}, s', steps[{pos + 1}..], tid); assert |steps[{pos + 1}..]| == 0 <==> ThreadYieldingOrRecurrent(asf, LAtomic_IsRecurrentPC, s{pos + 1}, tid); lemma_PCEffectOfStep_L_{nextRoutine.NameSuffix}(s{pos}.s, steps[{pos}], tid); "; if (pathPrefix.EndType is PCAtomicType.Nonyielding) { str += $@" assert !ThreadYieldingOrRecurrent(asf, LAtomic_IsRecurrentPC, s{pos + 1}, tid); assert steps[{pos+1}] == steps[{pos + 1}..][0]; lemma_PossibleStepsFromPC_L_{nextRoutine.endPC}(s{pos + 1}.s, steps[{pos + 1}], tid); "; foreach (var descendant in pathPrefix.Extensions) { str += GenerateCompressStepSequenceBodyForPathPrefix(descendant); } str += @" assert false; } "; } else { var path = pathPrefix.PathVal; var pr = new PathPrinter(lAtomic); str += $@" assert ThreadYieldingOrRecurrent(asf, LAtomic_IsRecurrentPC, s{pos + 1}, tid); assert s' == s{pos + 1}; path := LAtomic_Path_{path.Name}(LAtomic_PathSteps_{path.Name}( "; str += String.Join(", ", Enumerable.Range(0, pos + 1).Select(i => $"steps[{i}]")); str += $@")); { pr.GetOpenPathInvocation(path) } return; }} "; } return str; } public void GenerateCompressStepSequenceIntoPathStartingAt(ArmadaPC startPC) { string str; str = $@" lemma lemma_CompressStepSequenceIntoPathStartingAt_{startPC}( asf: Armada_SpecFunctions, s: LPlusState, s': LPlusState, steps: seq, tid: Armada_ThreadHandle ) returns ( path: LAtomic_Path ) requires asf == LPlus_GetSpecFunctions() requires |steps| > 0 requires !steps[0].Armada_Step_Tau? requires LPlus_ValidStep(s, steps[0], tid) requires ThreadYieldingOrRecurrent(asf, LAtomic_IsRecurrentPC, s, tid) requires ThreadYieldingOrRecurrent(asf, LAtomic_IsRecurrentPC, s', tid) requires StepsStartNonyieldingNonrecurrent(asf, LAtomic_IsRecurrentPC, asf.step_next(s, steps[0], tid), s', steps[1..], tid) requires s.s.threads[tid].pc.{startPC}? ensures LAtomic_ValidPath(s, path, tid) ensures s' == LAtomic_GetStateAfterPath(s, path, tid) ensures !LAtomic_GetPathType(path).AtomicPathType_Tau? ensures AtomicPathTypeMatchesPCTypes(LAtomic_GetSpecFunctions(), s, path, tid) {{ var s0 := s; lemma_PossibleStepsFromPC_L_{startPC}(s.s, steps[0], tid); "; foreach (var pathPrefix in lAtomic.RootPathPrefixesByPC(startPC)) { str += GenerateCompressStepSequenceBodyForPathPrefix(pathPrefix); } str += @" assert false; }} "; pgp.AddLemma(str, "LiftToAtomic"); } public void GenerateCompressStepSequenceLemmas() { string str; str = @" lemma lemma_CompressStepSequenceIntoPath( asf: Armada_SpecFunctions, s: LPlusState, s': LPlusState, steps: seq, tid: Armada_ThreadHandle ) returns ( path: LAtomic_Path ) requires asf == LPlus_GetSpecFunctions() requires |steps| > 0 requires !steps[0].Armada_Step_Tau? requires LPlus_ValidStep(s, steps[0], tid) requires ThreadYieldingOrRecurrent(asf, LAtomic_IsRecurrentPC, s, tid) requires ThreadYieldingOrRecurrent(asf, LAtomic_IsRecurrentPC, s', tid) requires StepsStartNonyieldingNonrecurrent(asf, LAtomic_IsRecurrentPC, asf.step_next(s, steps[0], tid), s', steps[1..], tid) ensures LAtomic_ValidPath(s, path, tid) ensures s' == LAtomic_GetStateAfterPath(s, path, tid) ensures !LAtomic_GetPathType(path).AtomicPathType_Tau? ensures AtomicPathTypeMatchesPCTypes(LAtomic_GetSpecFunctions(), s, path, tid) { var pc := s.s.threads[tid].pc; assert L.Armada_IsNonyieldingPC(pc) ==> LAtomic_IsRecurrentPC(pc); match pc { "; foreach (var pc in lAtomic.AllPCs) { str += $" case {pc} =>\n"; if (pgp.symbolsLow.IsNonyieldingPC(pc) && !lAtomic.RecurrentPCs.Contains(pc)) { str += " assert false;\n"; } else { GenerateCompressStepSequenceIntoPathStartingAt(pc); str += $" path := lemma_CompressStepSequenceIntoPathStartingAt_{pc}(asf, s, s', steps, tid);\n"; } } str += "}\n}\n"; pgp.AddLemma(str, "LiftToAtomic"); } public void GenerateSequencesCompressibleLemma() { string str = @" lemma lemma_SequencesCompressible() ensures SequencesCompressible(LPlus_GetSpecFunctions(), LAtomic_GetSpecFunctions(), LAtomic_IsRecurrentPC) { var lasf := LPlus_GetSpecFunctions(); var hasf := LAtomic_GetSpecFunctions(); forall s, s', lsteps, tid {:trigger SequencesCompressibleConditions(lasf, LAtomic_IsRecurrentPC, s, s', lsteps, tid)} | SequencesCompressibleConditions(lasf, LAtomic_IsRecurrentPC, s, s', lsteps, tid) ensures exists hpath :: SequencesCompressibleConclusions(hasf, s, s', tid, hpath) { var hpath := lemma_CompressStepSequenceIntoPath(lasf, s, s', lsteps, tid); assert SequencesCompressibleConclusions(hasf, s, s', tid, hpath); } } "; pgp.AddLemma(str, "LiftToAtomic"); } public void GenerateLiftToAtomicLemma() { var liftFile = pgp.proofFiles.CreateAuxiliaryProofFile("LiftToAtomic"); liftFile.IncludeAndImportGeneratedFile("specs"); liftFile.IncludeAndImportGeneratedFile("defs"); liftFile.IncludeAndImportGeneratedFile("pceffect"); liftFile.IncludeAndImportGeneratedFile("revelations"); liftFile.IncludeAndImportGeneratedFile("latomic"); liftFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/seqs.s.dfy", "util_collections_seqs_s"); liftFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/LiftToAtomic.i.dfy", "LiftToAtomicModule"); liftFile.AddImport("GenericArmadaAtomicModule"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("LiftToAtomic"); GenerateLiftToAtomicSimpleRequirementsLemma(); GenerateTausLiftableLemma(); GenerateCompressStepSequenceLemmas(); GenerateSequencesCompressibleLemma(); string str = $@" lemma lemma_LiftToAtomic() returns (refinement_relation:RefinementRelation) ensures SpecRefinesSpec(Armada_SpecFunctionsToSpec(LPlus_GetSpecFunctions()), AtomicSpec(LAtomic_GetSpecFunctions()), refinement_relation) ensures refinement_relation == iset s: LPlusState | true :: RefinementPair(s, s) {{ lemma_LiftToAtomicSimpleRequirements(); lemma_TausLiftable(); lemma_SequencesCompressible(); refinement_relation := lemma_SpecRefinesAtomicSpec(LPlus_GetSpecFunctions(), LAtomic_GetSpecFunctions(), LAtomic_IsRecurrentPC); }} "; pgp.AddLemma(str, "LiftToAtomic"); } //////////////////////////////////////////////////////////////////////// /// LiftFromAtomic //////////////////////////////////////////////////////////////////////// private void GenerateLiftTauPathFromAtomicLemma(AtomicPath atomicPath) { var pr = new PathPrinter(hAtomic); var str = $@" lemma lemma_HAtomic_LiftFromAtomic_Tau( lasf: AtomicSpecFunctions, hasf: Armada_SpecFunctions, s: H.Armada_TotalState, s': H.Armada_TotalState, path: HAtomic_Path, tid: Armada_ThreadHandle ) returns ( hsteps: seq ) requires lasf == HAtomic_GetSpecFunctions() requires hasf == H.Armada_GetSpecFunctions() requires lasf.path_type(path).AtomicPathType_Tau? requires AtomicLiftPathFromAtomicPossible(lasf, s, s', path, tid) ensures AtomicLiftPathFromAtomicSuccessful(lasf, hasf, s, s', path, tid, hsteps) {{ { pr.GetOpenPathInvocation(hAtomic.TauPath) } ProofCustomizationGoesHere(); hsteps := [steps.step0]; lemma_PCEffectOfStep_H_Tau(s, steps.step0, tid); }} "; pgp.AddLemma(str, "LiftFromAtomic"); } private void GenerateSpecificLiftPathFromAtomicLemma(AtomicPath atomicPath) { if (atomicPath.Tau) { GenerateLiftTauPathFromAtomicLemma(atomicPath); return; } var n = atomicPath.NumNextRoutines; var pr = new PathPrinter(hAtomic); var str = $@" lemma lemma_HAtomic_LiftFromAtomic_{atomicPath.Name}( lasf: AtomicSpecFunctions, hasf: Armada_SpecFunctions, s: H.Armada_TotalState, s': H.Armada_TotalState, path: HAtomic_Path, tid: Armada_ThreadHandle ) returns ( hsteps: seq ) requires lasf == HAtomic_GetSpecFunctions() requires hasf == H.Armada_GetSpecFunctions() requires path.HAtomic_Path_{atomicPath.Name}? requires AtomicLiftPathFromAtomicPossible(lasf, s, s', path, tid) ensures AtomicLiftPathFromAtomicSuccessful(lasf, hasf, s, s', path, tid, hsteps) {{ { pr.GetOpenValidPathInvocation(atomicPath) } "; str += "hsteps := ["; str += String.Join(", ", Enumerable.Range(0, n).Select(i => $"steps.step{i}")); str += "];\n"; str += String.Concat(Enumerable.Range(0, n).Select( i => $@" lemma_PCEffectOfStep_H_{atomicPath.NextRoutines[i].NameSuffix}(states.s{i}, steps.step{i}, tid); " )); str += String.Concat(Enumerable.Range(0, n).Select( i => $@" assert Armada_NextMultipleSteps(hasf, states.s{n - i}, s', hsteps[{n-i}..], tid); assert hsteps[{n-i-1}..][1..] == hsteps[{n-i}..]; " )); str += String.Concat(Enumerable.Range(1, n - 1).Select( i => $@" assert Armada_StepsStartNonyielding(hasf, states.s{n - i}, s', hsteps[{n-i}..], tid); " )); if (atomicPath.EndType == PCAtomicType.Yielding) { str += $@" assert Armada_ThreadYielding(hasf, s', tid); "; } str += $@" assert hsteps == hsteps[0..]; assert {atomicPath.OptionalNotForOK}hasf.state_ok(states.s{n}); assert Armada_NextMultipleSteps(hasf, s, s', hsteps, tid); }} "; pgp.AddLemma(str, "LiftFromAtomic"); } private void GenerateLiftPathFromAtomicLemma() { foreach (var atomicPath in hAtomic.AtomicPaths) { GenerateSpecificLiftPathFromAtomicLemma(atomicPath); } var str = $@" lemma lemma_LiftPathFromAtomic( lasf: AtomicSpecFunctions, hasf: Armada_SpecFunctions, s: H.Armada_TotalState, s': H.Armada_TotalState, path: HAtomic_Path, tid: Armada_ThreadHandle ) returns ( hsteps: seq ) requires lasf == HAtomic_GetSpecFunctions() requires hasf == H.Armada_GetSpecFunctions() requires AtomicLiftPathFromAtomicPossible(lasf, s, s', path, tid) ensures AtomicLiftPathFromAtomicSuccessful(lasf, hasf, s, s', path, tid, hsteps) {{ match path {{ "; str += String.Concat(hAtomic.AtomicPaths.Select(p => $@" case HAtomic_Path_{p.Name}(_) => hsteps := lemma_HAtomic_LiftFromAtomic_{p.Name}(lasf, hasf, s, s', path, tid); return; ")); str += "}\n }\n"; pgp.AddLemma(str, "LiftFromAtomic"); } public void GenerateLiftFromAtomicLemma() { var liftFile = pgp.proofFiles.CreateAuxiliaryProofFile("LiftFromAtomic"); liftFile.IncludeAndImportGeneratedFile("specs"); liftFile.IncludeAndImportGeneratedFile("defs"); liftFile.IncludeAndImportGeneratedFile("pceffect"); liftFile.IncludeAndImportGeneratedFile("revelations"); liftFile.IncludeAndImportGeneratedFile("hatomic"); liftFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/seqs.s.dfy", "util_collections_seqs_s"); liftFile.AddIncludeImport(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/LiftFromAtomic.i.dfy", "LiftFromAtomicModule"); liftFile.AddImport("GenericArmadaAtomicModule"); pgp.proofFiles.MainProof.IncludeAndImportGeneratedFile("LiftFromAtomic"); GenerateLiftPathFromAtomicLemma(); string str = $@" lemma lemma_LiftFromAtomic() returns (refinement_relation: RefinementRelation) ensures SpecRefinesSpec(AtomicSpec(HAtomic_GetSpecFunctions()), Armada_SpecFunctionsToSpec(H.Armada_GetSpecFunctions()), refinement_relation) ensures refinement_relation == iset s: H.Armada_TotalState | true :: RefinementPair(s, s) {{ var lasf := HAtomic_GetSpecFunctions(); var hasf := H.Armada_GetSpecFunctions(); forall ls, ls', path, tid | AtomicLiftPathFromAtomicPossible(lasf, ls, ls', path, tid) ensures exists hsteps :: AtomicLiftPathFromAtomicSuccessful(lasf, hasf, ls, ls', path, tid, hsteps) {{ var hsteps := lemma_LiftPathFromAtomic(lasf, hasf, ls, ls', path, tid); }} refinement_relation := lemma_AtomicSpecRefinesSpec(lasf, hasf); }} "; pgp.AddLemma(str, "LiftFromAtomic"); } //////////////////////////////////////////////////////////////////////// /// Utility functions //////////////////////////////////////////////////////////////////////// protected string GetConversionFunctionForTypeName_LH(string s) { var m = Regex.Match(s, @"^Armada_(PC|Globals|StoreBufferLocation|StoreBufferEntry|Stack|StoreBuffer|Thread|Threads|TotalState|Config)$"); if (m.Success) { return $"Abstract{m.Groups[1]}_LH"; } return null; } protected string GetConversionFunctionForTypeName_HL(string s) { var m = Regex.Match(s, @"^Armada_(PC|Globals|StoreBufferLocation|StoreBufferEntry|Stack|StoreBuffer|Thread|Threads|TotalState|Config)$"); if (m.Success) { return $"Abstract{m.Groups[1]}_HL"; } return null; } protected Type AddModuleNameToArmadaType(Type ty, string moduleName) { if (ty is UserDefinedType) { var udt = (UserDefinedType)ty; string conversionFn = GetConversionFunctionForTypeName_LH(udt.Name); if (conversionFn != null) { return new UserDefinedType(udt.tok, $"{moduleName}.{udt.Name}", udt.TypeArgs); } } else if (ty is SeqType) { var sty = (SeqType)ty; return new SeqType(AddModuleNameToArmadaType(sty.Arg, moduleName)); } else if (ty is SetType) { var sty = (SetType)ty; return new SetType(sty.Finite, AddModuleNameToArmadaType(sty.Arg, moduleName)); } return ty; } protected string GetCalleeNameForCallStmt(Statement stmt) { var us = (UpdateStmt)stmt; var rhs = us.Rhss[0]; var ex = (ExprRhs)rhs; var suffix = (ApplySuffix)ex.Expr; var suffixName = (NameSegment)suffix.Lhs; return suffixName.Name; } //////////////////////////////////////////////////////////////////////// /// State abstraction functions //////////////////////////////////////////////////////////////////////// protected virtual void GenerateConvertPC_LH() { var caseBodies = String.Concat(pcMap.Select(mapping => $"case {mapping.Key.ToString()} => H.{mapping.Value}\n")); var fn = $@" function ConvertPC_LH(pc: L.Armada_PC) : H.Armada_PC {{ match pc {caseBodies} }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertStackVars_LH(string methodName) { var smst = pgp.symbolsLow.GetMethodSymbolTable(methodName); var ps = smst.AllVariablesInOrder.Select(v => $"vs.{v.FieldName}"); var fn = $@" function ConvertStackVars_LH_{methodName}(vs: L.Armada_StackVars_{methodName}) : H.Armada_StackVars_{methodName} {{ H.Armada_StackVars_{methodName}({AH.CombineStringsWithCommas(ps)}) }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertStackFrame_LH() { var methodNames = pgp.symbolsLow.MethodNames; foreach (var methodName in methodNames) { GenerateConvertStackVars_LH(methodName); } var caseBodies = String.Concat(methodNames.Select(methodName => $@"case Armada_StackFrame_{methodName}(vs) => H.Armada_StackFrame_{methodName}(ConvertStackVars_LH_{methodName}(vs))" )); var fn = $@" function ConvertStackFrame_LH(frame: L.Armada_StackFrame) : H.Armada_StackFrame {{ match frame {caseBodies} }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertGlobals_LH() { var ps = new List(); foreach (var varName in pgp.symbolsLow.Globals.VariableNames) { var v = pgp.symbolsLow.Globals.Lookup(varName); if (v is GlobalUnaddressableArmadaVariable) { ps.Add($"globals.{v.FieldName}"); } } var fn = $@" function ConvertGlobals_LH(globals: L.Armada_Globals) : H.Armada_Globals {{ H.Armada_Globals({AH.CombineStringsWithCommas(ps)}) }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertGhosts_LH() { var ps = new List(); foreach (var varName in pgp.symbolsLow.Globals.VariableNames) { var v = pgp.symbolsLow.Globals.Lookup(varName); if (v is GlobalGhostArmadaVariable) { ps.Add($"ghosts.{v.FieldName}"); } } var fn = $@" function ConvertGhosts_LH(ghosts: L.Armada_Ghosts) : H.Armada_Ghosts {{ H.Armada_Ghosts({AH.CombineStringsWithCommas(ps)}) }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertAddrs_LH() { var ps = new List(); foreach (var varName in pgp.symbolsLow.Globals.VariableNames) { var v = pgp.symbolsLow.Globals.Lookup(varName); if (v is GlobalAddressableArmadaVariable) { ps.Add($"addrs.{v.FieldName}"); } } var fn = $@" function ConvertAddrs_LH(addrs: L.Armada_Addrs) : H.Armada_Addrs {{ H.Armada_Addrs({AH.CombineStringsWithCommas(ps)}) }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertGlobalStaticVar_LH() { var caseBodies = "case Armada_GlobalStaticVarNone => H.Armada_GlobalStaticVarNone\n"; foreach (var varName in pgp.symbolsLow.Globals.VariableNames) { var gv = pgp.symbolsLow.Globals.Lookup(varName); if (gv is GlobalUnaddressableArmadaVariable) { caseBodies += $"case Armada_GlobalStaticVar_{varName} => H.Armada_GlobalStaticVar_{varName}\n"; } } var fn = $@" function ConvertGlobalStaticVar_LH(v: L.Armada_GlobalStaticVar) : H.Armada_GlobalStaticVar {{ match v {caseBodies} }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertSharedMemory_LH() { string str = @" function ConvertSharedMemory_LH(mem: L.Armada_SharedMemory) : H.Armada_SharedMemory { H.Armada_SharedMemory(mem.heap, ConvertGlobals_LH(mem.globals)) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertStoreBufferLocation_LH() { string str = @" function ConvertStoreBufferLocation_LH(loc:L.Armada_StoreBufferLocation) : H.Armada_StoreBufferLocation { match loc case Armada_StoreBufferLocation_Unaddressable(v, fields) => H.Armada_StoreBufferLocation_Unaddressable(ConvertGlobalStaticVar_LH(v), fields) case Armada_StoreBufferLocation_Addressable(p) => H.Armada_StoreBufferLocation_Addressable(p) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertStoreBufferEntry_LH() { string str = @" function ConvertStoreBufferEntry_LH(entry:L.Armada_StoreBufferEntry) : H.Armada_StoreBufferEntry { H.Armada_StoreBufferEntry(ConvertStoreBufferLocation_LH(entry.loc), entry.value, ConvertPC_LH(entry.pc)) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertStoreBuffer_LH() { string str = @" function ConvertStoreBuffer_LH(entries:seq) : seq { MapSeqToSeq(entries, ConvertStoreBufferEntry_LH) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertExtendedFrame_LH() { string str = @" function ConvertExtendedFrame_LH(eframe:L.Armada_ExtendedFrame) : H.Armada_ExtendedFrame { H.Armada_ExtendedFrame(ConvertPC_LH(eframe.return_pc), ConvertStackFrame_LH(eframe.frame), eframe.new_ptrs) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertStack_LH() { string str = @" function ConvertStack_LH(stack:seq) : seq { MapSeqToSeq(stack, ConvertExtendedFrame_LH) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertThread_LH() { string str = @" function ConvertThread_LH(t:L.Armada_Thread) : H.Armada_Thread { H.Armada_Thread(ConvertPC_LH(t.pc), ConvertStackFrame_LH(t.top), t.new_ptrs, ConvertStack_LH(t.stack), ConvertStoreBuffer_LH(t.storeBuffer)) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertThreads_LH() { string str = @" function ConvertThreads_LH(threads:map) : map { MapMapToMap(threads, ConvertThread_LH) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertTotalState_LH() { string str = @" function ConvertTotalState_LH(s:L.Armada_TotalState) : H.Armada_TotalState { H.Armada_TotalState(s.stop_reason, ConvertThreads_LH(s.threads), ConvertSharedMemory_LH(s.mem), ConvertGhosts_LH(s.ghosts), ConvertAddrs_LH(s.addrs), s.joinable_tids) } "; pgp.AddFunction(str, "convert"); str = @" function ConvertTotalState_LPlusH(s:LPlusState) : HState { ConvertTotalState_LH(s.s) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertConfig_LH() { string str = @" function ConvertConfig_LH(config:Armada_Config) : Armada_Config { config } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateStateAbstractionFunctions_LH() { GenerateConvertPC_LH(); GenerateConvertStackFrame_LH(); GenerateConvertGlobals_LH(); GenerateConvertGhosts_LH(); GenerateConvertAddrs_LH(); GenerateConvertGlobalStaticVar_LH(); GenerateConvertSharedMemory_LH(); GenerateConvertStoreBufferLocation_LH(); GenerateConvertStoreBufferEntry_LH(); GenerateConvertStoreBuffer_LH(); GenerateConvertExtendedFrame_LH(); GenerateConvertStack_LH(); GenerateConvertThread_LH(); GenerateConvertThreads_LH(); GenerateConvertTotalState_LH(); GenerateConvertConfig_LH(); } //////////////////////////////////////////////////////////////////////// /// Step abstraction functions //////////////////////////////////////////////////////////////////////// protected virtual string GetStepCaseForSuppressedNextRoutine_LH(NextRoutine nextRoutine) { string nextRoutineName = nextRoutine.NameSuffix; // This is the case where the next routine doesn't have a corresponding next routine in the high layer, // e.g., because it's an assignment to a hidden variable. In this case, just arbitrarily map it to // something we know to exist, namely H.Armada_Step_Tau. var bvs = nextRoutine.HasFormals ? "_" : ""; return $"case Armada_Step_{nextRoutine.NameSuffix}({bvs}) => H.Armada_Step_Tau\n"; } protected virtual string GetStepCaseForNormalNextRoutine_LH(NextRoutine nextRoutine) { string nextRoutineName = nextRoutine.NameSuffix; var hNextRoutine = LiftNextRoutine(nextRoutine); string hname = (hNextRoutine != null) ? hNextRoutine.NameSuffix : "Tau"; var bvs = nextRoutine.HasFormals ? "params" : ""; string caseBody; if (hNextRoutine == null) { caseBody = "H.Armada_Step_Tau"; } else if (hNextRoutine.HasFormals) { var ps = nextRoutine.Formals.Select(f => $"params.{f.LocalVarName}"); caseBody = $"H.Armada_Step_{hname}(H.Armada_StepParams_{hname}({AH.CombineStringsWithCommas(ps)}))"; } else { caseBody = $"H.Armada_Step_{hname}"; } return $"case Armada_Step_{nextRoutineName}({bvs}) => {caseBody}\n"; } protected virtual string GetStepCaseForNextRoutine_LH(NextRoutine nextRoutine) { return GetStepCaseForNormalNextRoutine_LH(nextRoutine); } protected virtual void GenerateConvertStep_LH() { var caseBodies = String.Concat(pgp.symbolsLow.NextRoutines.Select(nextRoutine => GetStepCaseForNextRoutine_LH(nextRoutine))); var fn = $@" function ConvertStep_LH(step: L.Armada_Step) : H.Armada_Step {{ match step {caseBodies} }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertAtomicPathStateDependent_LH() { string str = @" function ConvertAtomicPath_LH(ls: LPlusState, lpath: LAtomic_Path, tid: Armada_ThreadHandle) : HAtomic_Path requires LAtomic_ValidPath(ls, lpath, tid) { reveal LAtomic_ValidPath(); reveal LAtomic_GetStateAfterPath(); match lpath "; var pr = new PathPrinter(lAtomic); foreach (var lpath in lAtomic.AtomicPaths) { if (pathMap.ContainsKey(lpath)) { var hpath = pathMap[lpath]; str += $@" case LAtomic_Path_{lpath.Name}(steps) => var states := LAtomic_GetPathStates_{lpath.Name}(ls, tid, steps); "; str += $"HAtomic_Path_{hpath.Name}(HAtomic_PathSteps_{hpath.Name}("; str += String.Join(", ", Enumerable.Range(0, lpath.NextRoutines.Count) .Where(i => nextRoutineMap.ContainsKey(lpath.NextRoutines[i])) .Select(i => $"ConvertStep_LH(states.s{i}, steps.step{i}, tid)")); str += "))\n"; } else { str += $"case LAtomic_Path_{lpath.Name}(_) => HAtomic_Path_Tau(HAtomic_PathSteps_Tau(H.Armada_Step_Tau()))\n"; } } str += "}"; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertAtomicPath_LH() { if (stateDependentConvertStep) { GenerateConvertAtomicPathStateDependent_LH(); return; } string str = @" function ConvertAtomicPath_LH(lpath: LAtomic_Path) : HAtomic_Path { match lpath "; foreach (var lpath in lAtomic.AtomicPaths) { if (pathMap.ContainsKey(lpath) && pathMap[lpath] != null) { var hpath = pathMap[lpath]; str += $"case LAtomic_Path_{lpath.Name}(steps) => HAtomic_Path_{hpath.Name}(HAtomic_PathSteps_{hpath.Name}("; str += String.Join(", ", Enumerable.Range(0, lpath.NextRoutines.Count) .Where(i => nextRoutineMap.ContainsKey(lpath.NextRoutines[i])) .Select(i => $"ConvertStep_LH(steps.step{i})")); str += "))\n"; } else { str += $"case LAtomic_Path_{lpath.Name}(_) => HAtomic_Path_Tau(HAtomic_PathSteps_Tau(H.Armada_Step_Tau()))\n"; } } str += "}"; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertAtomicTraceEntry_LH() { string str; if (stateDependentConvertStep) { str = @" function ConvertAtomicTraceEntry_LH(ls: LPlusState, lentry: AtomicTraceEntry): AtomicTraceEntry requires AtomicValidStep(LAtomic_GetSpecFunctions(), ls, lentry) { GenericAtomicLiftTraceEntryStateDependent(LAtomic_GetSpecFunctions(), ls, lentry, ConvertAtomicPath_LH) } "; pgp.AddFunction(str, "convert"); } else { str = @" function ConvertAtomicTraceEntry_LH(lentry:AtomicTraceEntry) : AtomicTraceEntry { GenericAtomicLiftTraceEntry(lentry, ConvertAtomicPath_LH) } "; pgp.AddFunction(str, "convert"); } } //////////////////////////////////////////////////////////////////////// /// State refinement functions //////////////////////////////////////////////////////////////////////// protected virtual void GenerateConvertPC_HL(Dictionary reversePCMap) { var caseBodies = String.Concat(reversePCMap.Select(mapping => $"case {mapping.Key} => L.{mapping.Value}\n")); var fn = $@" function ConvertPC_HL(pc: H.Armada_PC) : L.Armada_PC {{ match pc {caseBodies} }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertStackVars_HL(string methodName) { var smst = pgp.symbolsHigh.GetMethodSymbolTable(methodName); var ps = smst.AllVariablesInOrder.Select(v => $"vs.{v.FieldName}"); var fn = $@" function ConvertStackVars_HL_{methodName}(vs: H.Armada_StackVars_{methodName}) : L.Armada_StackVars_{methodName} {{ L.Armada_StackVars_{methodName}({AH.CombineStringsWithCommas(ps)}) }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertStackFrame_HL() { var caseBodies = ""; foreach (var methodName in pgp.symbolsHigh.MethodNames) { GenerateConvertStackVars_HL(methodName); caseBodies += $"case Armada_StackFrame_{methodName}(vs) => L.Armada_StackFrame_{methodName}(ConvertStackVars_HL_{methodName}(vs))\n"; } var fn = $@" function ConvertStackFrame_HL(frame: H.Armada_StackFrame) : L.Armada_StackFrame {{ match frame {caseBodies} }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertGlobals_HL() { var g = pgp.symbolsHigh.Globals; var ps = pgp.symbolsHigh.Globals.VariableNames.Select(varName => $"globals.{g.Lookup(varName).FieldName}"); var fn = $@" function ConvertGlobals_HL(globals: H.Armada_Globals) : L.Armada_Globals {{ L.Armada_Globals({AH.CombineStringsWithCommas(ps)}) }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertGhosts_HL() { var ps = new List(); foreach (var varName in pgp.symbolsHigh.Globals.VariableNames) { var v = pgp.symbolsHigh.Globals.Lookup(varName); if (v is GlobalGhostArmadaVariable) { ps.Add($"ghosts.{v.FieldName}"); } } var fn = $@" function ConvertGhosts_HL(ghosts: H.Armada_Ghosts) : L.Armada_Ghosts {{ L.Armada_Ghosts({AH.CombineStringsWithCommas(ps)}) }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertAddrs_HL() { var ps = new List(); foreach (var varName in pgp.symbolsHigh.Globals.VariableNames) { var v = pgp.symbolsHigh.Globals.Lookup(varName); if (v is GlobalAddressableArmadaVariable) { ps.Add($"addrs.{v.FieldName}"); } } var fn = $@" function ConvertAddrs_HL(addrs: H.Armada_Addrs) : L.Armada_Addrs {{ L.Armada_Addrs({AH.CombineStringsWithCommas(ps)}) }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertGlobalStaticVar_HL() { var caseBodies = $"case Armada_GlobalStaticVarNone => L.Armada_GlobalStaticVarNone\n"; foreach (var varName in pgp.symbolsHigh.Globals.VariableNames) { var gv = pgp.symbolsHigh.Globals.Lookup(varName); if (gv is UnaddressableArmadaVariable && !gv.NoTSO()) { caseBodies += $"case Armada_GlobalStaticVar_{varName} => L.Armada_GlobalStaticVar_{varName}\n"; } } var fn = $@" function ConvertGlobalStaticVar_HL(v: H.Armada_GlobalStaticVar) : L.Armada_GlobalStaticVar {{ match v {caseBodies} }} "; pgp.AddFunction(fn, "convert"); } protected virtual void GenerateConvertSharedMemory_HL() { string str = @" function ConvertSharedMemory_HL(mem: H.Armada_SharedMemory) : L.Armada_SharedMemory { L.Armada_SharedMemory(mem.heap, ConvertGlobals_HL(mem.globals)) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertStoreBufferLocation_HL() { string str = @" function ConvertStoreBufferLocation_HL(loc:H.Armada_StoreBufferLocation) : L.Armada_StoreBufferLocation { match loc case Armada_StoreBufferLocation_Unaddressable(v, fields) => L.Armada_StoreBufferLocation_Unaddressable(ConvertGlobalStaticVar_HL(v), fields) case Armada_StoreBufferLocation_Addressable(p) => L.Armada_StoreBufferLocation_Addressable(p) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertStoreBufferEntry_HL() { string str = @" function ConvertStoreBufferEntry_HL(entry:H.Armada_StoreBufferEntry) : L.Armada_StoreBufferEntry { L.Armada_StoreBufferEntry(ConvertStoreBufferLocation_HL(entry.loc), entry.value, ConvertPC_HL(entry.pc)) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertStoreBuffer_HL() { string str = @" function ConvertStoreBuffer_HL(entries:seq) : seq { MapSeqToSeq(entries, ConvertStoreBufferEntry_HL) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertExtendedFrame_HL() { string str = @" function ConvertExtendedFrame_HL(eframe:H.Armada_ExtendedFrame) : L.Armada_ExtendedFrame { L.Armada_ExtendedFrame(ConvertPC_HL(eframe.return_pc), ConvertStackFrame_HL(eframe.frame), eframe.new_ptrs) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertStack_HL() { string str = @" function ConvertStack_HL(stack:seq) : seq { MapSeqToSeq(stack, ConvertExtendedFrame_HL) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertThread_HL() { string str = @" function ConvertThread_HL(t:H.Armada_Thread) : L.Armada_Thread { L.Armada_Thread(ConvertPC_HL(t.pc), ConvertStackFrame_HL(t.top), t.new_ptrs, ConvertStack_HL(t.stack), ConvertStoreBuffer_HL(t.storeBuffer)) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertThreads_HL() { string str = @" function ConvertThreads_HL(threads:map) : map { MapMapToMap(threads, ConvertThread_HL) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertTotalState_HL() { string str = @" function ConvertTotalState_HL(s:H.Armada_TotalState) : L.Armada_TotalState { L.Armada_TotalState(s.stop_reason, ConvertThreads_HL(s.threads), ConvertSharedMemory_HL(s.mem), ConvertGhosts_HL(s.ghosts), ConvertAddrs_HL(s.addrs), s.joinable_tids) } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateConvertConfig_HL() { string str = @" function ConvertConfig_HL(config:Armada_Config) : Armada_Config { config } "; pgp.AddFunction(str, "convert"); } protected virtual void GenerateStateAbstractionFunctions_HL(Dictionary reversePCMap) { GenerateConvertPC_HL(reversePCMap); GenerateConvertStackFrame_HL(); GenerateConvertGlobals_HL(); GenerateConvertGhosts_HL(); GenerateConvertAddrs_HL(); GenerateConvertGlobalStaticVar_HL(); GenerateConvertSharedMemory_HL(); GenerateConvertStoreBufferLocation_HL(); GenerateConvertStoreBufferEntry_HL(); GenerateConvertStoreBuffer_HL(); GenerateConvertExtendedFrame_HL(); GenerateConvertStack_HL(); GenerateConvertThread_HL(); GenerateConvertThreads_HL(); GenerateConvertTotalState_HL(); GenerateConvertConfig_HL(); } //////////////////////////////////////////////////////////////////////// /// Proof header //////////////////////////////////////////////////////////////////////// public void AddCommonHeaderElements(ProofFile proof) { proof.AddInclude(pgp.mLow.Name + ".dfy"); proof.AddInclude(pgp.mHigh.Name + ".dfy"); proof.AddInclude(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/refinement/AnnotatedBehavior.i.dfy"); if (pgp.symbolsLow.StructsModuleName != null) { proof.AddInclude($"{pgp.symbolsLow.StructsModuleName}.dfy"); } if (proof.IncludeImportedFiles) { foreach (var importedFile in importedFiles) { proof.AddInclude(importedFile); } } proof.AddImport(pgp.mLow.Name, "L"); proof.AddImport(pgp.mHigh.Name, "H"); proof.AddImport("AnnotatedBehaviorModule"); if (pgp.symbolsLow.StructsModuleName != null) { proof.AddImport(pgp.symbolsLow.StructsModuleName); } if (proof.IncludeImportedFiles) { foreach (var importedModule in importedModules) { proof.AddImport(importedModule); } } } protected void GenerateProofHeader() { GenerateSpecsFile(); GenerateRevelationLemmas(); pgp.proofFiles.AssociateProofGenerator(this); } protected void GenerateSpecsFile() { pgp.AddImport("util_collections_seqs_s", null, "specs"); pgp.AddTypeSynonym("type LState = L.Armada_TotalState", "specs"); pgp.AddTypeSynonym("type HState = H.Armada_TotalState", "specs"); pgp.AddTypeSynonym("type LSpec = AnnotatedBehaviorSpec>", "specs"); pgp.AddTypeSynonym("type HSpec = AnnotatedBehaviorSpec>", "specs"); pgp.AddTypeSynonym("type LPlusSpec = AnnotatedBehaviorSpec>", "specs"); string str; str = @" function GetLSpec() : LSpec { SpecFunctionsToAnnotatedSpec(L.Armada_GetSpecFunctions()) } "; pgp.AddFunction(str, "specs"); str = @" function GetHSpec() : HSpec { SpecFunctionsToAnnotatedSpec(H.Armada_GetSpecFunctions()) } "; pgp.AddFunction(str, "specs"); str = @" function GetLPlusSpec() : LPlusSpec { SpecFunctionsToAnnotatedSpec(LPlus_GetSpecFunctions()) } "; pgp.AddFunction(str, "specs"); str = $@" predicate LHStateRefinement(ls:LState, hs:HState) {{ {pgp.symbolsLow.RefinementConstraint} }} "; pgp.AddPredicate(str, "specs"); str = @" function GetLHRefinementRelation() : RefinementRelation { iset p:RefinementPair | LHStateRefinement(p.low, p.high) } "; pgp.AddFunction(str, "specs"); var auxiliaryParams = String.Concat(auxiliaries.Select(aux => $", {aux.FieldName}: {aux.TypeName}")); pgp.AddDatatype($"datatype LPlusState = LPlusState(s: LState, config: Armada_Config{auxiliaryParams})", "specs"); str = @" predicate LPlusHStateRefinement(lplus:LPlusState, hs:HState) { LHStateRefinement(lplus.s, hs) } "; pgp.AddPredicate(str, "specs"); str = @" function GetLPlusHRefinementRelation() : RefinementRelation { iset p:RefinementPair | LPlusHStateRefinement(p.low, p.high) } "; pgp.AddFunction(str, "specs"); str = @" predicate LPlus_Init(splus:LPlusState) { && L.Armada_InitConfig(splus.s, splus.config) "; foreach (var aux in auxiliaries) { str += $" && splus.{aux.FieldName} == {aux.InitName}(splus.s, splus.config)\n"; } str += "}\n"; pgp.AddPredicate(str, "specs"); str = @" predicate LPlus_ValidStep(s: LPlusState, step:L.Armada_Step, tid: Armada_ThreadHandle) { L.Armada_ValidStep(s.s, step, tid) } "; pgp.AddPredicate(str, "specs"); str = @" function MakeLPlusInitialState(s:LState) : (splus:LPlusState) requires s in L.Armada_Spec().init ensures splus.s == s ensures LPlus_Init(splus) { var config :| L.Armada_InitConfig(s, config); "; foreach (var aux in auxiliaries) { str += $" var field_{aux.FieldName} := {aux.InitName}(s, config);\n"; } str += " LPlusState(s, config"; foreach (var aux in auxiliaries) { str += $", field_{aux.FieldName}"; } str += ")\n"; str += "}\n"; pgp.AddFunction(str, "specs"); str = @" function LPlus_GetNextState(splus: LPlusState, step: L.Armada_Step, tid: Armada_ThreadHandle) : (splus': LPlusState) { var s' := L.Armada_GetNextState(splus.s, step, tid); "; foreach (var aux in auxiliaries) { str += $" var field_{aux.FieldName} := {aux.NextName}(splus, s', step, tid);\n"; } str += "LPlusState(s', splus.config"; foreach (var aux in auxiliaries) { str += $", field_{aux.FieldName}"; } str += ")\n"; str += "}\n"; pgp.AddFunction(str, "specs"); str = @" function LPlus_GetSpecFunctions() : Armada_SpecFunctions { Armada_SpecFunctions(LPlus_Init, LPlus_ValidStep, LPlus_GetNextState, (step: L.Armada_Step) => step.Armada_Step_Tau?, (s: LPlusState) => s.s.stop_reason.Armada_NotStopped?, (s: LPlusState, tid: Armada_ThreadHandle) => if tid in s.s.threads then Some(s.s.threads[tid].pc) else None, L.Armada_IsNonyieldingPC) } "; pgp.AddFunction(str, "specs"); str = @" function ConvertTotalState_LPlusL(lps: LPlusState) : LState { lps.s } "; pgp.AddFunction(str, "PlusLemmas"); str = @" lemma lemma_EstablishRequirementsForLSpecRefinesLPlusSpec() ensures RequirementsForSpecRefinesPlusSpec(L.Armada_GetSpecFunctions(), LPlus_GetSpecFunctions(), ConvertTotalState_LPlusL) { var lasf := L.Armada_GetSpecFunctions(); var hasf := LPlus_GetSpecFunctions(); var convert := ConvertTotalState_LPlusL; forall ls | lasf.init(ls) ensures exists hs :: hasf.init(hs) && ls == convert(hs) { var hs := MakeLPlusInitialState(ls); assert hasf.init(hs) && ls == convert(hs); } } "; pgp.AddLemma(str, "PlusLemmas"); str = @" lemma lemma_LSpecRefinesLPlusSpec() returns (refinement_relation:RefinementRelation) ensures SpecRefinesSpec(Armada_SpecFunctionsToSpec(L.Armada_GetSpecFunctions()), Armada_SpecFunctionsToSpec(LPlus_GetSpecFunctions()), refinement_relation) ensures refinement_relation == iset ls: LState, lps: LPlusState | ls == lps.s :: RefinementPair(ls, lps) { lemma_EstablishRequirementsForLSpecRefinesLPlusSpec(); refinement_relation := lemma_SpecRefinesPlusSpec(L.Armada_GetSpecFunctions(), LPlus_GetSpecFunctions(), ConvertTotalState_LPlusL); } "; pgp.AddLemma(str, "PlusLemmas"); } //////////////////////////////////////////////////////////////////////// /// Lemmas about region invariant //////////////////////////////////////////////////////////////////////// protected virtual List GenerateAddressableInvariant_Global() { pgp.AuxiliaryProof("specs").AddInclude(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/util/collections/maps.i.dfy"); pgp.AuxiliaryProof("specs").AddImport("util_collections_maps_i"); var abstractAddresses = new List(); var preds = new List(); foreach (var globalVarName in pgp.symbolsLow.Globals.VariableNames) { var globalVar = pgp.symbolsLow.Globals.Lookup(globalVarName); if (globalVar is GlobalAddressableArmadaVariable) { string globalAddress = $"s.s.addrs.{globalVarName}"; preds.Add(AH.GetInvocationOfValidPointer("s.s.mem.heap", $"s.s.addrs.{globalVarName}", globalVar.ty)); var descendants = AH.GetInvocationOfDescendants("s.s.mem.heap", $"s.s.addrs.{globalVarName}", globalVar.ty); preds.Add($@" forall p {{:trigger Armada_TriggerPointer(p)}} :: Armada_TriggerPointer(p) in {descendants} ==> p in s.addr_map && s.addr_map[p] == GlobalAbstractAddress_{globalVarName} "); abstractAddresses.Add($"GlobalAbstractAddress_{globalVarName}"); } } var str = $@" predicate AddressableInvariant_Globals(s: LPlusState) {{ {AH.CombineStringsWithAnd(preds)} }} "; pgp.AddPredicate(str, "defs"); return abstractAddresses; } protected virtual List GenerateAddressableInvariant_StackFrame(string methodName) { var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(methodName); var abstractAddresses = new List(); var preds = new List(); foreach (var localVar in methodSymbols.AllVariablesInOrder.Where(v => v is MethodStackFrameAddressableLocalArmadaVariable)) { string varStackFrameFieldName = methodSymbols.GetVariableStackFrameFieldName(localVar.name); preds.Add(AH.GetInvocationOfValidPointer("heap", $"frame_vars.{varStackFrameFieldName}", localVar.ty)); var descendants = AH.GetInvocationOfDescendants("heap", $"frame_vars.{varStackFrameFieldName}", localVar.ty); preds.Add($@" forall p {{:trigger Armada_TriggerPointer(p)}} :: Armada_TriggerPointer(p) in ({descendants}) ==> p in addr_map && addr_map[p] == LocalAbstractAddress_{AH.ExpandUnderscores(methodName)}_{AH.ExpandUnderscores(varStackFrameFieldName)}(tid, h) "); abstractAddresses.Add($"LocalAbstractAddress_{AH.ExpandUnderscores(methodName)}_{AH.ExpandUnderscores(varStackFrameFieldName)}(tid:Armada_ThreadHandle, h:int)"); } var str = $@" predicate AddressableInvariant_StackFrame_{methodName}(addr_map:map, frame: L.Armada_StackFrame, tid: Armada_ThreadHandle, h: int, heap: Armada_Heap) requires frame.Armada_StackFrame_{methodName}? {{ var frame_vars := frame.{methodName}; {AH.CombineStringsWithAnd(preds)} }} "; pgp.AddPredicate(str, "defs"); return abstractAddresses; } protected virtual void GenerateAddressableMapInit() { var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable("main"); MapBuilder mapBuilder = new MapBuilder("m"); foreach (var localVar in methodSymbols.AllVariablesInOrder.Where(v => v is MethodStackFrameAddressableLocalArmadaVariable)) { string varStackFrameFieldName = methodSymbols.GetVariableStackFrameFieldName(localVar.name); var localAddress = $"s.threads[config.tid_init].top.main.{varStackFrameFieldName}"; var descendants = AH.GetInvocationOfDescendants("s.mem.heap", localAddress, localVar.ty); mapBuilder.Add(descendants, $"LocalAbstractAddress_main_{AH.ExpandUnderscores(varStackFrameFieldName)}(config.tid_init, 0)"); } foreach (var globalVarName in pgp.symbolsLow.Globals.VariableNames) { var globalVar = pgp.symbolsLow.Globals.Lookup(globalVarName); if (globalVar is GlobalAddressableArmadaVariable) { string globalAddress = $"s.addrs.{globalVarName}"; var descendants = AH.GetInvocationOfDescendants("s.mem.heap", globalAddress, globalVar.ty); mapBuilder.Add(descendants, $"GlobalAbstractAddress_{globalVarName}"); } } string str = $@" function AddrMapInit(s: LState, config: Armada_Config): map requires L.Armada_InitConfig(s, config) {{ {mapBuilder.Extract()} }} "; pgp.AddFunction(str, "specs"); } protected virtual string GenerateAddressableMapNextCase_Call(NextRoutine nextRoutine) { var armadaCallStmt = (ArmadaCallStatement)nextRoutine.armadaStatement; var methodName = armadaCallStmt.CalleeName; var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(methodName); MapBuilder mapBuilder = new MapBuilder("m", "splus.addr_map"); foreach (var localVar in methodSymbols.AllVariablesInOrder.Where(v => v is MethodStackFrameAddressableLocalArmadaVariable)) { string varStackFrameFieldName = methodSymbols.GetVariableStackFrameFieldName(localVar.name); var localAddress = $"new_frame.{varStackFrameFieldName}"; var descendants = AH.GetInvocationOfDescendants("s'.mem.heap", localAddress, localVar.ty); mapBuilder.Add(descendants, $"LocalAbstractAddress_{AH.ExpandUnderscores(methodName)}_{AH.ExpandUnderscores(varStackFrameFieldName)}(tid, |s'.threads[tid].stack|)"); } var pr = new ModuleStepPrinter("L"); pr.State = "splus.s"; string str = $@" { pr.GetOpenStepInvocation(nextRoutine) } if s'.stop_reason.Armada_NotStopped? then var new_frame := s'.threads[tid].top.{methodName}; {mapBuilder.Extract()} else splus.addr_map "; return str; } protected virtual string GenerateAddressableMapNextCase_CreateThread(NextRoutine nextRoutine) { var armadaCreateThreadStmt = (ArmadaCreateThreadStatement)nextRoutine.armadaStatement; var stmt = (UpdateStmt)armadaCreateThreadStmt.Stmt; var rhs = (CreateThreadRhs)stmt.Rhss[0]; var methodName = rhs.MethodName.val; var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(methodName); MapBuilder mapBuilder = new MapBuilder("m", "splus.addr_map"); foreach (var localVar in methodSymbols.AllVariablesInOrder.Where(v => v is MethodStackFrameAddressableLocalArmadaVariable)) { string varStackFrameFieldName = methodSymbols.GetVariableStackFrameFieldName(localVar.name); var localAddress = $"new_frame.{varStackFrameFieldName}"; var descendants = AH.GetInvocationOfDescendants("s'.mem.heap", localAddress, localVar.ty); mapBuilder.Add(descendants, $"LocalAbstractAddress_{AH.ExpandUnderscores(methodName)}_{AH.ExpandUnderscores(varStackFrameFieldName)}(newtid, 0)"); } var pr = new ModuleStepPrinter("L"); pr.State = "splus.s"; string str = $@" { pr.GetOpenStepInvocation(nextRoutine) } if s'.stop_reason.Armada_NotStopped? then var newtid := params.newtid; var new_frame := s'.threads[newtid].top.{methodName}; {mapBuilder.Extract()} else splus.addr_map "; return str; } protected virtual string GenerateAddressableMapNextCase(NextRoutine nextRoutine) { string caseBody = ""; if (nextRoutine.Stopping) { caseBody = "splus.addr_map"; } else if (nextRoutine.nextType == NextType.Call) { caseBody = GenerateAddressableMapNextCase_Call(nextRoutine); } else if (nextRoutine.nextType == NextType.CreateThread) { caseBody = GenerateAddressableMapNextCase_CreateThread(nextRoutine); } else { caseBody = "splus.addr_map"; } var pr = new ModuleStepPrinter("L"); return $"{pr.CaseEntry(nextRoutine)} => {caseBody}"; } protected virtual void GenerateAddressableMapNext() { var caseStrings = new List(); foreach (var nextRoutine in pgp.symbolsLow.NextRoutines) { caseStrings.Add(GenerateAddressableMapNextCase(nextRoutine)); } string str = $@" function AddrMapNext(splus: LPlusState, s': LState, step: L.Armada_Step, tid: Armada_ThreadHandle) : map requires s' == L.Armada_GetNextState(splus.s, step, tid) {{ if L.Armada_ValidStep(splus.s, step, tid) then match step {{ {string.Join("\n", caseStrings)} }} else splus.addr_map }} "; pgp.AddFunction(str, "specs"); } protected virtual void GenerateAddressableMapAux(List abstractAddresses) { string datatypeDecl = "datatype AbstractAddress = " + string.Join(" | ", abstractAddresses); if (abstractAddresses.Count == 0) { datatypeDecl = "datatype AbstractAddress = AbstractAddress_None"; } pgp.AddDatatype(datatypeDecl, "specs"); GenerateAddressableMapNext(); GenerateAddressableMapInit(); var auxiliaryInfo = new AuxiliaryInfo("addr_map", "map", "AddrMapInit", "AddrMapNext"); auxiliaries.Add(auxiliaryInfo); } protected virtual void GenerateAddressableInvariant() { GenerateValidStackFramePredicate(); string body = $@""; List abstractAddresses = new List(); abstractAddresses = GenerateAddressableInvariant_Global(); foreach (var methodName in pgp.symbolsLow.MethodNames) { abstractAddresses.AddRange(GenerateAddressableInvariant_StackFrame(methodName)); body += $@" && (forall tid :: tid in s.s.threads && s.s.threads[tid].top.Armada_StackFrame_{methodName}? ==> var frame := s.s.threads[tid].top; var heap := s.s.mem.heap; AddressableInvariant_StackFrame_{methodName}(s.addr_map, frame, tid, |s.s.threads[tid].stack|, heap) ) && (forall tid, h :: tid in s.s.threads && 0 <= h < |s.s.threads[tid].stack| && s.s.threads[tid].stack[h].frame.Armada_StackFrame_{methodName}? ==> var frame := s.s.threads[tid].stack[h].frame; var heap := s.s.mem.heap; AddressableInvariant_StackFrame_{methodName}(s.addr_map, frame, tid, |s.s.threads[tid].stack| - 1 - h, heap) ) "; } GenerateAddressableMapAux(abstractAddresses); string predicateDecl = $@" predicate AddressableInvariant(s: LPlusState) {{ {body} && AllValidStackFrames(s) && AddressableInvariant_Globals(s) && (forall p :: p in s.addr_map ==> (p in s.s.mem.heap.valid || p in s.s.mem.heap.freed) ) }} "; pgp.AddDefaultClassDecl((Predicate)AH.ParseTopLevelDecl(pgp.prog, "AddressableInvariant", predicateDecl), "defs"); AddInvariant(new InternalInvariantInfo("AddressableInvariant", "AddressableInvariant", new List())); } protected virtual void GenerateValidStackMethod(string methodName) { var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(methodName); var preds = new List(); var p_in_new_ptrs = new List(); foreach (var localVar in methodSymbols.AllVariablesInOrder.Where(v => v is MethodStackFrameAddressableLocalArmadaVariable)) { string varStackFrameFieldName = methodSymbols.GetVariableStackFrameFieldName(localVar.name); preds.Add(AH.GetInvocationOfValidPointer("heap", $"frame_vars.{varStackFrameFieldName}", localVar.ty)); preds.Add($@" heap.tree[frame_vars.{varStackFrameFieldName}].child_type.Armada_ChildTypeRoot? && heap.tree[frame_vars.{varStackFrameFieldName}].child_type.rt.Armada_RootTypeStack?"); var descendants = AH.GetInvocationOfDescendants("heap", $"frame_vars.{varStackFrameFieldName}", localVar.ty); p_in_new_ptrs.Add($"p in ({descendants})"); } if (p_in_new_ptrs.Count > 0) { preds.Add($@" forall p {{:trigger Armada_TriggerPointer(p)}} :: (Armada_TriggerPointer(p) in new_ptrs) <==> ({AH.CombineStringsWithOr(p_in_new_ptrs)}) "); } else { preds.Add("|new_ptrs| == 0"); } string predicateDecl = $@" predicate ValidStackFrame_{methodName}(frame: L.Armada_StackFrame, heap: Armada_Heap, new_ptrs: set) requires frame.Armada_StackFrame_{methodName}? {{ var frame_vars := frame.{methodName}; {AH.CombineStringsWithAnd(preds)} }} "; pgp.AddPredicate(predicateDecl, "defs"); } protected virtual void GenerateValidStackFramePredicate() { List caseBodies = new List(); foreach (var methodName in pgp.symbolsLow.MethodNames) { GenerateValidStackMethod(methodName); caseBodies.Add($@" forall tid :: tid in s.s.threads && s.s.threads[tid].top.Armada_StackFrame_{methodName}? ==> var t := s.s.threads[tid]; ValidStackFrame_{methodName}(t.top, s.s.mem.heap, t.new_ptrs) "); caseBodies.Add($@" forall tid, h :: tid in s.s.threads && 0 <= h < |s.s.threads[tid].stack| && s.s.threads[tid].stack[h].frame.Armada_StackFrame_{methodName}? ==> ValidStackFrame_{methodName}(s.s.threads[tid].stack[h].frame, s.s.mem.heap, s.s.threads[tid].stack[h].new_ptrs) "); } string str = $@" predicate AllValidStackFrames(s: LPlusState) {{ {AH.CombineStringsWithAnd(caseBodies)} }} "; pgp.AddPredicate(str, "defs"); } public class RegionInfo { public Dictionary globalPtrVarRegionMap; public Dictionary globalAddrVarRegionMap; public Dictionary> methodToPtrVarRegionTable; public Dictionary> methodToAddrVarRegionTable; public List regionIds; public RegionInfo() { globalPtrVarRegionMap = new Dictionary(); globalAddrVarRegionMap = new Dictionary(); methodToPtrVarRegionTable = new Dictionary>(); methodToAddrVarRegionTable = new Dictionary>(); regionIds = new List(); } public string GetGlobalRegionId(string varName) { string regionId = null; if (varName.Length > 0 && varName[0] == '&') { globalAddrVarRegionMap.TryGetValue(varName.Substring(1), out regionId); } else { globalPtrVarRegionMap.TryGetValue(varName, out regionId); } return regionId; } public string GetLocalRegionId(string methodName, string varName) { string regionId = null; Dictionary methodAddrVarRegionTable = null; if (methodToAddrVarRegionTable.TryGetValue(methodName, out methodAddrVarRegionTable)) { if (varName.Length > 0 && varName[0] == '&') { methodToAddrVarRegionTable[methodName].TryGetValue(varName.Substring(1), out regionId); } else { methodToPtrVarRegionTable[methodName].TryGetValue(varName, out regionId); } } return regionId; } public string GetRegionId(string methodName, string varName) { string regionId = GetLocalRegionId(methodName, varName); if (regionId == null) { regionId = GetGlobalRegionId(varName); } return regionId; } } // Initial region of &globalVariable is represented as `r_&globalVariableName` // Initial region of globalPointer is represented as `r_globalPointer` // Initial region of methodName's &localVariable is represented as `r_methodName.&localVariable` // Initial region of methodName's localPointer is represented as `r_methodName.localVariable` // Initial region of methodName's Malloc/Calloc statement at PC=p is represented `alloc_r_methodName[p]` private RegionInfo GenerateInitialRegionInfo() { RegionInfo r = new RegionInfo(); foreach (var globalVarName in pgp.symbolsLow.Globals.VariableNames) { var globalVar = pgp.symbolsLow.Globals.Lookup(globalVarName); // Global addressable variables if (globalVar is GlobalAddressableArmadaVariable) { r.globalAddrVarRegionMap[globalVarName] = $"rga_{globalVarName}"; r.regionIds.Add($"rga_{globalVarName}"); } if (globalVar.ty is PointerType) { r.globalPtrVarRegionMap[globalVarName] = $"rgp_{globalVarName}"; r.regionIds.Add($"rgp_{globalVarName}"); } } foreach (var methodName in pgp.symbolsLow.MethodNames) { var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(methodName); Dictionary ptrRegionTable = new Dictionary(); Dictionary addrRegionTable = new Dictionary(); foreach (var localVar in methodSymbols.AllVariables) { var localVarName = localVar.name; if (localVar is MethodStackFrameAddressableLocalArmadaVariable) { addrRegionTable[localVarName] = $"rap_{AH.ExpandUnderscores(methodName)}_{AH.ExpandUnderscores(localVarName)}"; r.regionIds.Add($"rap_{AH.ExpandUnderscores(methodName)}_{AH.ExpandUnderscores(localVarName)}"); } if (localVar.ty is PointerType) { ptrRegionTable[localVarName] = $"rlp_{AH.ExpandUnderscores(methodName)}_{AH.ExpandUnderscores(localVarName)}"; r.regionIds.Add($"rlp_{AH.ExpandUnderscores(methodName)}_{AH.ExpandUnderscores(localVarName)}"); } r.methodToAddrVarRegionTable[methodName] = addrRegionTable; r.methodToPtrVarRegionTable[methodName] = ptrRegionTable; } } // Console.WriteLine(string.Join(" ", r.regionIds)); return r; } protected virtual List GetPossibleResultantVariables(Expression expr) { var vars = new List(); if (expr is NameSegment) // return `variable` { var e = (NameSegment)expr; vars.Add(e.Name); } if (expr is UnaryOpExpr) { var e = (UnaryOpExpr)expr; if (e.Op == UnaryOpExpr.Opcode.AddressOf) { vars.Add(Printer.ExprToString(e)); // Want to return `&variable` } } return vars; } protected virtual RegionInfo GenerateVariableRegionMap() { RegionInfo r = GenerateInitialRegionInfo(); Dictionary regionIdToIndex = new Dictionary(); List indexToRegionId = new List(); int numRegions = 0; foreach (var regionId in r.regionIds) { regionIdToIndex[regionId] = numRegions; indexToRegionId.Add(regionId); numRegions += 1; } DisjointSet d = new DisjointSet(numRegions); // TODO: Merge for al initializations of pointer variables // Merge for all statements that force regions to be merged foreach (var nextRoutine in pgp.symbolsLow.NextRoutines) { if (nextRoutine.Stopping) { continue; } if (nextRoutine.nextType == NextType.Update) { var updateStmt = (UpdateStmt)nextRoutine.stmt; var lhss = updateStmt.Lhss; var rhss = updateStmt.Rhss; var methodName = nextRoutine.method.Name; for (int i = 0; i < lhss.Count; ++i) { var lhs = lhss[i]; var rhs_Rhs = rhss[i]; var lhsVarName = Printer.ExprToString(lhs); var eRhs = (ExprRhs)rhs_Rhs; var rhs = eRhs.Expr; List rhsVarNames = GetPossibleResultantVariables(rhs); var r1 = r.GetRegionId(methodName, lhsVarName); if (r1 != null) { foreach (var rhsVarName in rhsVarNames) { // Console.WriteLine($"Merging {lhsVarName} with {rhsVarName}"); var r2 = r.GetRegionId(methodName, rhsVarName); // Console.WriteLine($"Merging region {r1} with {r2}"); if (r2 != null) { d.Join(regionIdToIndex[r1], regionIdToIndex[r2]); } else { // For whatever reason, we don't know the region of the RHS, so we can't reason about the LHS either. //Console.WriteLine($"Region can't be specified for {lhsVarName} because of {Printer.ExprToString(rhs)}"); } } } else { //Console.WriteLine(lhsVarName); } } } else if (nextRoutine.nextType == NextType.Call) { var methodName = nextRoutine.method.Name; var armadaCallStmt = (ArmadaCallStatement)nextRoutine.armadaStatement; var stmt = (UpdateStmt)armadaCallStmt.Stmt; var ex = (ExprRhs)stmt.Rhss[0]; var suffix = (ApplySuffix)ex.Expr; var args = suffix.Args; var calledMethodName = armadaCallStmt.CalleeName; var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(calledMethodName); var methodInputVars = methodSymbols.InputVariableNames.ToList(); for (int i = 0; i < methodInputVars.Count; i++) { var inputVar = methodSymbols.LookupVariable(methodInputVars[i]); if (inputVar.ty is PointerType) { var lhsVarName = methodInputVars[i]; var arg = args[i]; List rhsVarNames = GetPossibleResultantVariables(arg); foreach (var rhsVarName in rhsVarNames) { // Console.WriteLine($"Merging {lhsVarName} with {rhsVarName}"); var r1 = r.GetRegionId(calledMethodName, lhsVarName); var r2 = r.GetRegionId(methodName, rhsVarName); //Console.WriteLine($"Method varName: {methodName} {rhsVarName}"); //Console.WriteLine($"Merging region {r1} with {r2}"); if (r2 != null) { d.Join(regionIdToIndex[r1], regionIdToIndex[r2]); } else { // For whatever reason, we don't know the region of the RHS, so we can't reason about the LHS either. //Console.WriteLine($"Region can't be specified for {lhsVarName} because of {rhsVarName}"); } } } } } else if (nextRoutine.nextType == NextType.CreateThread) { var methodName = nextRoutine.method.Name; var armadaCallStmt = (ArmadaCreateThreadStatement)nextRoutine.armadaStatement; var stmt = (UpdateStmt)armadaCallStmt.Stmt; var rhs = (CreateThreadRhs)stmt.Rhss[0]; var calledMethodName = rhs.MethodName.val; var args = rhs.Args; var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(calledMethodName); var methodInputVars = methodSymbols.InputVariableNames.ToList(); for (int i = 0; i < methodInputVars.Count; i++) { var inputVar = methodSymbols.LookupVariable(methodInputVars[i]); if (inputVar.ty is PointerType) { var lhsVarName = methodInputVars[i]; var arg = args[i]; List rhsVarNames = GetPossibleResultantVariables(arg); foreach (var rhsVarName in rhsVarNames) { // Console.WriteLine($"Merging {lhsVarName} with {rhsVarName}"); var r1 = r.GetRegionId(calledMethodName, lhsVarName); var r2 = r.GetRegionId(methodName, rhsVarName); // Console.WriteLine($"Merging region {r1} with {r2}"); d.Join(regionIdToIndex[r1], regionIdToIndex[r2]); } } } } } foreach (var k in r.globalAddrVarRegionMap.Keys.ToList()) { r.globalAddrVarRegionMap[k] = indexToRegionId[d.Find(regionIdToIndex[r.globalAddrVarRegionMap[k]])]; } foreach (var k in r.globalPtrVarRegionMap.Keys.ToList()) { r.globalPtrVarRegionMap[k] = indexToRegionId[d.Find(regionIdToIndex[r.globalPtrVarRegionMap[k]])]; } foreach (var k1 in r.methodToAddrVarRegionTable.Keys.ToList()) { foreach (var k2 in r.methodToAddrVarRegionTable[k1].Keys.ToList()) { r.methodToAddrVarRegionTable[k1][k2] = indexToRegionId[d.Find(regionIdToIndex[r.methodToAddrVarRegionTable[k1][k2]])]; } } foreach (var k1 in r.methodToPtrVarRegionTable.Keys.ToList()) { foreach (var k2 in r.methodToPtrVarRegionTable[k1].Keys.ToList()) { r.methodToPtrVarRegionTable[k1][k2] = indexToRegionId[d.Find(regionIdToIndex[r.methodToPtrVarRegionTable[k1][k2]])]; } } HashSet possibleRegionIds = new HashSet(); foreach(var pair in regionIdToIndex) { possibleRegionIds.Add(indexToRegionId[d.Find(pair.Value)]); } string str = "datatype RegionId = " + String.Join(" | ", possibleRegionIds); pgp.AddDatatype(str, "specs"); return r; } protected virtual string GenerateRegionMapNextCase_CreateThread(NextRoutine nextRoutine, RegionInfo regionInfo) { var armadaCreateThreadStmt = (ArmadaCreateThreadStatement)nextRoutine.armadaStatement; var stmt = (UpdateStmt)armadaCreateThreadStmt.Stmt; var rhs = (CreateThreadRhs)stmt.Rhss[0]; var methodName = rhs.MethodName.val; var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(methodName); List mapUpdates = new List(); foreach (var localVar in methodSymbols.AllVariablesInOrder.Where(v => v is MethodStackFrameAddressableLocalArmadaVariable)) { string regionIdStr = regionInfo.GetRegionId(methodName, "&" + localVar.name); mapUpdates.Add($"[params.newframe_{localVar.name} := {regionIdStr}]"); } string mapUpdateBody = string.Join("", mapUpdates); var pr = new LPlusStepPrinter(); string str = $@" { pr.GetOpenStepInvocation(nextRoutine) } s.region_map{mapUpdateBody} "; return str; } protected virtual string GenerateRegionMapNextCase_Call(NextRoutine nextRoutine, RegionInfo regionInfo) { var armadaCallStmt = (ArmadaCallStatement)nextRoutine.armadaStatement; var methodName = armadaCallStmt.CalleeName; var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(methodName); List mapUpdates = new List(); foreach (var localVar in methodSymbols.AllVariablesInOrder.Where(v => v is MethodStackFrameAddressableLocalArmadaVariable)) { string regionIdStr = regionInfo.GetRegionId(methodName, "&" + localVar.name); mapUpdates.Add($"[params.newframe_{localVar.name} := {regionIdStr}]"); } string mapUpdateBody = string.Join("", mapUpdates); var pr = new LPlusStepPrinter(); string str = $@" { pr.GetOpenStepInvocation(nextRoutine) } s.region_map{mapUpdateBody} "; return str; } protected virtual string GenerateRegionMapNextCase(NextRoutine nextRoutine, RegionInfo regionInfo) { string str = ""; // $"case Armada_Step_{nextRoutine.NameSuffix} => "; if (nextRoutine.Stopping) { str = "s.region_map"; } else if (nextRoutine.nextType == NextType.MallocSuccess) { var updateStmt = (UpdateStmt)nextRoutine.armadaStatement.Stmt; // Get the variable from the LHS and use that to determine the case string methodName = nextRoutine.method.Name; string varName = Printer.ExprToString(updateStmt.Lhss[0]); var regionId = regionInfo.GetRegionId(methodName, varName); var pr = new LPlusStepPrinter(); str = $@" { pr.GetOpenStepInvocation(nextRoutine) } s.region_map[params.new_ptr := {regionId}] "; } else if (nextRoutine.nextType == NextType.Call) { str = GenerateRegionMapNextCase_Call(nextRoutine, regionInfo); } else if (nextRoutine.nextType == NextType.CreateThread) { str = GenerateRegionMapNextCase_CreateThread(nextRoutine, regionInfo); } else { str = "s.region_map"; } var bvs = nextRoutine.HasFormals ? $"params: L.Armada_StepParams_{nextRoutine.NameSuffix}" : ""; return $"case Armada_Step_{nextRoutine.NameSuffix}({bvs}) => {str}\n"; } protected virtual void GenerateRegionMapNext(RegionInfo regionInfo) { var caseBodies = String.Concat(pgp.symbolsLow.NextRoutines.Select(nextRoutine => GenerateRegionMapNextCase(nextRoutine, regionInfo))); var fn = $@" function RegionMapNext(s: LPlusState, ls': LState, step: L.Armada_Step, tid: Armada_ThreadHandle) : map {{ match step {caseBodies} }} "; pgp.AddFunction(fn, "specs"); } protected virtual void GenerateRegionMap(IEnumerable regionIds) { var auxiliaryInfo = new AuxiliaryInfo("region_map", "map", "RegionMapInit", "RegionMapNext"); auxiliaries.Add(auxiliaryInfo); } protected virtual void GenerateRegionInvariant() { GenerateHeapInvariant(); RegionInfo regionInfo = GenerateVariableRegionMap(); GenerateAddressableInvariant(); // OtherWay lemmas are needed for addressable pointer variables GenerateAppendStoreBufferOtherWay(); var regionIds = new HashSet(); var preds = new List(); List regionMapInitUpdates = new List(); foreach (var globalVarName in pgp.symbolsLow.Globals.VariableNames) { var globalVar = pgp.symbolsLow.Globals.Lookup(globalVarName); // Global addressable variables if (globalVar is GlobalAddressableArmadaVariable) { string regionNameStr = regionInfo.GetGlobalRegionId("&" + globalVarName); preds.Add($"addrs.{globalVarName} in region_map"); preds.Add($"region_map[addrs.{globalVarName}] == {regionNameStr}"); regionMapInitUpdates.Add($"s.addrs.{globalVarName} := {regionNameStr}"); } // Deal with global pointer variables if (globalVar.ty is PointerType) { string regionNameStr = regionInfo.GetGlobalRegionId(globalVarName); // Global addressable pointer variable if (globalVar is GlobalAddressableArmadaVariable) { // Need to ensure that the address stored in this pointer variable is in the correct region var pointerValue = AH.GetInvocationOfDereferencePointer("mem.heap", $"addrs.{globalVarName}", globalVar.ty); var validPointer = AH.GetInvocationOfValidPointer("mem.heap", $"addrs.{globalVarName}", globalVar.ty); preds.Add(validPointer); preds.Add($"({pointerValue}) == 0 || (({pointerValue}) in region_map && region_map[{pointerValue}] == {regionNameStr})"); } // Global non-addressable pointer variable else { preds.Add($"mem.globals.{globalVarName} == 0 || (mem.globals.{globalVarName} in region_map && region_map[mem.globals.{globalVarName}] == {regionNameStr})"); } } } // Deal with addresses of addressable stack variables foreach (var methodName in pgp.symbolsLow.MethodNames) { var methodSymbols = pgp.symbolsLow.GetMethodSymbolTable(methodName); foreach (var localVar in methodSymbols.AllVariables) { var localVarName = localVar.name; if (localVar is MethodStackFrameAddressableLocalArmadaVariable) { string varStackFrameFieldName = methodSymbols.GetVariableStackFrameFieldName(localVar.name); string regionIdStr = regionInfo.GetLocalRegionId(methodName, "&" + localVarName); if (methodName == "main") { regionMapInitUpdates.Add($"s.threads[config.tid_init].top.{methodName}.{varStackFrameFieldName} := {regionIdStr}"); } preds.Add($@" forall tid, extended_frame :: tid in threads && extended_frame in threads[tid].stack && extended_frame.frame.Armada_StackFrame_{methodName}? ==> var stack_frame := extended_frame.frame.{methodName}; (stack_frame.{varStackFrameFieldName} in region_map && region_map[stack_frame.{varStackFrameFieldName}] == {regionIdStr}) "); preds.Add($@" forall tid :: tid in threads && threads[tid].top.Armada_StackFrame_{methodName}? ==> var stack_frame := threads[tid].top.{methodName}; (stack_frame.{varStackFrameFieldName} in region_map && region_map[stack_frame.{varStackFrameFieldName}] == {regionIdStr}) "); } if (localVar.ty is PointerType) { // If the variable is a pointer type string regionIdStr = regionInfo.GetLocalRegionId(methodName, localVarName); string varStackFrameFieldName = methodSymbols.GetVariableStackFrameFieldName(localVar.name); var validPointerStr = AH.GetInvocationOfValidPointer("mem.heap", $"frame_vars.{varStackFrameFieldName}", localVar.ty); var pointerValueStr = AH.GetInvocationOfDereferencePointer("mem.heap", $"frame_vars.{varStackFrameFieldName}", localVar.ty); // Local addressable pointer if (localVar is MethodStackFrameAddressableLocalArmadaVariable) { preds.Add($@" forall tid :: tid in threads && threads[tid].top.Armada_StackFrame_{methodName}? ==> var frame_vars := threads[tid].top.{methodName}; {validPointerStr} && ({pointerValueStr} == 0 || ({pointerValueStr} in region_map && region_map[{pointerValueStr}] == {regionIdStr} ) ) "); preds.Add($@" forall tid, extended_frame :: tid in threads && extended_frame in threads[tid].stack && extended_frame.frame.Armada_StackFrame_{methodName}? ==> var frame_vars := extended_frame.frame.{methodName}; {validPointerStr} && ({pointerValueStr} == 0 || ({pointerValueStr} in region_map && region_map[{pointerValueStr}] == {regionIdStr} ) ) "); } // Local non-addressable pointer else { preds.Add($@" forall tid, extended_frame :: tid in threads && extended_frame in threads[tid].stack && extended_frame.frame.Armada_StackFrame_{methodName}? ==> var stack_frame_vars := extended_frame.frame.{methodName}; stack_frame_vars.{varStackFrameFieldName} == 0 || (stack_frame_vars.{varStackFrameFieldName} in region_map && region_map[stack_frame_vars.{varStackFrameFieldName}] == {regionIdStr}) "); preds.Add($@" forall tid :: tid in threads && threads[tid].top.Armada_StackFrame_{methodName}? ==> var stack_frame_vars := threads[tid].top.{methodName}; stack_frame_vars.{varStackFrameFieldName} == 0 || (stack_frame_vars.{varStackFrameFieldName} in region_map && region_map[stack_frame_vars.{varStackFrameFieldName}] == {regionIdStr}) "); } } } } var str = $@" function RegionMapInit(s: LState, config: Armada_Config) : map requires L.Armada_InitConfig(s, config) {{ map[{AH.CombineStringsWithCommas(regionMapInitUpdates)}] }} "; pgp.AddFunction(str, "specs"); GenerateRegionMapNext(regionInfo); str = $@" predicate RegionInvariant_mem(threads: map, addrs: L.Armada_Addrs, mem: L.Armada_SharedMemory, region_map: map) {{ {AH.CombineStringsWithAnd(preds)} }} "; pgp.AddPredicate(str, "defs"); str = @" predicate RegionMapOnlyContainsValidOrFreedPointers(s: LPlusState) { forall k :: k in s.region_map ==> k in s.s.mem.heap.valid || k in s.s.mem.heap.freed } "; pgp.AddPredicate(str, "defs"); str = $@" predicate RegionInvariant(s: LPlusState) {{ RegionMapOnlyContainsValidOrFreedPointers(s) && RegionInvariant_mem(s.s.threads, s.s.addrs, s.s.mem, s.region_map) && (forall tid, storeBufferEntry, threads, addrs, mem, region_map :: tid in s.s.threads && storeBufferEntry in s.s.threads[tid].storeBuffer && (forall p :: p in s.region_map ==> p in region_map && region_map[p] == s.region_map[p]) && RegionInvariant_mem(threads, addrs, mem, region_map) ==> RegionInvariant_mem(threads, addrs, L.Armada_ApplyStoreBufferEntry(mem, storeBufferEntry), region_map)) }} "; pgp.AddPredicate(str, "defs"); GenerateRegionMap(regionIds); AddInvariant(new InternalInvariantInfo("RegionInvariant", "RegionInvariant", new List(){"AddressableInvariant"}, "lemma_RegionInvariantOnGlobalViewAlwaysImpliesRegionInvariantOnLocalView();")); GenerateRegionInvariantHoldsOnLocalViewLemmas(); } protected virtual void GenerateRegionInvariantHoldsOnLocalViewLemmas() { string str = @" lemma lemma_RegionInvariantOnGlobalViewImpliesRegionInvariantOnLocalView(s_threads: map, s_addrs: L.Armada_Addrs, s_mem: L.Armada_SharedMemory, s_region_map: map, storeBuffer:seq) requires RegionInvariant_mem(s_threads, s_addrs, s_mem, s_region_map) requires forall storeBufferEntry, threads, addrs, mem, region_map :: storeBufferEntry in storeBuffer && (forall p :: p in s_region_map ==> p in region_map && region_map[p] == s_region_map[p]) && RegionInvariant_mem(threads, addrs, mem, region_map) ==> RegionInvariant_mem(threads, addrs, L.Armada_ApplyStoreBufferEntry(mem, storeBufferEntry), region_map) ensures RegionInvariant_mem(s_threads, s_addrs, L.Armada_ApplyStoreBuffer(s_mem, storeBuffer), s_region_map) decreases |storeBuffer| { if |storeBuffer| > 0 { var s_mem' := L.Armada_ApplyStoreBufferEntry(s_mem, storeBuffer[0]); lemma_RegionInvariantOnGlobalViewImpliesRegionInvariantOnLocalView(s_threads, s_addrs, s_mem', s_region_map, storeBuffer[1..]); } } "; pgp.AddLemma(str, "invariants"); str = @" lemma lemma_RegionInvariantOnGlobalViewAlwaysImpliesRegionInvariantOnLocalView() ensures forall s :: RegionInvariant(s) ==> (forall tid :: tid in s.s.threads ==> RegionInvariant_mem(s.s.threads, s.s.addrs, L.Armada_GetThreadLocalView(s.s, tid), s.region_map)) { forall s | RegionInvariant(s) ensures (forall tid :: tid in s.s.threads ==> RegionInvariant_mem(s.s.threads, s.s.addrs, L.Armada_GetThreadLocalView(s.s, tid), s.region_map)) { forall tid | tid in s.s.threads ensures RegionInvariant_mem(s.s.threads, s.s.addrs, L.Armada_GetThreadLocalView(s.s, tid), s.region_map) { lemma_RegionInvariantOnGlobalViewImpliesRegionInvariantOnLocalView(s.s.threads, s.s.addrs, s.s.mem, s.region_map, s.s.threads[tid].storeBuffer); } } } "; pgp.AddLemma(str, "invariants"); } //////////////////////////////////////////////////////////////////////// /// Lemmas about heap invariant preservation //////////////////////////////////////////////////////////////////////// protected virtual void GenerateHeapInvariant() { string str = @" predicate HeapInvariant(s:LPlusState) { Armada_HeapInvariant(s.s.mem.heap) } "; pgp.AddDefaultClassDecl((Predicate)AH.ParseTopLevelDecl(pgp.prog, "HeapInvariant", str), "specs"); AddInvariant(new InternalInvariantInfo("HeapInvariant", "HeapInvariant", new List() {"AddressableInvariant"} )); } protected virtual void GenerateLemmasHelpfulForProvingInitPreservation_LH() { string str; str = $@" lemma lemma_ConvertTotalStatePreservesAddressableStaticVariablesAreValid(ls:LState, hs:HState, new_ptrs:set) requires hs == ConvertTotalState_LH(ls) requires L.Armada_AddressableStaticVariablesAreValid(ls, new_ptrs) ensures H.Armada_AddressableStaticVariablesAreValid(hs, new_ptrs) {{ var lheap := ls.mem.heap.(valid := ls.mem.heap.valid - new_ptrs); var hheap := hs.mem.heap.(valid := hs.mem.heap.valid - new_ptrs); }} "; pgp.AddLemma(str); str = @" lemma lemma_ConvertTotalStatePreservesAddressableStaticVariablesAreRoots(ls:LState, hs:HState) requires hs == ConvertTotalState_LH(ls) requires L.Armada_AddressableStaticVariablesAreDistinctRoots(ls) ensures H.Armada_AddressableStaticVariablesAreDistinctRoots(hs) { } "; pgp.AddLemma(str); str = @" lemma lemma_ConvertTotalStatePreservesInit(ls: LState, hs: HState, lconfig: Armada_Config, hconfig: Armada_Config) requires L.Armada_InitConfig(ls, lconfig) requires hs == ConvertTotalState_LH(ls) requires hconfig == ConvertConfig_LH(lconfig) ensures H.Armada_InitConfig(hs, hconfig) { lemma_ConvertTotalStatePreservesAddressableStaticVariablesAreValid(ls, hs, lconfig.new_ptrs); lemma_ConvertTotalStatePreservesAddressableStaticVariablesAreRoots(ls, hs); } "; pgp.AddLemma(str); } //////////////////////////////////////////////////////////////////////// /// Lemmas about commutativity //////////////////////////////////////////////////////////////////////// protected virtual void GenerateLocalViewCommutativityLemmas() { string str; str = @" lemma lemma_ApplyStoreBufferEntryUnaddressableCommutesWithConvert( lglobals:L.Armada_Globals, lv:L.Armada_GlobalStaticVar, fields:seq, value:Armada_PrimitiveValue, hv:H.Armada_GlobalStaticVar, hglobals1:H.Armada_Globals, hglobals2:H.Armada_Globals) requires hv == ConvertGlobalStaticVar_LH(lv) requires hglobals1 == ConvertGlobals_LH(L.Armada_ApplyTauUnaddressable(lglobals, lv, fields, value)) requires hglobals2 == H.Armada_ApplyTauUnaddressable(ConvertGlobals_LH(lglobals), hv, fields, value) ensures hglobals1 == hglobals2 { } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_ApplyStoreBufferEntryCommutesWithConvert(lmem:L.Armada_SharedMemory, lentry:L.Armada_StoreBufferEntry, hentry:H.Armada_StoreBufferEntry, hmem1:H.Armada_SharedMemory, hmem2:H.Armada_SharedMemory) requires hentry == ConvertStoreBufferEntry_LH(lentry) requires hmem1 == ConvertSharedMemory_LH(L.Armada_ApplyStoreBufferEntry(lmem, lentry)) requires hmem2 == H.Armada_ApplyStoreBufferEntry(ConvertSharedMemory_LH(lmem), hentry) ensures hmem1 == hmem2 { match lentry.loc case Armada_StoreBufferLocation_Unaddressable(lv, lfields) => { var hv := ConvertGlobalStaticVar_LH(lv); lemma_ApplyStoreBufferEntryUnaddressableCommutesWithConvert(lmem.globals, lv, lfields, lentry.value, hv, hmem1.globals, hmem2.globals); } case Armada_StoreBufferLocation_Addressable(p) => { } } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_ApplyStoreBufferCommutesWithConvert(lmem:L.Armada_SharedMemory, lbuf:seq, hbuf:seq, hmem1:H.Armada_SharedMemory, hmem2:H.Armada_SharedMemory) requires hbuf == ConvertStoreBuffer_LH(lbuf) requires hmem1 == ConvertSharedMemory_LH(L.Armada_ApplyStoreBuffer(lmem, lbuf)) requires hmem2 == H.Armada_ApplyStoreBuffer(ConvertSharedMemory_LH(lmem), hbuf) ensures hmem1 == hmem2 decreases |lbuf| + |hbuf| { if |lbuf| == 0 { return; } var lmem' := L.Armada_ApplyStoreBufferEntry(lmem, lbuf[0]); var hmem1' := ConvertSharedMemory_LH(L.Armada_ApplyStoreBufferEntry(lmem, lbuf[0])); var hmem2' := H.Armada_ApplyStoreBufferEntry(ConvertSharedMemory_LH(lmem), hbuf[0]); lemma_ApplyStoreBufferEntryCommutesWithConvert(lmem, lbuf[0], hbuf[0], hmem1', hmem2'); lemma_ApplyStoreBufferCommutesWithConvert(lmem', lbuf[1..], hbuf[1..], hmem1, hmem2); } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_GetThreadLocalViewCommutesWithConvert(ls:LState, hs:HState, tid:Armada_ThreadHandle) requires hs == ConvertTotalState_LH(ls) requires tid in ls.threads; ensures ConvertSharedMemory_LH(L.Armada_GetThreadLocalView(ls, tid)) == H.Armada_GetThreadLocalView(hs, tid) { assert tid in hs.threads; lemma_ApplyStoreBufferCommutesWithConvert(ls.mem, ls.threads[tid].storeBuffer, hs.threads[tid].storeBuffer, ConvertSharedMemory_LH(L.Armada_GetThreadLocalView(ls, tid)), H.Armada_GetThreadLocalView(hs, tid)); } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_GetThreadLocalViewAlwaysCommutesWithConvert() ensures forall ls:L.Armada_TotalState, tid:Armada_ThreadHandle {:trigger L.Armada_GetThreadLocalView(ls, tid)} :: tid in ls.threads ==> ConvertSharedMemory_LH(L.Armada_GetThreadLocalView(ls, tid)) == H.Armada_GetThreadLocalView(ConvertTotalState_LH(ls), tid) { forall ls:L.Armada_TotalState, tid:Armada_ThreadHandle {:trigger L.Armada_GetThreadLocalView(ls, tid)} | tid in ls.threads ensures ConvertSharedMemory_LH(L.Armada_GetThreadLocalView(ls, tid)) == H.Armada_GetThreadLocalView(ConvertTotalState_LH(ls), tid) { var hs := ConvertTotalState_LH(ls); lemma_GetThreadLocalViewCommutesWithConvert(ls, hs, tid); } } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_StoreBufferAppendConversion(buf: seq, entry: L.Armada_StoreBufferEntry) ensures ConvertStoreBuffer_LH(buf + [entry]) == ConvertStoreBuffer_LH(buf) + [ConvertStoreBufferEntry_LH(entry)] { assert [entry][1..] == []; if |buf| == 0 { assert buf + [entry] == [entry]; assert ConvertStoreBuffer_LH(buf + [entry]) == ConvertStoreBuffer_LH([entry]); assert ConvertStoreBuffer_LH(buf) == []; calc { ConvertStoreBuffer_LH([entry]); [ConvertStoreBufferEntry_LH(entry)] + ConvertStoreBuffer_LH([entry][1..]); [ConvertStoreBufferEntry_LH(entry)] + ConvertStoreBuffer_LH([]); [ConvertStoreBufferEntry_LH(entry)] + []; [ConvertStoreBufferEntry_LH(entry)]; } } else { calc { ConvertStoreBuffer_LH(buf + [entry]); { assert buf == [buf[0]] + buf[1..]; } ConvertStoreBuffer_LH([buf[0]] + buf[1..] + [entry]); { assert [buf[0]] + buf[1..] + [entry] == [buf[0]] + (buf[1..] + [entry]); } ConvertStoreBuffer_LH([buf[0]] + (buf[1..] + [entry])); } calc { ConvertStoreBuffer_LH(buf + [entry]); ConvertStoreBuffer_LH([buf[0]] + (buf[1..] + [entry])); [ConvertStoreBufferEntry_LH(buf[0])] + ConvertStoreBuffer_LH(buf[1..] + [entry]); } lemma_StoreBufferAppendConversion(buf[1..], entry); calc { ConvertStoreBuffer_LH(buf + [entry]); [ConvertStoreBufferEntry_LH(buf[0])] + (ConvertStoreBuffer_LH(buf[1..]) + [ConvertStoreBufferEntry_LH(entry)]); [ConvertStoreBufferEntry_LH(buf[0])] + ConvertStoreBuffer_LH(buf[1..]) + [ConvertStoreBufferEntry_LH(entry)]; ConvertStoreBuffer_LH(buf) + [ConvertStoreBufferEntry_LH(entry)]; } } } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_StoreBufferAppendAlwaysCommutesWithConvert() ensures forall lbuf: seq, lentry: L.Armada_StoreBufferEntry {:trigger L.Armada_StoreBufferAppend(lbuf, lentry)} :: H.Armada_StoreBufferAppend(ConvertStoreBuffer_LH(lbuf), ConvertStoreBufferEntry_LH(lentry)) == ConvertStoreBuffer_LH(L.Armada_StoreBufferAppend(lbuf, lentry)) { forall lbuf: seq, lentry: L.Armada_StoreBufferEntry {:trigger L.Armada_StoreBufferAppend(lbuf, lentry)} ensures H.Armada_StoreBufferAppend(ConvertStoreBuffer_LH(lbuf), ConvertStoreBufferEntry_LH(lentry)) == ConvertStoreBuffer_LH(L.Armada_StoreBufferAppend(lbuf, lentry)) { lemma_StoreBufferAppendConversion(lbuf, lentry); } } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_ConvertStoreBufferCommutesOverBeheadment(buf:seq) requires |buf| > 0 ensures ConvertStoreBuffer_LH(buf[1..]) == ConvertStoreBuffer_LH(buf)[1..] { var hbuf1 := ConvertStoreBuffer_LH(buf[1..]); var hbuf2 := ConvertStoreBuffer_LH(buf)[1..]; assert |hbuf1| == |hbuf2| == |buf| - 1; forall i | 0 <= i < |buf| - 1 ensures hbuf1[i] == hbuf2[i] { } assert hbuf1 == hbuf2; } "; pgp.AddLemma(str, "utility"); } protected virtual void GenerateAppendStoreBufferOtherWay() { if (calledGenerateAppendStoreBufferOtherWay) { return; } calledGenerateAppendStoreBufferOtherWay= true; string str; str = @" function ApplyStoreBufferOtherWay_L(mem: L.Armada_SharedMemory, storeBuffer: seq): (mem': L.Armada_SharedMemory) decreases |storeBuffer| { if |storeBuffer| == 0 then mem else L.Armada_ApplyStoreBufferEntry(ApplyStoreBufferOtherWay_L(mem, all_but_last(storeBuffer)), last(storeBuffer)) } "; pgp.AddFunction(str, "utility"); str = @" function ApplyStoreBufferOtherWay_H(mem: H.Armada_SharedMemory, storeBuffer: seq): (mem': H.Armada_SharedMemory) decreases |storeBuffer| { if |storeBuffer| == 0 then mem else H.Armada_ApplyStoreBufferEntry(ApplyStoreBufferOtherWay_H(mem, all_but_last(storeBuffer)), last(storeBuffer)) } "; pgp.AddFunction(str, "utility"); str = @" lemma lemma_ApplyStoreBufferOtherWayEquivalent_L(mem: L.Armada_SharedMemory, storeBuffer: seq) ensures L.Armada_ApplyStoreBuffer(mem, storeBuffer) == ApplyStoreBufferOtherWay_L(mem, storeBuffer) decreases |storeBuffer| { if |storeBuffer| == 0 || |storeBuffer| == 1 { return; } var mem' := L.Armada_ApplyStoreBufferEntry(mem, storeBuffer[0]); calc { L.Armada_ApplyStoreBuffer(mem, storeBuffer); L.Armada_ApplyStoreBuffer(mem', storeBuffer[1..]); { lemma_ApplyStoreBufferOtherWayEquivalent_L(mem', storeBuffer[1..]); } ApplyStoreBufferOtherWay_L(mem', storeBuffer[1..]); L.Armada_ApplyStoreBufferEntry(ApplyStoreBufferOtherWay_L(mem', all_but_last(storeBuffer[1..])), last(storeBuffer[1..])); { assert last(storeBuffer[1..]) == last(storeBuffer); } L.Armada_ApplyStoreBufferEntry(ApplyStoreBufferOtherWay_L(mem', all_but_last(storeBuffer[1..])), last(storeBuffer)); { lemma_ApplyStoreBufferOtherWayEquivalent_L(mem', all_but_last(storeBuffer[1..])); } L.Armada_ApplyStoreBufferEntry(L.Armada_ApplyStoreBuffer(mem', all_but_last(storeBuffer[1..])), last(storeBuffer)); L.Armada_ApplyStoreBufferEntry(L.Armada_ApplyStoreBuffer(mem, [storeBuffer[0]] + all_but_last(storeBuffer[1..])), last(storeBuffer)); { assert [storeBuffer[0]] + all_but_last(storeBuffer[1..]) == all_but_last(storeBuffer); } L.Armada_ApplyStoreBufferEntry(L.Armada_ApplyStoreBuffer(mem, all_but_last(storeBuffer)), last(storeBuffer)); { lemma_ApplyStoreBufferOtherWayEquivalent_L(mem, all_but_last(storeBuffer)); } L.Armada_ApplyStoreBufferEntry(ApplyStoreBufferOtherWay_L(mem, all_but_last(storeBuffer)), last(storeBuffer)); ApplyStoreBufferOtherWay_L(mem, storeBuffer); } } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_ApplyStoreBufferOtherWayEquivalent_H(mem: H.Armada_SharedMemory, storeBuffer: seq) ensures H.Armada_ApplyStoreBuffer(mem, storeBuffer) == ApplyStoreBufferOtherWay_H(mem, storeBuffer) decreases |storeBuffer| { if |storeBuffer| == 0 || |storeBuffer| == 1 { return; } var mem' := H.Armada_ApplyStoreBufferEntry(mem, storeBuffer[0]); calc { H.Armada_ApplyStoreBuffer(mem, storeBuffer); H.Armada_ApplyStoreBuffer(mem', storeBuffer[1..]); { lemma_ApplyStoreBufferOtherWayEquivalent_H(mem', storeBuffer[1..]); } ApplyStoreBufferOtherWay_H(mem', storeBuffer[1..]); H.Armada_ApplyStoreBufferEntry(ApplyStoreBufferOtherWay_H(mem', all_but_last(storeBuffer[1..])), last(storeBuffer[1..])); { assert last(storeBuffer[1..]) == last(storeBuffer); } H.Armada_ApplyStoreBufferEntry(ApplyStoreBufferOtherWay_H(mem', all_but_last(storeBuffer[1..])), last(storeBuffer)); { lemma_ApplyStoreBufferOtherWayEquivalent_H(mem', all_but_last(storeBuffer[1..])); } H.Armada_ApplyStoreBufferEntry(H.Armada_ApplyStoreBuffer(mem', all_but_last(storeBuffer[1..])), last(storeBuffer)); H.Armada_ApplyStoreBufferEntry(H.Armada_ApplyStoreBuffer(mem, [storeBuffer[0]] + all_but_last(storeBuffer[1..])), last(storeBuffer)); { assert [storeBuffer[0]] + all_but_last(storeBuffer[1..]) == all_but_last(storeBuffer); } H.Armada_ApplyStoreBufferEntry(H.Armada_ApplyStoreBuffer(mem, all_but_last(storeBuffer)), last(storeBuffer)); { lemma_ApplyStoreBufferOtherWayEquivalent_H(mem, all_but_last(storeBuffer)); } H.Armada_ApplyStoreBufferEntry(ApplyStoreBufferOtherWay_H(mem, all_but_last(storeBuffer)), last(storeBuffer)); ApplyStoreBufferOtherWay_H(mem, storeBuffer); } } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_ApplyStoreBufferOtherWayAlwaysEquivalent() ensures forall mem, storeBuffer :: L.Armada_ApplyStoreBuffer(mem, storeBuffer) == ApplyStoreBufferOtherWay_L(mem, storeBuffer) ensures forall mem, storeBuffer :: H.Armada_ApplyStoreBuffer(mem, storeBuffer) == ApplyStoreBufferOtherWay_H(mem, storeBuffer) { forall mem, storeBuffer ensures L.Armada_ApplyStoreBuffer(mem, storeBuffer) == ApplyStoreBufferOtherWay_L(mem, storeBuffer) { lemma_ApplyStoreBufferOtherWayEquivalent_L(mem, storeBuffer); } forall mem, storeBuffer ensures H.Armada_ApplyStoreBuffer(mem, storeBuffer) == ApplyStoreBufferOtherWay_H(mem, storeBuffer) { lemma_ApplyStoreBufferOtherWayEquivalent_H(mem, storeBuffer); } } "; pgp.AddLemma(str, "utility"); } //////////////////////////////////////////////////////////////////////// /// Store buffers //////////////////////////////////////////////////////////////////////// protected void GenerateGenericStoreBufferLemmas_L() { string str; str = @" lemma lemma_ApplyStoreBufferCommutesWithAppend_L( mem: L.Armada_SharedMemory, buf:seq, entry:L.Armada_StoreBufferEntry ) ensures L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry)) == L.Armada_ApplyStoreBufferEntry(L.Armada_ApplyStoreBuffer(mem, buf), entry) decreases |buf| { if |buf| > 0 { var mem' := L.Armada_ApplyStoreBufferEntry(mem, buf[0]); var buf' := L.Armada_StoreBufferAppend(buf, entry); assert buf'[0] == buf[0]; assert buf'[1..] == buf[1..] + [entry]; calc { L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry)); L.Armada_ApplyStoreBuffer(mem, buf'); L.Armada_ApplyStoreBuffer(L.Armada_ApplyStoreBufferEntry(mem, buf'[0]), buf'[1..]); L.Armada_ApplyStoreBuffer(mem', buf'[1..]); { lemma_ApplyStoreBufferCommutesWithAppend_L(mem', buf[1..], entry); } L.Armada_ApplyStoreBufferEntry(L.Armada_ApplyStoreBuffer(mem', buf[1..]), entry); L.Armada_ApplyStoreBufferEntry(L.Armada_ApplyStoreBuffer(mem, buf), entry); } } } "; pgp.AddLemma(str, "utility"); str = @" lemma lemma_ApplyStoreBufferCommutesWithAppendAlways_L() ensures forall mem: L.Armada_SharedMemory, buf:seq, entry:L.Armada_StoreBufferEntry :: L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry)) == L.Armada_ApplyStoreBufferEntry(L.Armada_ApplyStoreBuffer(mem, buf), entry) { forall mem: L.Armada_SharedMemory, buf:seq, entry:L.Armada_StoreBufferEntry ensures L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry)) == L.Armada_ApplyStoreBufferEntry(L.Armada_ApplyStoreBuffer(mem, buf), entry) { lemma_ApplyStoreBufferCommutesWithAppend_L(mem, buf, entry); } } "; pgp.AddLemma(str, "utility"); str = @" predicate SharedMemoryHasPredecessor_L(mem':L.Armada_SharedMemory, entry:L.Armada_StoreBufferEntry) { exists mem :: mem' == L.Armada_ApplyStoreBufferEntry(mem, entry) } "; pgp.AddPredicate(str, "utility"); str = @" lemma lemma_StoreBufferAppendHasEffectOfAppendedEntryAlways_L() ensures forall mem:L.Armada_SharedMemory, buf:seq, entry:L.Armada_StoreBufferEntry {:trigger L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry))} :: SharedMemoryHasPredecessor_L(L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry)), entry) { forall mem:L.Armada_SharedMemory, buf:seq, entry:L.Armada_StoreBufferEntry {:trigger L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry))} ensures SharedMemoryHasPredecessor_L(L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry)), entry) { lemma_ApplyStoreBufferCommutesWithAppend_L(mem, buf, entry); var mem_prev := L.Armada_ApplyStoreBuffer(mem, buf); assert L.Armada_ApplyStoreBuffer(mem, L.Armada_StoreBufferAppend(buf, entry)) == L.Armada_ApplyStoreBufferEntry(mem_prev, entry); } } "; pgp.AddLemma(str, "utility"); } //////////////////////////////////////////////////////////////////////// /// Invariants //////////////////////////////////////////////////////////////////////// protected void AddInvariant(InvariantInfo inv) { invariants.Add(inv); } private void GenerateInductiveInv(ProofGenerationParams pgp, bool onlyNonstoppingPaths) { var invNames = invariants.Select(x => x.Name).ToList(); var conjuncts = new List(); if (onlyNonstoppingPaths) { conjuncts.Add("s.s.stop_reason.Armada_NotStopped?"); } conjuncts.AddRange(invariants.Select(inv => $"{inv.Name}(s)")); string str = $@" predicate InductiveInv(s:LPlusState) {{ { AH.CombineStringsWithAnd(conjuncts) } }} "; pgp.AddPredicate(str, "defs"); } private void GenerateInitImpliesInductiveInvLemma(ProofGenerationParams pgp) { string str = @" lemma lemma_InitImpliesInductiveInv(s:LPlusState) requires LPlus_Init(s) ensures InductiveInv(s) { "; str += String.Concat(invariants.Select(inv => $"{inv.InitLemmaName}(s);\n")); str += " }\n"; pgp.AddLemma(str, "invariants"); } private void GenerateAtomicPathMaintainsInductiveInvLemma(ProofGenerationParams pgp) { string str = @" lemma lemma_AtomicPathMaintainsInductiveInv(s: LPlusState, s': LPlusState, path: LAtomic_Path, tid: Armada_ThreadHandle) requires InductiveInv(s) requires LAtomic_ValidPath(s, path, tid) requires s' == LAtomic_GetStateAfterPath(s, path, tid) ensures InductiveInv(s') { "; str += String.Concat(invariants.Select(inv => $"{inv.NextLemmaName}(s, s', path, tid);\n")); str += "}\n"; pgp.AddLemma(str, "invariants"); } public void GenerateInvariantProof(ProofGenerationParams pgp, bool onlyNonstoppingPaths = false) { if (lAtomic == null) { AH.PrintError(pgp.prog, "Internal error: Must call GenerateProofHeader before calling GenerateInvariantProof"); } foreach (var inv in invariants) { inv.GenerateProofs(pgp, invariants, lAtomic, onlyNonstoppingPaths); } GenerateInductiveInv(pgp, onlyNonstoppingPaths); GenerateInitImpliesInductiveInvLemma(pgp); GenerateAtomicPathMaintainsInductiveInvLemma(pgp); } //////////////////////////////////////////////////////////////////////// /// Lemmas about path lifting //////////////////////////////////////////////////////////////////////// protected virtual void GenerateLiftAtomicPathLemmaForNormalPath(AtomicPath atomicPath, string typeComparison = "HAtomic_GetPathType(hpath) == ty", string extraSignatureLines = "", string extraProof = "") { string convertParams = stateDependentConvertStep ? "ls, lpath, tid" : "lpath"; var hAtomicPath = pathMap[atomicPath]; var lpr = new PrefixedVarsPathPrinter(lAtomic); var hpr = new PrefixedVarsPathPrinter(hAtomic); var prioritizationAttrs = Prioritizer.GetPrioritizationAttributesForLiftAtomicPath(atomicPath, hAtomicPath); var str = $@" lemma {prioritizationAttrs} lemma_LiftAtomicPath_{atomicPath.Name}(ls: LPlusState, lpath: LAtomic_Path, tid: Armada_ThreadHandle) requires InductiveInv(ls) requires LAtomic_ValidPath(ls, lpath, tid) requires lpath.LAtomic_Path_{atomicPath.Name}? { extraSignatureLines } ensures var ls' := LAtomic_GetStateAfterPath(ls, lpath, tid); var hs := ConvertTotalState_LPlusH(ls); var hpath := ConvertAtomicPath_LH({convertParams}); var hs' := HAtomic_GetStateAfterPath(hs, hpath, tid); var ty := LAtomic_GetPathType(lpath); && {typeComparison} && HAtomic_ValidPath(hs, hpath, tid) && hs' == ConvertTotalState_LPlusH(ls') && {atomicPath.OptionalNotForOK}ls'.s.stop_reason.Armada_NotStopped? && {atomicPath.OptionalNotForOK}hs'.stop_reason.Armada_NotStopped? {{ { lpr.GetOpenValidPathInvocation(atomicPath) } var locv: H.Armada_SharedMemory := H.Armada_GetThreadLocalView(ConvertTotalState_LPlusH(ls), tid); var ls' := LAtomic_GetStateAfterPath(ls, lpath, tid); var hs := ConvertTotalState_LPlusH(ls); var hpath := ConvertAtomicPath_LH({convertParams}); var hs' := ConvertTotalState_LPlusH(ls'); { hpr.GetOpenPathInvocation(hAtomicPath) } lemma_GetThreadLocalViewAlwaysCommutesWithConvert(); lemma_StoreBufferAppendAlwaysCommutesWithConvert(); { extraProof } ProofCustomizationGoesHere(); "; if (atomicPath.Stopping) { str += "assert !hs'.stop_reason.Armada_NotStopped?;\n"; } else { str += @" assert ls'.s.stop_reason.Armada_NotStopped?; var alt_hs' := HAtomic_GetStateAfterPath(hs, hpath, tid); assert hs'.stop_reason == alt_hs'.stop_reason; "; if (atomicPath.LastNextRoutine.nextType == NextType.TerminateThread) { str += @" assert tid !in hs'.threads; assert tid !in alt_hs'.threads; "; } else { str += "assert hs'.threads[tid] == alt_hs'.threads[tid];\n"; } foreach (var tup in atomicPath.NextRoutinesWithIndices) { var nextRoutine = tup.Item1; if (nextRoutine.nextType == NextType.CreateThread) { var i = tup.Item2; str += $@" assert hs'.threads[lsteps.step{i}.params_{nextRoutine.NameSuffix}.newtid] == alt_hs'.threads[lsteps.step{i}.params_{nextRoutine.NameSuffix}.newtid]; "; } } str += @" forall other_tid ensures other_tid !in hs'.threads ==> other_tid !in alt_hs'.threads ensures other_tid in hs'.threads ==> other_tid in alt_hs'.threads && hs'.threads[other_tid] == alt_hs'.threads[other_tid] { } assert hs'.threads == alt_hs'.threads; assert hs'.mem == alt_hs'.mem; assert hs' == alt_hs'; "; } // str += hpr.GetAssertValidPathInvocation(hAtomicPath); str += "}\n"; pgp.AddLemma(str, "lift"); } protected virtual void GenerateLiftAtomicPathLemmaForTauPath(AtomicPath atomicPath, string typeComparison = "HAtomic_GetPathType(hpath) == ty", string extraSignatureLines = "") { string convertParams = stateDependentConvertStep ? "ls, lpath, tid" : "lpath"; var hAtomicPath = pathMap[atomicPath]; var lpr = new PrefixedVarsPathPrinter(lAtomic); var hpr = new PrefixedVarsPathPrinter(hAtomic); var str = $@" lemma lemma_LiftAtomicPath_Tau(ls: LPlusState, lpath: LAtomic_Path, tid: Armada_ThreadHandle) requires InductiveInv(ls) requires LAtomic_ValidPath(ls, lpath, tid) requires lpath.LAtomic_Path_{atomicPath.Name}? { extraSignatureLines } ensures var ls' := LAtomic_GetStateAfterPath(ls, lpath, tid); var hs := ConvertTotalState_LPlusH(ls); var hpath := ConvertAtomicPath_LH({convertParams}); var hs' := HAtomic_GetStateAfterPath(hs, hpath, tid); var ty := LAtomic_GetPathType(lpath); && {typeComparison} && HAtomic_ValidPath(hs, hpath, tid) && hs' == ConvertTotalState_LPlusH(ls') && ls'.s.stop_reason.Armada_NotStopped? == hs'.stop_reason.Armada_NotStopped? {{ { lpr.GetOpenValidPathInvocation(atomicPath) } var ls' := LAtomic_GetStateAfterPath(ls, lpath, tid); var hs := ConvertTotalState_LPlusH(ls); var hpath := ConvertAtomicPath_LH({convertParams}); var hs' := ConvertTotalState_LPlusH(ls'); var lentry := ls.s.threads[tid].storeBuffer[0]; var hentry := hs.threads[tid].storeBuffer[0]; var lmem := ls.s.mem; var hmem1 := ConvertSharedMemory_LH(L.Armada_ApplyStoreBufferEntry(lmem, lentry)); var hmem2 := H.Armada_ApplyStoreBufferEntry(ConvertSharedMemory_LH(lmem), hentry); lemma_ApplyStoreBufferEntryCommutesWithConvert(lmem, lentry, hentry, hmem1, hmem2); { hpr.GetOpenPathInvocation(hAtomicPath) } var alt_hs' := HAtomic_GetStateAfterPath(hs, hpath, tid); ProofCustomizationGoesHere(); assert hs'.threads[tid] == alt_hs'.threads[tid]; assert hs'.threads == alt_hs'.threads; assert hs' == alt_hs'; /* { hpr.GetAssertValidPathInvocation(hAtomicPath) } */ }} "; pgp.AddLemma(str, "lift"); } protected virtual void GenerateLiftAtomicPathLemmaForUnmappedPath(AtomicPath atomicPath, string extraSignatureLines) { var lpr = new PrefixedVarsPathPrinter(lAtomic); var str = $@" lemma lemma_LiftAtomicPath_{atomicPath.Name}(ls: LPlusState, lpath: LAtomic_Path, tid: Armada_ThreadHandle) requires InductiveInv(ls) requires LAtomic_ValidPath(ls, lpath, tid) requires lpath.LAtomic_Path_{atomicPath.Name}? { extraSignatureLines } ensures false {{ { lpr.GetOpenValidPathInvocation(atomicPath) } var ls' := LAtomic_GetStateAfterPath(ls, lpath, tid); lemma_GetThreadLocalViewAlwaysCommutesWithConvert(); lemma_StoreBufferAppendAlwaysCommutesWithConvert(); ProofCustomizationGoesHere(); }} "; pgp.AddLemma(str, "lift"); } protected virtual void GenerateLiftAtomicPathLemmas(string typeComparison = "HAtomic_GetPathType(hpath) == ty", string extraSignatureLines = "", string extraProof = "") { var finalCases = ""; foreach (var atomicPath in lAtomic.AtomicPaths) { if (atomicPath.Tau) { GenerateLiftAtomicPathLemmaForTauPath(atomicPath, typeComparison, extraSignatureLines); } else if (pathMap.ContainsKey(atomicPath) && pathMap[atomicPath] != null) { GenerateLiftAtomicPathLemmaForNormalPath(atomicPath, typeComparison, extraSignatureLines, extraProof); } else { GenerateLiftAtomicPathLemmaForUnmappedPath(atomicPath, extraSignatureLines); } finalCases += $"case LAtomic_Path_{atomicPath.Name}(_) => lemma_LiftAtomicPath_{atomicPath.Name}(ls, lpath, tid);\n"; } string convertParams = stateDependentConvertStep ? "ls, lpath, tid" : "lpath"; string str = $@" lemma lemma_LiftAtomicPath(ls: LPlusState, lpath: LAtomic_Path, tid: Armada_ThreadHandle) requires InductiveInv(ls) requires LAtomic_ValidPath(ls, lpath, tid) { extraSignatureLines } ensures var ls' := LAtomic_GetStateAfterPath(ls, lpath, tid); var hs := ConvertTotalState_LPlusH(ls); var hpath := ConvertAtomicPath_LH({convertParams}); var hs' := HAtomic_GetStateAfterPath(hs, hpath, tid); var ty := LAtomic_GetPathType(lpath); && {typeComparison} && HAtomic_ValidPath(hs, hpath, tid) && hs' == ConvertTotalState_LPlusH(ls') && ls'.s.stop_reason.Armada_NotStopped? == hs'.stop_reason.Armada_NotStopped? {{ match lpath {{ {finalCases} }} }} "; pgp.AddLemma(str, "lift"); } protected virtual void GenerateLiftingRelation() { string str = @" predicate LiftingRelation(ls:LPlusState, hs:HState) { hs == ConvertTotalState_LPlusH(ls) } "; pgp.AddPredicate(str, "defs"); } protected virtual void GenerateEstablishAtomicPathLiftableLemma() { var convertParams = stateDependentConvertStep ? "ls, lpath, tid" : "lpath"; string str = $@" lemma lemma_EstablishAtomicPathLiftable( lasf:AtomicSpecFunctions, hasf:AtomicSpecFunctions, ls: LPlusState, lpath: LAtomic_Path, tid: Armada_ThreadHandle, hs: HState ) returns ( hpath: HAtomic_Path ) requires lasf == LAtomic_GetSpecFunctions() requires hasf == HAtomic_GetSpecFunctions() requires InductiveInv(ls) requires LiftingRelation(ls, hs) requires LAtomic_ValidPath(ls, lpath, tid) ensures LiftAtomicPathSuccessful(lasf, hasf, InductiveInv, LiftingRelation, ls, lpath, tid, hs, hpath) {{ var ls' := LAtomic_GetStateAfterPath(ls, lpath, tid); lemma_AtomicPathMaintainsInductiveInv(ls, ls', lpath, tid); assert InductiveInv(ls'); lemma_LiftAtomicPath(ls, lpath, tid); hpath := ConvertAtomicPath_LH({convertParams}); assert LiftAtomicPathSuccessful(lasf, hasf, InductiveInv, LiftingRelation, ls, lpath, tid, hs, hpath); }} "; pgp.AddLemma(str); } protected virtual void GenerateEstablishAtomicPathsLiftableLemma(bool skippable, bool introducible) { pgp.MainProof.AddInclude(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/generic/LiftAtomicToAtomic.i.dfy"); pgp.MainProof.AddImport("LiftAtomicToAtomicModule"); string str = @" lemma lemma_EstablishAtomicPathsLiftable( lasf:AtomicSpecFunctions, hasf:AtomicSpecFunctions, inv:LPlusState->bool, relation:(LPlusState, HState)->bool "; if (introducible) { str += ", progress_measure:(HState, LAtomic_Path, Armada_ThreadHandle)->(int, int)"; } str += @" ) requires lasf == LAtomic_GetSpecFunctions() requires hasf == HAtomic_GetSpecFunctions() requires inv == InductiveInv requires relation == LiftingRelation "; if (introducible) { str += @" requires progress_measure == ProgressMeasure "; } str += @" ensures forall ls, lpath, hs, tid :: inv(ls) && relation(ls, hs) && lasf.path_valid(ls, lpath, tid) "; if (skippable) { str += " && !AtomicPathSkippable(lasf, inv, relation, ls, lpath, tid, hs)"; } str += @" ==> exists hpath :: LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath) "; if (introducible) { str += @" || AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpath, tid, hs, hpath) "; } str += @" { forall ls, lpath, hs, tid | inv(ls) && relation(ls, hs) && lasf.path_valid(ls, lpath, tid) "; if (skippable) { str += " && !AtomicPathSkippable(lasf, inv, relation, ls, lpath, tid, hs)"; } str += @" ensures exists hpath :: LiftAtomicPathSuccessful(lasf, hasf, inv, relation, ls, lpath, tid, hs, hpath) "; if (introducible) { str += @" || AtomicPathIntroduced(lasf, hasf, relation, progress_measure, ls, lpath, tid, hs, hpath) "; } str += @" { var hpath := lemma_EstablishAtomicPathLiftable(lasf, hasf, ls, lpath, tid, hs); } } "; pgp.AddLemma(str); } protected virtual void GenerateEstablishInitRequirementsLemma() { string str = @" lemma lemma_EstablishInitRequirements( lasf:AtomicSpecFunctions, hasf:AtomicSpecFunctions, inv:LPlusState->bool, relation:(LPlusState, HState)->bool ) requires lasf == LAtomic_GetSpecFunctions() requires hasf == HAtomic_GetSpecFunctions() requires inv == InductiveInv requires relation == LiftingRelation ensures AtomicInitImpliesInv(lasf, inv) ensures forall ls :: lasf.init(ls) ==> exists hs :: hasf.init(hs) && relation(ls, hs) { forall ls | lasf.init(ls) ensures inv(ls) ensures exists hs :: hasf.init(hs) && relation(ls, hs) { lemma_InitImpliesInductiveInv(ls); var hs := ConvertTotalState_LPlusH(ls); var hconfig := ConvertConfig_LH(ls.config); assert H.Armada_InitConfig(hs, hconfig); assert hasf.init(hs) && relation(ls, hs); } } "; pgp.AddLemma(str); } protected virtual void GenerateEstablishStateOKRequirementLemma() { string str = @" lemma lemma_EstablishStateOKRequirement( lasf:AtomicSpecFunctions, hasf:AtomicSpecFunctions, relation:(LPlusState, HState)->bool ) requires lasf == LAtomic_GetSpecFunctions() requires hasf == HAtomic_GetSpecFunctions() requires relation == LiftingRelation ensures forall ls, hs :: relation(ls, hs) ==> lasf.state_ok(ls) == hasf.state_ok(hs) { } "; pgp.AddLemma(str); } protected virtual void GenerateEstablishRelationRequirementLemma() { string str = @" lemma lemma_EstablishRelationRequirement( lasf:AtomicSpecFunctions, hasf:AtomicSpecFunctions, inv:LPlusState->bool, relation:(LPlusState, HState)->bool, refinement_relation:RefinementRelation ) requires lasf == LAtomic_GetSpecFunctions() requires hasf == HAtomic_GetSpecFunctions() requires inv == InductiveInv requires relation == LiftingRelation requires refinement_relation == GetLPlusHRefinementRelation() ensures forall ls, hs :: inv(ls) && relation(ls, hs) ==> RefinementPair(ls, hs) in refinement_relation { forall ls, hs | inv(ls) && relation(ls, hs) ensures RefinementPair(ls, hs) in refinement_relation { } } "; pgp.AddLemma(str); } protected virtual void GenerateLiftLAtomicToHAtomicLemma(bool skippable, bool introducible) { string str = @" lemma lemma_LiftLAtomicToHAtomic() returns (refinement_relation:RefinementRelation) ensures SpecRefinesSpec(AtomicSpec(LAtomic_GetSpecFunctions()), AtomicSpec(HAtomic_GetSpecFunctions()), refinement_relation) ensures refinement_relation == GetLPlusHRefinementRelation() { var lasf := LAtomic_GetSpecFunctions(); var hasf := HAtomic_GetSpecFunctions(); var inv := InductiveInv; var relation := LiftingRelation; refinement_relation := GetLPlusHRefinementRelation(); "; if (introducible) { str += @" var progress_measure := ProgressMeasure; lemma_EstablishAtomicPathsLiftable(lasf, hasf, inv, relation, progress_measure); "; } else { str += @" lemma_EstablishAtomicPathsLiftable(lasf, hasf, inv, relation); "; } str += @" lemma_EstablishInitRequirements(lasf, hasf, inv, relation); lemma_EstablishStateOKRequirement(lasf, hasf, relation); lemma_EstablishRelationRequirement(lasf, hasf, inv, relation, refinement_relation); "; if (skippable) { if (introducible) { str += @" lemma_LiftAtomicToAtomicGivenAtomicPathsLiftableGeneral(lasf, hasf, inv, relation, progress_measure, refinement_relation); "; } else { str += @" lemma_LiftAtomicToAtomicGivenAtomicPathsSkippablyLiftable(lasf, hasf, inv, relation, refinement_relation); "; } } else if (introducible) { str += @" lemma_LiftAtomicToAtomicGivenAtomicPathsIntroduciblyLiftable(lasf, hasf, inv, relation, progress_measure, refinement_relation); "; } else { str += @" lemma_LiftAtomicToAtomicGivenAtomicPathsLiftable(lasf, hasf, inv, relation, refinement_relation); "; } str += "}\n"; pgp.AddLemma(str); } protected void GenerateGenericAtomicPropertyLemmas() { string str; str = $@" lemma lemma_LAtomic_AtomicInitImpliesOK() ensures AtomicInitImpliesOK(LAtomic_GetSpecFunctions()) {{ }} "; pgp.AddLemma(str); str = $@" lemma lemma_LAtomic_AtomicInitImpliesInv() ensures AtomicInitImpliesInv(LAtomic_GetSpecFunctions(), InductiveInv) {{ forall s | LAtomic_GetSpecFunctions().init(s) ensures InductiveInv(s) {{ lemma_InitImpliesInductiveInv(s); }} }} "; pgp.AddLemma(str); str = $@" lemma lemma_LAtomic_AtomicPathPreservesInv() ensures AtomicPathPreservesInv(LAtomic_GetSpecFunctions(), InductiveInv) {{ var asf := LAtomic_GetSpecFunctions(); forall s, path, tid | InductiveInv(s) && asf.path_valid(s, path, tid) ensures InductiveInv(asf.path_next(s, path, tid)) {{ var s' := asf.path_next(s, path, tid); lemma_AtomicPathMaintainsInductiveInv(s, s', path, tid); }} }} "; pgp.AddLemma(str); } protected virtual void GenerateFinalProof() { pgp.MainProof.AddInclude(ArmadaOptions.O.ArmadaCommonDefsPath + "/Armada/strategies/refinement/RefinementConvolution.i.dfy"); pgp.MainProof.AddImport("RefinementConvolutionModule"); string str = @" lemma lemma_ProveRefinement() ensures SpecRefinesSpec(L.Armada_Spec(), H.Armada_Spec(), GetLHRefinementRelation()) { var rr1 := lemma_LSpecRefinesLPlusSpec(); var rr2 := lemma_LiftToAtomic(); var rr3 := lemma_LiftLAtomicToHAtomic(); var rr4 := lemma_LiftFromAtomic(); lemma_SpecRefinesSpecQuadrupleConvolution( L.Armada_Spec(), Armada_SpecFunctionsToSpec(LPlus_GetSpecFunctions()), AtomicSpec(LAtomic_GetSpecFunctions()), AtomicSpec(HAtomic_GetSpecFunctions()), H.Armada_Spec(), rr1, rr2, rr3, rr4, GetLHRefinementRelation() ); } "; pgp.AddLemma(str); } //////////////////////////////////////////////////////////////////////// /// PC functions //////////////////////////////////////////////////////////////////////// protected virtual void GeneratePCFunctions(bool low) { var c = low ? "L" : "H"; var symbols = low ? pgp.symbolsLow : pgp.symbolsHigh; var methodNames = symbols.AllMethods.AllMethodNames; string str; str = $"datatype {c}MethodName = " + String.Join(" | ", methodNames.Select(x => $"{c}MethodName_{x}")); pgp.AddDatatype(str, "specs"); str = $@" function MethodToInstructionCount_{c}(m:{c}MethodName) : (v:int) ensures v >= 0 {{ match m "; foreach (var methodName in methodNames) { var methodInfo = symbols.AllMethods.LookupMethod(methodName); str += $" case {c}MethodName_{methodName} => {methodInfo.NumPCs}\n"; } str += " }\n"; pgp.AddFunction(str, "specs"); var pcs = new List(); symbols.AllMethods.AppendAllPCs(pcs); var maxInstructionCount = pcs.Select(pc => pc.instructionCount).Max(); str = $@" function PCToMethod_{c}(pc:{c}.Armada_PC) : {c}MethodName {{ match pc "; foreach (var pc in pcs) { str += $" case {pc} => {c}MethodName_{pc.methodName}\n"; } str += " }\n"; pgp.AddFunction(str, "specs"); str = $@" function MaxPCInstructionCount_{c}() : int {{ { maxInstructionCount } }} "; pgp.AddFunction(str, "specs"); str = $@" function PCToInstructionCount_{c}(pc:{c}.Armada_PC) : (v:int) ensures 0 <= v <= MaxPCInstructionCount_{c}() {{ match pc "; foreach (var pc in pcs) { str += $" case {pc} => {pc.instructionCount}\n"; } str += " }\n"; pgp.AddFunction(str, "specs"); str = $@" lemma lemma_PCInstructionCountLessThanMethodInstructionCount_{c}(pc:{c}.Armada_PC) ensures 0 <= PCToInstructionCount_{c}(pc) < MethodToInstructionCount_{c}(PCToMethod_{c}(pc)) {{ }} "; pgp.AddLemma(str, "specs"); str = $@" predicate StackMatchesMethod_{c}(frame:{c}.Armada_StackFrame, m:{c}MethodName) {{ match m "; foreach (var methodName in methodNames) { str += $" case {c}MethodName_{methodName} => frame.Armada_StackFrame_{methodName}?\n"; } str += " }\n"; pgp.AddPredicate(str, "specs"); } protected virtual void GeneratePCFunctions_L() { GeneratePCFunctions(true); } protected virtual void GeneratePCFunctions_H() { GeneratePCFunctions(false); } protected void AddStackMatchesMethodInvariant() { string str; str = @" predicate StackMatchesMethodInv(s:LPlusState) { forall tid :: tid in s.s.threads ==> var pc := s.s.threads[tid].pc; StackMatchesMethod_L(s.s.threads[tid].top, PCToMethod_L(pc)) } "; pgp.AddPredicate(str, "defs"); var inv = new InternalInvariantInfo("StackMatchesMethodInv", "StackMatchesMethodInv", new List()); invariants.Add(inv); } } } ================================================ FILE: Source/Armada/Armada.atg ================================================ /*----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //-----------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------- // Armada // Rustan Leino, first created 25 January 2008 //--------------------------------------------------------------------------*/ using System.Collections.Generic; using System.Numerics; using Microsoft.Boogie; using System.IO; using System.Text; COMPILER Armada /*--------------------------------------------------------------------------*/ readonly Expression/*!*/ dummyExpr; readonly AssignmentRhs/*!*/ dummyRhs; readonly FrameExpression/*!*/ dummyFrameExpr; readonly Statement/*!*/ dummyStmt; readonly Statement/*!*/ dummyIfStmt; readonly Include theInclude; readonly ModuleDecl theModule; readonly BuiltIns theBuiltIns; readonly bool theVerifyThisFile; int anonymousIds = 0; /// /// Holds the modifiers given for a declaration /// /// Not all modifiers are applicable to all kinds of declarations. /// Errors are given when a modify does not apply. /// We also record the tokens for the specified modifiers so that /// they can be used in error messages. /// struct DeclModifierData { public bool IsAbstract; public IToken AbstractToken; public bool IsGhost; public IToken GhostToken; public bool IsStatic; public IToken StaticToken; public bool IsProtected; public IToken ProtectedToken; public bool IsNoAddr; public IToken NoAddrToken; } // Check that token has not been set, then set it. public void CheckAndSetToken(ref IToken token) { if (token != null) { SemErr(t, "Duplicate declaration modifier: " + t.val); } token = t; } /// // A flags type used to tell what declaration modifiers are allowed for a declaration. /// [Flags] enum AllowedDeclModifiers { None = 0, Abstract = 1, Ghost = 2, // Means ghost not allowed because already implicitly ghost. AlreadyGhost = 4, Static = 8, Protected = 16, NoAddr = 32 }; /// /// Check the declaration modifiers against those that are allowed. /// /// The 'allowed' parameter specifies which declaratio modifiers are allowed. /// The 'declCaption' parameter should be a string describing the kind of declaration. /// It is used in error messages. /// Any declaration modifiers that are present but not allowed are cleared. /// void CheckDeclModifiers(DeclModifierData dmod, string declCaption, AllowedDeclModifiers allowed) { if (dmod.IsAbstract && ((allowed & AllowedDeclModifiers.Abstract) == 0)) { SemErr(dmod.AbstractToken, declCaption + " cannot be declared 'abstract'."); dmod.IsAbstract = false; } if (dmod.IsGhost) { if ((allowed & AllowedDeclModifiers.AlreadyGhost) != 0) { SemErr(dmod.GhostToken, declCaption + " cannot be declared ghost (they are 'ghost' by default)."); dmod.IsGhost = false; } else if ((allowed & AllowedDeclModifiers.Ghost) == 0) { SemErr(dmod.GhostToken, declCaption + " cannot be declared 'ghost'."); dmod.IsGhost = false; } } if (dmod.IsStatic && ((allowed & AllowedDeclModifiers.Static) == 0)) { SemErr(dmod.StaticToken, declCaption + " cannot be declared 'static'."); dmod.IsStatic = false; } if (dmod.IsProtected && ((allowed & AllowedDeclModifiers.Protected) == 0)) { SemErr(dmod.ProtectedToken, declCaption + " cannot be declared 'protected'."); dmod.IsProtected = false; } if (dmod.IsNoAddr && ((allowed & AllowedDeclModifiers.NoAddr) == 0)) { SemErr(dmod.NoAddrToken, declCaption + " cannot be declared 'noaddr'."); dmod.IsNoAddr = false; } } /// /// Parses top-level things (modules, classes, datatypes, class members) from "filename" /// and appends them in appropriate form to "module". /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner. /// public static int Parse (string/*!*/ filename, Include include, ModuleDecl module, BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) /* throws System.IO.IOException */ { Contract.Requires(filename != null); Contract.Requires(module != null); string s; if (filename == "stdin.dfy") { s = Microsoft.Boogie.ParserHelper.Fill(System.Console.In, new List()); return Parse(s, filename, filename, include, module, builtIns, errors, verifyThisFile); } else { using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) { s = Microsoft.Boogie.ParserHelper.Fill(reader, new List()); return Parse(s, filename, ArmadaOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, include, module, builtIns, errors, verifyThisFile); } } } /// /// Parses top-level things (modules, classes, datatypes, class members) /// and appends them in appropriate form to "module". /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner. /// public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, ErrorReporter reporter, bool verifyThisFile=true) { Contract.Requires(s != null); Contract.Requires(filename != null); Contract.Requires(module != null); Errors errors = new Errors(reporter); return Parse(s, fullFilename, filename, null, module, builtIns, errors, verifyThisFile); } public static Parser SetupParser(string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, Include include, ModuleDecl module, BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) { Contract.Requires(s != null); Contract.Requires(filename != null); Contract.Requires(module != null); Contract.Requires(errors != null); byte[]/*!*/ buffer = cce.NonNull( UTF8Encoding.Default.GetBytes(s)); MemoryStream ms = new MemoryStream(buffer,false); Scanner scanner = new Scanner(ms, errors, fullFilename, filename); return new Parser(scanner, errors, include, module, builtIns, verifyThisFile); } public static Expression ParseExpression(string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, Include include, ModuleDecl module, BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) { Parser parser = SetupParser(s, fullFilename, filename, include, module, builtIns, errors, verifyThisFile); parser.la = new Token(); parser.la.val = ""; parser.Get(); Expression e; parser.Expression(out e, true, true, true); return e; } // This has to return Declaration because it might return a TopLevelDecl or a MemberDecl public static Declaration ParseTopLevelDecl(string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, Include include, ModuleDecl module, BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) { Parser parser = SetupParser(s, fullFilename, filename, include, module, builtIns, errors, verifyThisFile); parser.la = new Token(); parser.la.val = ""; parser.Get(); var defaultClassMembers = new List(); var moduleDef = new ModuleDefinition(Token.NoToken, "DummyModule", new List(), false, false, false, Token.NoToken, null, null, false); parser.TopDecl(moduleDef, defaultClassMembers, true, false); if (moduleDef.TopLevelDecls.Count == 1) { return moduleDef.TopLevelDecls[0]; } else if (defaultClassMembers.Count == 1) { return defaultClassMembers[0]; } else { errors.SemErr(Token.NoToken, "ParseTopLevelDecl got wrong number of declarations"); return null; } } /// /// Parses top-level things (modules, classes, datatypes, class members) /// and appends them in appropriate form to "module". /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner with the given Errors sink. /// public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, Include include, ModuleDecl module, BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) { Parser parser = SetupParser(s, fullFilename, filename, include, module, builtIns, errors, verifyThisFile); parser.Parse(); return parser.errors.ErrorCount; } public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, Include include, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) : this(scanner, errors) // the real work { // initialize readonly fields dummyExpr = new LiteralExpr(Token.NoToken); dummyRhs = new ExprRhs(dummyExpr, null); dummyFrameExpr = new FrameExpression(dummyExpr.tok, dummyExpr, null); dummyStmt = new ReturnStmt(Token.NoToken, Token.NoToken, null); var dummyBlockStmt = new BlockStmt(Token.NoToken, Token.NoToken, new List()); dummyIfStmt = new IfStmt(Token.NoToken, Token.NoToken, false, null, dummyBlockStmt, null); theInclude = include; // the "include" that includes this file theModule = module; theBuiltIns = builtIns; theVerifyThisFile = verifyThisFile; } bool IsLabel(bool allowLabel) { if (!allowLabel) { return false; } scanner.ResetPeek(); IToken x = scanner.Peek(); return (la.kind == _ident || la.kind == _digits) && x.kind == _colon; } bool IsAlternative() { IToken x = scanner.Peek(); return (la.kind == _lbrace && x.kind == _case) || la.kind == _case; } bool FollowedByColon() { IToken x = la; while (x.kind == _ident || x.kind == _openparen) x = scanner.Peek(); return x.kind == _colon; } bool IsStaticArrayBracket() { return la.kind == _lbracket; } bool StarFollowedByCommaSemiOrOpenBrace() { if (la.kind != _star) { return false; } Token x = scanner.Peek(); return (x.kind == _comma || x.kind == _semi || x.kind == _lbrace); } bool IsGets() { return la.kind == _gets; } // an existential guard starts with an identifier and is then followed by // * a colon (if the first identifier is given an explicit type), // * a comma (if there's a list of bound variables and the first one is not given an explicit type), // * a start-attribute (if there's one bound variable and it is not given an explicit type and there are attributes), or // * a bored smiley (if there's one bound variable and it is not given an explicit type). bool IsExistentialGuard() { scanner.ResetPeek(); if (la.kind == _ident) { Token x = scanner.Peek(); if (x.kind == _colon || x.kind == _comma || x.kind == _boredSmiley || x.kind == _lbracecolon) { return true; } } return false; } bool IsLoopSpec() { return la.kind == _invariant || la.kind == _decreases || la.kind == _modifies; } bool IsWitness() { scanner.ResetPeek(); if (la.kind == _witness) { return true; } else if (la.kind == _ghost) { Token x = scanner.Peek(); return x.kind == _witness; } return false; } bool IsFunctionDecl() { scanner.ResetPeek(); switch (la.kind) { case _function: case _predicate: case _copredicate: return true; case _inductive: return scanner.Peek().kind != _lemma; case _twostate: var x = scanner.Peek(); return x.kind == _function || x.kind == _predicate; default: return false; } } bool IsParenStar() { scanner.ResetPeek(); Token x = scanner.Peek(); return la.kind == _openparen && x.kind == _star; } bool IsEquivOp() { return la.val == "<==>" || la.val == "\u21d4"; } bool IsImpliesOp() { return la.val == "==>" || la.val == "\u21d2"; } bool IsExpliesOp() { return la.val == "<==" || la.val == "\u21d0"; } bool IsAndOp() { return la.val == "&&" || la.val == "\u2227"; } bool IsOrOp() { return la.val == "||" || la.val == "\u2228"; } bool IsBitwiseAndOp() { return la.val == "&"; } bool IsBitwiseOrOp() { return la.val == "|"; } bool IsBitwiseXorOp() { return la.val == "^"; } bool IsBitwiseOp() { return IsBitwiseAndOp() || IsBitwiseOrOp() || IsBitwiseXorOp(); } bool IsAs() { return la.kind == _as; } bool IsRelOp() { return la.val == "==" || la.val == "<" || la.val == ">" || la.val == "<=" || la.val == ">=" || la.val == "!=" || la.val == "in" || la.kind == _notIn || la.val =="!" || la.val == "\u2260" || la.val == "\u2264" || la.val == "\u2265"; } bool IsShiftOp() { if (la.kind == _openAngleBracket) { } else if (la.kind == _closeAngleBracket) { } else { return false; } scanner.ResetPeek(); var x = scanner.Peek(); if (x.kind != la.kind) { return false; } return x.pos == la.pos + 1; // return true only if the tokens are adjacent to each other } bool IsAddOp() { return la.val == "+" || la.val == "-"; } bool IsMulOp() { return la.kind == _star || la.val == "/" || la.val == "%"; } bool IsQSep() { return la.kind == _doublecolon || la.kind == _bullet; } bool IsNonFinalColon() { return la.kind == _colon && scanner.Peek().kind != _rbracket; } bool IsMapDisplay() { scanner.ResetPeek(); return la.kind == _map && scanner.Peek().kind == _lbracket; } bool IsIMapDisplay() { scanner.ResetPeek(); return la.kind == _imap && scanner.Peek().kind == _lbracket; } bool IsISetDisplay() { scanner.ResetPeek(); return la.kind == _iset && scanner.Peek().kind == _lbrace; } bool IsSuffix() { return la.kind == _dot || la.kind == _lbracket || la.kind == _openparen; } string UnwildIdent(string x, bool allowWildcardId) { if (x.StartsWith("_")) { if (allowWildcardId && x.Length == 1) { return "_v" + anonymousIds++; } else { SemErr("cannot declare identifier beginning with underscore"); } } return x; } bool IsLambda(bool allowLambda) { if (!allowLambda) { return false; } scanner.ResetPeek(); Token x; // peek at what might be a signature of a lambda expression if (la.kind == _ident) { // cool, that's the entire candidate signature } else if (la.kind != _openparen) { return false; // this is not a lambda expression } else { int identCount = 0; x = scanner.Peek(); while (x.kind != _closeparen) { if (identCount != 0) { if (x.kind != _comma) { return false; // not the signature of a lambda } x = scanner.Peek(); } if (x.kind != _ident) { return false; // not a lambda expression } identCount++; x = scanner.Peek(); if (x.kind == _colon) { // a colon belongs only in a lamdba signature, so this must be a lambda (or something ill-formed) return true; } } } // What we have seen so far could have been a lambda signature or could have been some // other expression (in particular, an identifier, a parenthesized identifier, or a // tuple all of whose subexpressions are identifiers). // It is a lambda expression if what follows is something that must be a lambda. x = scanner.Peek(); return x.kind == _darrow || x.kind == _reads || x.kind == _requires; } bool IsIdentParen() { scanner.ResetPeek(); Token x = scanner.Peek(); return la.kind == _ident && x.kind == _openparen; } /* Used to disambiguate the LHS of a VarDeclStmt. If it looks like the start of a CasePattern, * we consider it to be a LetStmt. But if we are looking at a simple identifier, then we * consider it to be a VarDeclStmt. */ bool IsLetStmt() { return IsIdentParen() || la.kind == _openparen; } bool IsIdentColonOrBar() { Token x = scanner.Peek(); return la.kind == _ident && (x.kind == _colon || x.kind == _verticalbar); } bool SemiFollowsCall(bool allowSemi, Expression e) { return allowSemi && la.kind == _semi && (e is ApplySuffix || (e is RevealExpr && (((RevealExpr)e).Expr is ApplySuffix))); } bool IsNotEndOfCase() { return la.kind != _EOF && la.kind != _rbrace && la.kind != _case; } /* The following is the largest lookahead there is. It needs to check if what follows * can be nothing but "<" Type { "," Type } ">". */ bool IsGenericInstantiation(bool inExpressionContext) { scanner.ResetPeek(); if (!inExpressionContext) { return la.kind == _openAngleBracket; } IToken pt = la; if (!IsTypeList(ref pt)) { return false; } /* There are ambiguities in the parsing. For example: * F( a < b , c > (d) ) * can either be a unary function F whose argument is a function "a" with type arguments "" and * parameter "d", or can be a binary function F with the two boolean arguments "a < b" and "c > (d)". * To make the situation a little better, we (somewhat heuristically) look at the character that * follows the ">". Note that if we, contrary to a user's intentions, pick "a" out as a function * with a type instantiation, the user can disambiguate it by making sure the ">" sits inside some * parentheses, like: * F( a < b , (c > (d)) ) */ switch (pt.kind) { case _dot: // here, we're sure it must have been a type instantiation we saw, because an expression cannot begin with dot case _openparen: // it was probably a type instantiation of a function/method case _lbracket: // it is possible that it was a type instantiation case _lbrace: // it was probably a type instantiation of a function/method // In the following cases, we're sure we must have read a type instantiation that just ended an expression case _closeparen: case _rbracket: case _rbrace: case _comma: case _semi: case _then: case _else: case _case: case _eq: case _neq: case _neqAlt: case _as: case _by: case _in: case _openAngleBracket: case _closeAngleBracket: case _EOF: // (specification clauses that can follow an expression) case _decreases: case _modifies: case _reads: case _requires: case _ensures: case _invariant: case _witness: // (top-level declarations that can follow an expression) case _function: case _predicate: case _inductive: case _twostate: case _lemma: case _copredicate: case _ghost: case _static: case _protected: case _import: case _export: case _class: case _trait: case _datatype: case _codatatype: case _var: case _const: case _newtype: case _type: case _iterator: case _method: case _colemma: case _constructor: return true; default: return false; } } /* Returns true if the next thing is of the form: * "<" Type { "," Type } ">" */ bool IsTypeList(ref IToken pt) { if (pt.kind != _openAngleBracket) { return false; } pt = scanner.Peek(); return IsTypeSequence(ref pt, _closeAngleBracket); } /* Returns true if the next thing is of the form: * Type { "," Type } * followed by an endBracketKind. */ bool IsTypeSequence(ref IToken pt, int endBracketKind) { while (true) { if (!IsType(ref pt)) { return false; } if (pt.kind == endBracketKind) { // end of type list pt = scanner.Peek(); return true; } else if (pt.kind == _comma) { // type list continues pt = scanner.Peek(); } else { // not a type list return false; } } } bool IsType(ref IToken pt) { switch (pt.kind) { case _bool: case _char: case _nat: case _int: case _real: case _ORDINAL: case _string: case _object_q: case _object: pt = scanner.Peek(); return true; case _arrayToken: case _arrayToken_q: case _bvToken: case _set: case _iset: case _multiset: case _seq: case _map: case _imap: pt = scanner.Peek(); return pt.kind != _openAngleBracket || IsTypeList(ref pt); case _ident: while (true) { // invariant: next token is an ident pt = scanner.Peek(); if (pt.kind == _openAngleBracket && !IsTypeList(ref pt)) { return false; } if (pt.kind != _dot) { // end of the type return true; } pt = scanner.Peek(); // get the _dot if (pt.kind != _ident) { return false; } } case _openparen: pt = scanner.Peek(); if (pt.kind == _closeparen) { // end of type list pt = scanner.Peek(); return true; } return IsTypeSequence(ref pt, _closeparen); default: return false; } } void ConvertKeywordTokenToIdent() { var oldKind = la.kind; la.kind = _ident; // call CheckLiteral with la var origT = t; t = la; scanner.CheckLiteral(); t = origT; if (la.kind != _ident) { // it has been changed by CheckLiteral, which means it was a keyword la.kind = _ident; // convert it to an ident } else { // la was something other than a keyword la.kind = oldKind; } } int StringToInt(string s, int defaultValue, string errString) { Contract.Requires(s != null); Contract.Requires(errString != null); try { if (s != "") { defaultValue = int.Parse(s); } } catch (System.OverflowException) { SemErr(string.Format("sorry, {0} ({1}) are not supported", errString, s)); } return defaultValue; } /*--------------------------------------------------------------------------*/ CHARACTERS letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". digit = "0123456789". posDigit = "123456789". hexdigit = "0123456789ABCDEFabcdef". special = "'_?". glyph = "`~!@#$%^&*()-_=+[{]}|;:',<.>/?\\". cr = '\r'. lf = '\n'. tab = '\t'. space = ' '. nondigit = letter + special. idchar = nondigit + digit. nonidchar = ANY - idchar. /* exclude the characters in 'array' and '\'' */ nondigitMinusABTick = nondigit - 'a' - 'b' - '\''. nondigitMinusQuery = nondigit - '?'. idcharMinusA = idchar - 'a'. idcharMinusR = idchar - 'r'. idcharMinusY = idchar - 'y'. idcharMinusV = idchar - 'v'. idcharMinusPosDigitMinusQuery = idchar - posDigit - '?'. idcharMinusTick = idchar - '\''. /* string literals */ charChar = ANY - '\'' - '\\' - cr - lf. stringChar = ANY - '"' - '\\' - cr - lf. verbatimStringChar = ANY - '"'. /*------------------------------------------------------------------------*/ TOKENS ident = nondigitMinusABTick {idchar} /* if char 0 is not an 'a' or '\'', then anything else is fine */ | 'a' [ idcharMinusR {idchar} ] /* if char 0 is an 'a', then either there is no char 1 or char 1 is not an 'r' */ | 'a' 'r' [ idcharMinusR {idchar} ] /* etc. */ | 'a' 'r' 'r' [ idcharMinusA {idchar} ] | 'a' 'r' 'r' 'a' [ idcharMinusY {idchar} ] | 'a' 'r' 'r' 'a' 'y' idcharMinusPosDigitMinusQuery {idchar} | 'a' 'r' 'r' 'a' 'y' '?' idchar {idchar} | 'a' 'r' 'r' 'a' 'y' posDigit {digit} nondigitMinusQuery {idchar} | 'a' 'r' 'r' 'a' 'y' posDigit {digit} '?' idchar {idchar} | 'b' [ idcharMinusV {idchar} ] | 'b' 'v' [ nondigit {idchar} ] | 'b' 'v' '0' idchar {idchar} | 'b' 'v' posDigit {idchar} nondigit {idchar} | "'" [ idchar ] /* if char 0 is a '\'' and length is 1 or 2, then it is an identifier */ | "'" idchar idcharMinusTick /* if char 0 is '\'' and length is 3, then it is an identifier provided char 2 is not '\'' */ | "'" idchar idchar idchar { idchar } /* if char 0 is '\'' and length exceeds 3, then it is an identifier */ . digits = digit {['_'] digit}. hexdigits = "0x" hexdigit {['_'] hexdigit}. decimaldigits = digit {['_'] digit} '.' digit {['_'] digit}. arrayToken = "array" [posDigit {digit}]. arrayToken_q = "array" [posDigit {digit}] '?'. bvToken = "bv" ( '0' | posDigit {digit} ). bool = "bool". char = "char". int = "int". nat = "nat". real = "real". ORDINAL = "ORDINAL". object = "object". object_q = "object?". string = "string". set = "set". iset = "iset". multiset = "multiset". seq = "seq". map = "map". imap = "imap". charToken = "'" ( charChar | "\\\'" | "\\\"" | "\\\\" | "\\0" | "\\n" | "\\r" | "\\t" | "\\u" hexdigit hexdigit hexdigit hexdigit ) "'". stringToken = '"' { stringChar | "\\\'" | "\\\"" | "\\\\" | "\\0" | "\\n" | "\\r" | "\\t" | "\\u" hexdigit hexdigit hexdigit hexdigit } '"' | '@' '"' { verbatimStringChar | "\"\"" } '"'. colon = ':'. comma = ','. verticalbar = '|'. doublecolon = "::". gets = ":=". boredSmiley = ":|". bullet = '\u2022'. dot = '.'. backtick = "`". semi = ';'. darrow = "=>". assume = "assume". calc = "calc". case = "case". then = "then". else = "else". as = "as". by = "by". in = "in". decreases = "decreases". invariant = "invariant". function = "function". predicate = "predicate". inductive = "inductive". twostate = "twostate". copredicate = "copredicate". lemma = "lemma". static = "static". protected = "protected". import = "import". export = "export". class = "class". trait = "trait". datatype = "datatype". codatatype = "codatatype". var = "var". const = "const". newtype = "newtype". type = "type". iterator = "iterator". method = "method". colemma = "colemma". constructor = "constructor". modifies = "modifies". reads = "reads". requires = "requires". ensures = "ensures". ghost = "ghost". witness = "witness". lbracecolon = "{:". lbrace = '{'. rbrace = '}'. lbracket = '['. rbracket = ']'. openparen = '('. closeparen = ')'. openAngleBracket = '<'. closeAngleBracket = '>'. eq = "==". neq = "!=". neqAlt = '\u2260'. star = '*'. notIn = "!in" CONTEXT (nonidchar). ellipsis = "...". reveal = "reveal". module = "module". commit = "commit". COMMENTS FROM "/*" TO "*/" NESTED COMMENTS FROM "//" TO lf IGNORE cr + lf + tab /*------------------------------------------------------------------------*/ PRODUCTIONS Armada = (. List membersDefaultClass = new List(); // to support multiple files, create a default module only if theModule is null DefaultModuleDecl defaultModule = (DefaultModuleDecl)((LiteralModuleDecl)theModule).ModuleDef; // theModule should be a DefaultModuleDecl (actually, the singular DefaultModuleDecl) Contract.Assert(defaultModule != null); .) { "include" stringToken (. { string parsedFile = scanner.FullFilename; bool isVerbatimString; string s = t.val; string includedFile = Util.RemoveParsedStringQuotes(s, out isVerbatimString); includedFile = Util.RemoveEscaping(includedFile, isVerbatimString); string fullPath = includedFile; if (!Path.IsPathRooted(includedFile)) { string basePath = Path.GetDirectoryName(parsedFile); includedFile = Path.Combine(basePath, includedFile); fullPath = Path.GetFullPath(includedFile); } defaultModule.Includes.Add(new Include(t, parsedFile, includedFile, fullPath, s)); } .) } { TopDecl } (. // find the default class in the default module, then append membersDefaultClass to its member list DefaultClassDecl defaultClass = null; foreach (TopLevelDecl topleveldecl in defaultModule.TopLevelDecls) { defaultClass = topleveldecl as DefaultClassDecl; if (defaultClass != null) { defaultClass.Members.AddRange(membersDefaultClass); break; } } if (defaultClass == null) { // create the default class here, because it wasn't found defaultClass = new DefaultClassDecl(defaultModule, membersDefaultClass); defaultModule.TopLevelDecls.Add(defaultClass); } .) EOF . DeclModifier = ( "abstract" (. dmod.IsAbstract = true; CheckAndSetToken(ref dmod.AbstractToken); .) | "ghost" (. dmod.IsGhost = true; CheckAndSetToken(ref dmod.GhostToken); .) | "static" (. dmod.IsStatic = true; CheckAndSetToken(ref dmod.StaticToken); .) | "protected" (. dmod.IsProtected = true; CheckAndSetToken(ref dmod.ProtectedToken); .) | "noaddr" (. dmod.IsNoAddr = true; CheckAndSetToken(ref dmod.NoAddrToken); .) ) . TopDecl<. ModuleDefinition module, List membersDefaultClass, bool isTopLevel, bool isAbstract .> = (. DeclModifierData dmod = new DeclModifierData(); ModuleDecl submodule; ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter; TraitDecl/*!*/ trait; ArmadaProofDecl proofDecl; RefinementConstraintDecl refinementConstraintDecl; .) { DeclModifier } ( SubModuleDecl (. var litmod = submodule as LiteralModuleDecl; if (litmod != null && litmod.ModuleDef.PrefixIds.Count != 0) { var tup = new Tuple, LiteralModuleDecl>(litmod.ModuleDef.PrefixIds, litmod); module.PrefixNamedModules.Add(tup); } else { module.TopLevelDecls.Add(submodule); } .) | ClassDecl (. module.TopLevelDecls.Add(c); .) | DatatypeDecl (. module.TopLevelDecls.Add(dt); .) | NewtypeDecl (. module.TopLevelDecls.Add(td); .) | OtherTypeDecl (. module.TopLevelDecls.Add(td); .) | IteratorDecl (. module.TopLevelDecls.Add(iter); .) | TraitDecl (. module.TopLevelDecls.Add(trait); .) /* NOTE (Luke): upstream Dafny now forbids mutable member declaration as direct children field of a module, which is what we want in Dafny. I enabled it here, but don't know if it will cause other problems. */ | ClassMemberDecl | RefinementConstraintDecl (. module.TopLevelDecls.Add(refinementConstraintDecl); .) | ArmadaProofDecl (. module.TopLevelDecls.Add(proofDecl); .) ) . SubModuleDecl = (. Attributes attrs = null; IToken/*!*/ id; var prefixIds = new List(); List namedModuleDefaultClassMembers = new List();; List idPath, idExports; IToken idRefined = null; ModuleDefinition module; submodule = null; // appease compiler bool isAbstract = dmod.IsAbstract; bool isProtected = dmod.IsProtected; bool opened = false; ArmadaModuleType moduleType = ArmadaModuleType.NotArmada; IToken abstracts = null; List reduces = null; IToken structsModuleName = null; CheckDeclModifiers(dmod, "Modules", AllowedDeclModifiers.Abstract | AllowedDeclModifiers.Protected); .) (( "module" | "layer" (. moduleType = ArmadaModuleType.ArmadaLevel; .) | "level" (. moduleType = ArmadaModuleType.ArmadaLevel; .) | "structs" (. moduleType = ArmadaModuleType.ArmadaStructs; theBuiltIns.CreateArrowTypeDecl(0); /* We always need arrow types 0-3 for the Armada heap */ theBuiltIns.CreateArrowTypeDecl(1); theBuiltIns.CreateArrowTypeDecl(2); theBuiltIns.CreateArrowTypeDecl(3); .) | "proof" (. moduleType = ArmadaModuleType.ArmadaProof; .) ) { Attribute } NoUSIdent { (. prefixIds.Add(id); .) "." NoUSIdent } ["using" NoUSIdent ] [ "refines" ModuleName ] (. module = new ModuleDefinition(id, id.val, new List(), isAbstract, isProtected, false, idRefined, parent, attrs, false, moduleType, structsModuleName, abstracts, reduces); module.IsToBeVerified = theVerifyThisFile; AliasModuleDecl aliasDecl; if (moduleType == ArmadaModuleType.ArmadaStructs || moduleType == ArmadaModuleType.ArmadaLevel) { /* Add "import opened ArmadaCommonDefinitions" */ Token commonModule = new Token(); commonModule.val = "ArmadaCommonDefinitions"; aliasDecl = new AliasModuleDecl(new List{commonModule}, commonModule, module, true, new List()); module.TopLevelDecls.Add(aliasDecl); } if (structsModuleName != null) { /* Add "import opened " */ aliasDecl = new AliasModuleDecl(new List{structsModuleName}, structsModuleName, module, true, new List()); module.TopLevelDecls.Add(aliasDecl); } .) "{" (. module.BodyStartTok = t; .) { TopDecl} "}" (. module.BodyEndTok = t; module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers)); submodule = new LiteralModuleDecl(module, parent); .) | "import" ["opened" (.opened = true;.)] ModuleName ( (. idPath = new List(); idExports = new List(); .) [ QualifiedModuleExportSuffix ] (. if (idPath.Count > 0) SemErr(idPath[0], "Qualified imports must be given a name."); idPath.Insert(0, id); submodule = new AliasModuleDecl(idPath, id, parent, opened, idExports); .) | "=" QualifiedModuleExport (. submodule = new AliasModuleDecl(idPath, id, parent, opened, idExports); .) | ":" QualifiedModuleExport (. submodule = new ModuleFacadeDecl(idPath, id, parent, opened, idExports); .) ) [ SYNC ";" // This semi-colon used to be required, but it seems silly to have it. // To stage the transition toward not having it at all, let's make it optional for now. Then, // in the next big version of Dafny, don't allow the semi-colon at all. (. errors.Deprecated(t, "the semi-colon that used to terminate a sub-module declaration has been deprecated; in the new syntax, just leave off the semi-colon"); .) ] | (. IToken exportId; List exports = new List();; List extends = new List(); bool provideAll = false; bool revealAll = false; bool isDefault = false; ExportSignature exsig; .) "export" (. exportId = t; .) [ ExportIdent ] { "provides" ( ( ModuleExportSignature (. exports.Add(exsig); .) {"," ModuleExportSignature (. exports.Add(exsig); .) } ) | "*" (. provideAll = true; .) ) | "reveals" ( ( ModuleExportSignature (. exports.Add(exsig); .) {"," ModuleExportSignature (. exports.Add(exsig); .) } ) | "*" (. revealAll = true; .) ) | "extends" ExportIdent (. extends.Add(id.val); .) {"," ExportIdent (. extends.Add(id.val); .) } } (. if (exportId.val == "export" || exportId.val == parent.Name) { isDefault = true; } submodule = new ModuleExportDecl(exportId, parent, exports, extends, provideAll, revealAll, isDefault); .) ) . ModuleExportSignature = (. IToken prefix; IToken suffix = null; .) TypeNameOrCtorSuffix [ "." TypeNameOrCtorSuffix ] (. if (suffix != null) { exsig = new ExportSignature(prefix, prefix.val, suffix, suffix.val, opaque); } else { exsig = new ExportSignature(prefix, prefix.val, opaque); } .) . /* The only modules in any given scope are siblings, so we no longer need qualified lookups */ ModuleName = Ident . /* This production is for referring to module exports for imports (both normal and facades) */ QualifiedModuleExportSuffix<.List ids, List exports.> = (. IToken id; .) ( "." ModuleName (. ids.Add(id); .) { "." ModuleName (. ids.Add(id); .) } | "`" ( ExportIdent (. exports.Add(id); .) | "{" ExportIdent (. exports.Add(id); .) { "," ExportIdent (. exports.Add(id); .) } "}" ) ) . QualifiedModuleExport<.out List ids, out List exports.> = (. IToken id; ids = new List(); List sids = new List(); exports = new List(); .) ModuleName (. ids.Add(id); .) [ QualifiedModuleExportSuffix ] (. ids.AddRange(sids); .) . ClassDecl = (. Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ id; Type trait = null; List/*!*/ traits = new List(); Attributes attrs = null; List typeArgs = new List(); List members = new List(); IToken bodyStart; CheckDeclModifiers(dmodClass, "Classes", AllowedDeclModifiers.None); DeclModifierData dmod; .) SYNC "struct" { Attribute } NoUSIdent [ GenericParameters ] ["extends" Type (. traits.Add(trait); .) {"," Type (. traits.Add(trait); .) } ] "{" (. bodyStart = t; .) { (. dmod = new DeclModifierData(); .) { DeclModifier } ClassMemberDecl } "}" (. c = new ClassDecl(id, id.val, module, typeArgs, members, attrs, traits); c.BodyStartTok = bodyStart; c.BodyEndTok = t; .) . TraitDecl = (. Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out trait) != null); CheckDeclModifiers(dmodIn, "Traits", AllowedDeclModifiers.None); IToken/*!*/ id; Attributes attrs = null; List typeArgs = new List(); //traits should not support type parameters at the moment List members = new List(); IToken bodyStart; DeclModifierData dmod; .) SYNC "trait" { Attribute } NoUSIdent [ GenericParameters ] "{" (. bodyStart = t; .) { (. dmod = new DeclModifierData(); .) { DeclModifier } ClassMemberDecl } "}" (. trait = new TraitDecl(id, id.val, module, typeArgs, members, attrs); trait.BodyStartTok = bodyStart; trait.BodyEndTok = t; .) . GlobalInvariantDecl = (. Contract.Ensures(Contract.ValueAtReturn(out globalInvariant) != null); CheckDeclModifiers(dModIn, "Global invariants", AllowedDeclModifiers.None); IToken id; Expression body; IToken bodyStart; IToken bodyEnd; .) SYNC "invariant" NoUSIdent FunctionBody (. globalInvariant = new GlobalInvariantDecl(id, id.val, null, body); .) . YieldPredicateDecl = (. Contract.Ensures(Contract.ValueAtReturn(out yieldPredicate) != null); CheckDeclModifiers(dModIn, "Yield predicates", AllowedDeclModifiers.None); IToken id; Expression body; IToken bodyStart; IToken bodyEnd; .) SYNC "yield_predicate" NoUSIdent FunctionBody (. yieldPredicate = new YieldPredicateDecl(id, id.val, null, body); .) . UniversalStepConstraintDecl = (. Contract.Ensures(Contract.ValueAtReturn(out stepConstraint) != null); CheckDeclModifiers(dModIn, "Yield predicates", AllowedDeclModifiers.None); IToken id; Expression body; IToken bodyStart; IToken bodyEnd; string code; bool isVerbatimString; stepConstraint = null; .) SYNC "universal_step_constraint" NoUSIdent ( FunctionBody (. stepConstraint = new UniversalStepConstraintDecl(id, id.val, null, body); .) | stringToken (. code = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString); stepConstraint = new UniversalStepConstraintDecl(id, id.val, null, code); .) ) . ArmadaProofDecl = (. Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out proofDecl) != null); CheckDeclModifiers(dModIn, "Strategies", AllowedDeclModifiers.None); Attributes attrs = null; if (module.ModuleType != ArmadaModuleType.ArmadaProof) { SemErr("Strategies only supported in Armada refinement proofs"); } IToken x; IToken id, id2, id3; List ids, ids2; List> ranges, ranges2; List usedItems; string includedFile, usedFile; string code, code2, code3; proofDecl = null; .) ( "refinement" (. x = t; .) NoUSIdent NoUSIdent (. proofDecl = new RefinementParametersDecl(x, module, id, id2); .) | "include_file" (. x = t; .) stringToken (. bool isVerbatimString; includedFile = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString); includedFile = Util.RemoveEscaping(includedFile, isVerbatimString); usedItems = new List(); .) [ "which_includes" stringToken (. usedFile = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString); usedFile = Util.RemoveEscaping(usedFile, isVerbatimString); usedItems.Add(usedFile); .) {"," stringToken (. usedFile = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString); usedFile = Util.RemoveEscaping(usedFile, isVerbatimString); usedItems.Add(usedFile); .) } ] (. proofDecl = new ImportFileArmadaProofDecl(x, module, includedFile, usedItems); .) | "import_module" (. x = t; .) NoUSIdent (. usedItems = new List(); .) [ "which_imports" NoUSIdent (. usedItems.Add(id2.val); .) {"," NoUSIdent (. usedItems.Add(id2.val); .) } ] (. proofDecl = new ImportModuleArmadaProofDecl(x, module, id.val, usedItems); .) | "extra" (. x = t; .) NoUSIdent stringToken (. proofDecl = new ExtraMaterialArmadaProofDecl(x, module, id.val, t.val); .) | "inductive_invariant" (. x = t; code = null; ids = new List(); .) { Attribute } NoUSIdent [ stringToken (. code = t.val; .) ] [ "depends_on" NoUSIdent (. ids.Add(id2); .) { "," NoUSIdent (. ids.Add(id2); .) } ] (. proofDecl = new InductiveInvariantArmadaProofDecl(x, module, id, code, ids, attrs); .) | "use_regions" (. proofDecl = new UseRegionsArmadaProofDecl(t, module); .) | "use_address_invariant" (. proofDecl = new UseAddressInvariantArmadaProofDecl(t, module); .) | "chl_invariant" (. x = t; code = null; .) { Attribute } NoUSIdent [ stringToken (. code = t.val; .) ] (. proofDecl = new CHLInvariantArmadaProofDecl(x, module, id, code, attrs); .) | "chl_local_invariant" (. x = t; code = null; .) { Attribute } NoUSIdent NoUSIdent [ stringToken (. code = t.val; .) ] (. proofDecl = new CHLLocalInvariantArmadaProofDecl(x, module, id, id2, code, attrs); .) | "chl_yield_pred" (. x = t; code = null; .) { Attribute } NoUSIdent [ stringToken (. code = t.val; .) ] (. proofDecl = new CHLYieldPredicateArmadaProofDecl(x, module, id, code, attrs); .) | "chl_precondition" (. x = t; code = null; .) { Attribute } NoUSIdent NoUSIdent [ stringToken (. code = t.val; .) ] (. proofDecl = new CHLPreconditionArmadaProofDecl(x, module, id, id2, code, attrs); .) | "chl_postcondition" (. x = t; code = null; .) { Attribute } NoUSIdent NoUSIdent [ stringToken (. code = t.val; .) ] (. proofDecl = new CHLPostconditionArmadaProofDecl(x, module, id, id2, code, attrs); .) | "chl_loop_modifies" (. x = t; code = null; .) { Attribute } NoUSIdent NoUSIdent [ stringToken (. code = t.val; .) ] (. proofDecl = new CHLLoopModifiesClauseArmadaProofDecl(x, module, id, id2, code, attrs); .) | "auxiliary" (. x = t; .) NoUSIdent stringToken (. code = t.val; .) stringToken (. code2 = t.val; .) stringToken (. code3 = t.val; .) stringToken (. proofDecl = new AuxiliaryArmadaProofDecl(x, module, id.val, code, code2, code3, t.val); .) | "weakening" (. x = t; ids = new List(); .) [ LabelIdent (. ids.Add(id); .) { "," LabelIdent (. ids.Add(id); .) } ] (. proofDecl = new WeakeningStrategyDecl(x, module, ids); .) | "starweakening" (. x = t; ids = new List(); ids2 = new List(); .) [ "statements" LabelIdent (. ids.Add(id); .) { "," LabelIdent (. ids.Add(id); .) } ] [ "variables" LabelIdent (. ids2.Add(id); .) { "," LabelIdent (. ids2.Add(id); .) } ] (. proofDecl = new StarWeakeningStrategyDecl(x, module, ids, ids2); .) | "var_hiding" (. x = t; ids = new List(); .) NoUSIdent (. ids.Add(id); .) { "," NoUSIdent (. ids.Add(id); .) } (. proofDecl = new GlobalVariableHidingStrategyDecl(x, module, ids); .) | "stack_var_hiding" (. x = t; ids = new List(); .) NoUSIdent NoUSIdent (. ids.Add(id2); .) { "," NoUSIdent (. ids.Add(id2); .) } (. proofDecl = new StackVariableHidingStrategyDecl(x, module, id, ids); .) | "var_intro" (. x = t; ids = new List(); .) NoUSIdent (. ids.Add(id); .) { "," NoUSIdent (. ids.Add(id); .) } (. proofDecl = new GlobalVariableIntroStrategyDecl(x, module, ids); .) | "stack_var_intro" (. x = t; code = null; .) NoUSIdent NoUSIdent [ stringToken (. code = t.val; .) ] (. proofDecl = new StackVariableIntroStrategyDecl(x, module, id, id2, code); .) | "reduction" (. x = t; ranges = new List>(); ranges2 = new List>(); .) [ "phase1" NoUSIdent (. id2 = id; .) [ "-" NoUSIdent ] (. ranges.Add(new Tuple(id.val, id2.val)); .) { "," NoUSIdent (. id2 = id; .) [ "-" NoUSIdent ] (. ranges.Add(new Tuple(id.val, id2.val)); .) } ] [ "phase2" NoUSIdent (. id2 = id; .) [ "-" NoUSIdent ] (. ranges2.Add(new Tuple(id.val, id2.val)); .) { "," NoUSIdent (. id2 = id; .) [ "-" NoUSIdent ] (. ranges2.Add(new Tuple(id.val, id2.val)); .) } ] (. proofDecl = new ReductionStrategyDecl(x, module, ranges, ranges2); .) | "combining" (. x = t; .) NoUSIdent NoUSIdent NoUSIdent (. proofDecl = new CombiningStrategyDecl(x, module, id, id2, id3); .) | "field_hiding" (. x = t; .) NoUSIdent NoUSIdent (. proofDecl = new FieldHidingStrategyDecl(x, module, id, id2); .) | "field_intro" (. x = t; .) NoUSIdent NoUSIdent (. proofDecl = new FieldIntroStrategyDecl(x, module, id, id2); .) | "assume_intro" (. x = t; ids = new List(); .) [ LabelIdent (. ids.Add(id); .) { "," LabelIdent (. ids.Add(id); .) } ] (. proofDecl = new AssumeIntroStrategyDecl(x, module, ids); .) | "chl" (. proofDecl = new ConcurrentHoareLogicStrategyDecl(t, module); .) | "critsec" (. x = t; .) NoUSIdent (. proofDecl = new CriticalSectionStrategyDecl(x, module, id); .) | "tso_elim" (. x = t; .) PeriodSeparatedIdentifierList stringToken (. proofDecl = new TSOEliminationStrategyDecl(x, module, ids, t.val); .) ) . RefinementConstraintDecl = (. Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out refinementConstraintDecl) != null); IToken x; .) ( "refinement_constraint" (. x = t; .) stringToken (. refinementConstraintDecl = new RefinementConstraintDecl(x, module, t.val); .) ) . PeriodSeparatedIdentifierList<.out List ids.> = (. IToken id; ids = new List(); .) NoUSIdent (. ids.Add(id); .) { "." NoUSIdent (. ids.Add(id); .) } . ClassMemberDecl<. DeclModifierData dmod, List mm, bool allowConstructors, bool isValueType, bool moduleLevelDecl, bool isWithinAbstractModule.> = (. Contract.Requires(cce.NonNullElements(mm)); Method/*!*/ m; Function/*!*/ f; GlobalInvariantDecl globalInvariant; YieldPredicateDecl yieldPredicate; UniversalStepConstraintDecl stepConstraint; .) ( (. if (moduleLevelDecl) { SemErr(la, "fields are not allowed to be declared at the module level; instead, wrap the field in a 'class' declaration"); dmod.IsStatic = false; } .) FieldDecl | ConstantFieldDecl | IF(IsFunctionDecl()) (. if (moduleLevelDecl && dmod.StaticToken != null) { errors.Warning(dmod.StaticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here"); dmod.IsStatic = false; } .) FunctionDecl (. mm.Add(f); .) | (. if (moduleLevelDecl && dmod.StaticToken != null) { errors.Warning(dmod.StaticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here"); dmod.IsStatic = false; } .) MethodDecl (. mm.Add(m); .) | GlobalInvariantDecl (. mm.Add(globalInvariant); .) | YieldPredicateDecl (. mm.Add(yieldPredicate); .) | UniversalStepConstraintDecl (. mm.Add(stepConstraint); .) ) . DatatypeDecl = (. Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out dt)!=null); IToken/*!*/ id; Attributes attrs = null; List typeArgs = new List(); List ctors = new List(); IToken bodyStart = Token.NoToken; // dummy assignment bool co = false; CheckDeclModifiers(dmod, "Datatypes or codatatypes", AllowedDeclModifiers.None); var members = new List(); .) SYNC ( "datatype" | "codatatype" (. co = true; .) ) { Attribute } NoUSIdent [ GenericParameters ] "=" (. bodyStart = t; .) [ "|" ] DatatypeMemberDecl { "|" DatatypeMemberDecl } [ TypeMembers ] (. if (co) { dt = new CoDatatypeDecl(id, id.val, module, typeArgs, ctors, members, attrs); } else { dt = new IndDatatypeDecl(id, id.val, module, typeArgs, ctors, members, attrs); } dt.BodyStartTok = bodyStart; dt.BodyEndTok = t; .) . DatatypeMemberDecl<.List/*!*/ ctors.> = (. Contract.Requires(cce.NonNullElements(ctors)); Attributes attrs = null; IToken/*!*/ id; List formals = new List(); .) { Attribute } NoUSIdent [ FormalsOptionalIds ] (. ctors.Add(new DatatypeCtor(id, id.val, formals, attrs)); .) . TypeMembers<. ModuleDefinition/*!*/ module, List members .> = (. DeclModifierData dmod; .) "{" { (. dmod = new DeclModifierData(); .) { DeclModifier } ClassMemberDecl } "}" . FieldDecl<.DeclModifierData dmod, bool isValueType, List mm.> = (. Contract.Requires(cce.NonNullElements(mm)); Attributes attrs = null; IToken/*!*/ id; Type/*!*/ ty; Expression initialValue = null; CheckDeclModifiers(dmod, "Fields", AllowedDeclModifiers.Ghost | AllowedDeclModifiers.NoAddr | AllowedDeclModifiers.Static); if (isValueType) { // we're about to produce an error; put fields into a throw-away list, so we don't return them mm = new List(); } .) SYNC "var" (. if (isValueType) { SemErr(t, "mutable fields are now allowed in value types"); } .) { Attribute } FIdentType [ ":=" Expression ] (. mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs, dmod.IsNoAddr, dmod.IsStatic, initialValue)); .) { "," FIdentType [ ":=" Expression ] (. mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs, dmod.IsNoAddr, dmod.IsStatic, initialValue)); .) } OldSemi . ConstantFieldDecl<.DeclModifierData dmod, List/*!*/ mm, bool moduleLevelDecl.> = (. Contract.Requires(cce.NonNullElements(mm)); Attributes attrs = null; IToken/*!*/ id; Type/*!*/ ty; Expression e = null; if (moduleLevelDecl && dmod.StaticToken != null) { errors.Warning(dmod.StaticToken, "module-level const declarations are always non-instance, so the 'static' keyword is not allowed here"); dmod.IsStatic = false; } CheckDeclModifiers(dmod, "Fields", AllowedDeclModifiers.Ghost | AllowedDeclModifiers.Static | AllowedDeclModifiers.NoAddr); .) SYNC "const" { Attribute } CIdentType (. if (ty == null) { ty = new InferredTypeProxy(); } .) [ ":=" Expression ] (. if (e == null && ty is InferredTypeProxy) { SemErr(id, "a const declaration must have a type or a RHS value"); } mm.Add(new ConstantField(id, id.val, e, dmod.IsStatic, dmod.IsGhost, ty, attrs)); .) OldSemi . NewtypeDecl = (. IToken id, bvId; Attributes attrs = null; td = null; Type baseType = null; Expression constraint; Expression witness = null; bool witnessIsGhost = false; CheckDeclModifiers(dmod, "Newtypes", AllowedDeclModifiers.None); var members = new List(); .) "newtype" { Attribute } NoUSIdent "=" ( IF(IsIdentColonOrBar()) NoUSIdent [ ":" Type ] (. if (baseType == null) { baseType = new InferredTypeProxy(); } .) "|" Expression [ IF(IsWitness()) [ "ghost" (. witnessIsGhost = true; .) ] "witness" Expression ] [ TypeMembers ] (. var witnessKind = witness == null ? SubsetTypeDecl.WKind.None : witnessIsGhost ? SubsetTypeDecl.WKind.Ghost : SubsetTypeDecl.WKind.Compiled; td = new NewtypeDecl(id, id.val, module, new BoundVar(bvId, bvId.val, baseType), constraint, witnessKind, witness, members, attrs); .) | Type [ TypeMembers ] (. td = new NewtypeDecl(id, id.val, module, baseType, members, attrs); .) ) . OtherTypeDecl = (. IToken id, bvId; Attributes attrs = null; var characteristics = new TypeParameter.TypeParameterCharacteristics(false); var typeArgs = new List(); td = null; Type ty = null; Expression constraint; Expression witness = null; bool witnessIsGhost = false; var kind = "Opaque type"; .) "type" { Attribute } NoUSIdent { TypeParameterCharacteristics } [ GenericParameters ] [ "=" ( IF(IsIdentColonOrBar()) NoUSIdent [ ":" Type ] (. if (ty == null) { ty = new InferredTypeProxy(); } .) "|" Expression [ IF(IsWitness()) [ "ghost" (. witnessIsGhost = true; .) ] "witness" Expression ] (. var witnessKind = witness == null ? SubsetTypeDecl.WKind.None : witnessIsGhost ? SubsetTypeDecl.WKind.Ghost : SubsetTypeDecl.WKind.Compiled; td = new SubsetTypeDecl(id, id.val, characteristics, typeArgs, module, new BoundVar(bvId, bvId.val, ty), constraint, witnessKind, witness, attrs); kind = "Subset type"; .) | Type (. td = new TypeSynonymDecl(id, id.val, characteristics, typeArgs, module, ty, attrs); kind = "Type synonym"; .) ) ] (. if (td == null) { if (module is DefaultModuleDecl) { // opaque type declarations at the very outermost program scope get an automatic (!new) characteristics.DisallowReferenceTypes = true; } td = new OpaqueTypeDecl(id, id.val, module, characteristics, typeArgs, attrs); } .) (. CheckDeclModifiers(dmod, kind, AllowedDeclModifiers.None); .) [ SYNC ";" // This semi-colon used to be required, but it seems silly to have it. // To stage the transition toward not having it at all, let's make it optional for now. Then, // in the next big version of Dafny, don't allow the semi-colon at all. (. errors.Deprecated(t, "the semi-colon that used to terminate an opaque-type declaration has been deprecated; in the new syntax, just leave off the semi-colon"); .) ] . GIdentType /* isGhost always returns as false if allowGhostKeyword is false */ = (. Contract.Ensures(Contract.ValueAtReturn(out id)!=null); Contract.Ensures(Contract.ValueAtReturn(out ty)!=null); isGhost = false; isOld = allowNewKeyword; .) { "ghost" (. if (allowGhostKeyword) { isGhost = true; } else { SemErr(t, "formal cannot be declared 'ghost' in this context"); } .) | "new" (. if (allowNewKeyword) { isOld = false; } else { SemErr(t, "formal cannot be declared 'new' in this context"); } .) } IdentType . FIdentType = (.Contract.Ensures(Contract.ValueAtReturn(out id) != null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null); id = Token.NoToken; .) ( WildIdent | digits (. id = t; .) ) ":" Type . CIdentType = (.Contract.Ensures(Contract.ValueAtReturn(out id) != null); id = Token.NoToken; ty = null; .) ( WildIdent | digits (. id = t; .) ) [ ":" Type ] . IdentType = (.Contract.Ensures(Contract.ValueAtReturn(out id) != null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null);.) WildIdent ":" Type . LocalIdentTypeOptional = (. IToken id; Type ty; Type optType = null; .) WildIdent [ ":" Type (. optType = ty; .) ] (. var = new LocalVariable(id, id, id.val, optType == null ? new InferredTypeProxy() : optType, isGhost, isNoAddr); .) . IdentTypeOptional = (. Contract.Ensures(Contract.ValueAtReturn(out var) != null); IToken id; Type ty; Type optType = null; .) WildIdent [ ":" Type (. optType = ty; .) ] (. var = new BoundVar(id, id.val, optType == null ? new InferredTypeProxy() : optType); .) . TypeIdentOptional = (.Contract.Ensures(Contract.ValueAtReturn(out id)!=null); Contract.Ensures(Contract.ValueAtReturn(out ty)!=null); Contract.Ensures(Contract.ValueAtReturn(out identName)!=null); string name = null; id = Token.NoToken; ty = new BoolType()/*dummy*/; isGhost = false; .) [ "ghost" (. isGhost = true; .) ] ( TypeAndToken [ ":" (. /* try to convert ty to an identifier */ UserDefinedType udt = ty as UserDefinedType; if (udt != null && udt.TypeArgs.Count == 0) { name = udt.Name; } else { SemErr(id, "invalid formal-parameter name in datatype constructor"); } .) Type ] | digits (. id = t; name = id.val;.) ":" Type ) (. if (name != null) { identName = name; } else { identName = "#" + anonymousIds++; } .) . /*------------------------------------------------------------------------*/ IteratorDecl = (. Contract.Ensures(Contract.ValueAtReturn(out iter) != null); IToken/*!*/ id; Attributes attrs = null; List/*!*/ typeArgs = new List(); List ins = new List(); List outs = new List(); List reads = new List(); List mod = new List(); List decreases = new List(); List req = new List(); List ens = new List(); List yieldReq = new List(); List yieldEns = new List(); List dec = new List(); Attributes readsAttrs = null; Attributes modAttrs = null; Attributes decrAttrs = null; BlockStmt body = null; IToken signatureEllipsis = null; IToken bodyStart = Token.NoToken; IToken bodyEnd = Token.NoToken; CheckDeclModifiers(dmod, "Iterators", AllowedDeclModifiers.None); .) SYNC "iterator" { Attribute } NoUSIdent ( [ GenericParameters ] Formals [ ( "yields" | "returns" (. SemErr(t, "iterators don't have a 'returns' clause; did you mean 'yields'?"); .) ) Formals ] | "..." (. signatureEllipsis = t; .) ) { IteratorSpec } [ BlockStmt ] (. iter = new IteratorDecl(id, id.val, module, typeArgs, ins, outs, new Specification(reads, readsAttrs), new Specification(mod, modAttrs), new Specification(decreases, decrAttrs), req, ens, yieldReq, yieldEns, body, attrs, signatureEllipsis); iter.BodyStartTok = bodyStart; iter.BodyEndTok = bodyEnd; .) . /*------------------------------------------------------------------------*/ GenericParameters<.List/*!*/ typeArgs, bool allowVariance.> = (. Contract.Requires(cce.NonNullElements(typeArgs)); IToken/*!*/ id; TypeParameter.TypeParameterCharacteristics characteristics; TypeParameter.TPVarianceSyntax variance = TypeParameter.TPVarianceSyntax.NonVariant_Strict; // assignment is to please compiler characteristics = new TypeParameter.TypeParameterCharacteristics(false); .) // There's a subtle issue here. The next things in the input could be a '<' followed by an '=' (and then an identifier). // This is legal (provided "allowVariance" is "true"). However, unless there's whitespace between the '<' and '=', the // scanner will read these two characters as a "<=" token. "<" [ Variance (. if (!allowVariance) { SemErr(t, "type-parameter variance is not allowed to be specified in this context"); } .) ] NoUSIdent { TypeParameterCharacteristics } (. typeArgs.Add(new TypeParameter(id, id.val, variance, characteristics)); .) { "," (. variance = TypeParameter.TPVarianceSyntax.NonVariant_Strict; characteristics = new TypeParameter.TypeParameterCharacteristics(false); .) [ Variance (. if (!allowVariance) { SemErr(t, "type-parameter variance is not allowed to be specified in this context"); } .) ] NoUSIdent { TypeParameterCharacteristics } (. typeArgs.Add(new TypeParameter(id, id.val, variance, characteristics)); .) } ">" . Variance = (. variance = TypeParameter.TPVarianceSyntax.NonVariant_Strict; // never used; here just to please the C# compiler .) ( "*" (. variance = TypeParameter.TPVarianceSyntax.Covariant_Permissive; .) | "+" (. variance = TypeParameter.TPVarianceSyntax.Covariant_Strict; .) | "!" (. variance = TypeParameter.TPVarianceSyntax.NonVariant_Permissive; .) | "-" (. variance = TypeParameter.TPVarianceSyntax.Contravariance; .) ) . TypeParameterCharacteristics = "(" TPCharOption { "," TPCharOption } ")" . TPCharOption = ( "==" (. characteristics.EqualitySupport = TypeParameter.EqualitySupportValue.Required; .) | digits (. if (t.val == "0") { characteristics.MustSupportZeroInitialization = true; } else { SemErr(t, "unexpected TPCharOption"); } .) | "!" "new" (. characteristics.DisallowReferenceTypes = true; .) ) . /*------------------------------------------------------------------------*/ MethodDecl = (. Contract.Ensures(Contract.ValueAtReturn(out m) !=null); IToken/*!*/ id = Token.NoToken; bool hasName = false; IToken keywordToken; Attributes attrs = null; List/*!*/ typeArgs = new List(); List ins = new List(); List outs = new List(); List req = new List(); List mod = new List(); List ens = new List(); List dec = new List(); List reads = new List(); List awaits = new List(); List undefinedUnless = new List(); Attributes decAttrs = null; Attributes modAttrs = null; BlockStmt body = null; bool isPlainOlMethod = false; bool isLemma = false; bool isTwoStateLemma = false; bool isConstructor = false; bool isDestructor = false; bool isIndLemma = false; bool isCoLemma = false; IToken signatureEllipsis = null; IToken bodyStart = Token.NoToken; IToken bodyEnd = Token.NoToken; AllowedDeclModifiers allowed = AllowedDeclModifiers.None; string caption = ""; FixpointPredicate.KType kType = FixpointPredicate.KType.Unspecified; .) SYNC ( "method" (. isPlainOlMethod = true; caption = "Methods"; allowed = AllowedDeclModifiers.Ghost | AllowedDeclModifiers.Static; .) | "lemma" (. isLemma = true; caption = "Lemmas"; allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected; .) | "colemma" (. isCoLemma = true; caption = "Colemmas"; allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected; .) | "comethod" (. isCoLemma = true; caption = "Comethods"; allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected; errors.Deprecated(t, "the 'comethod' keyword has been deprecated; it has been renamed to 'colemma'"); .) | "inductive" "lemma" (. isIndLemma = true; caption = "Inductive lemmas"; allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static;.) | "twostate" "lemma" (. isTwoStateLemma = true; caption = "Two-state lemmas"; allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected; .) | "constructor" (. if (allowConstructor) { isConstructor = true; } else { SemErr(t, "constructors are allowed only in classes"); } caption = "Constructors"; allowed = AllowedDeclModifiers.None; .) | "destructor" (. if (allowConstructor) { isDestructor = true; } else { SemErr(t, "destructors are allowed only in classes"); } caption = "Destructors"; allowed = AllowedDeclModifiers.None; .) ) (. keywordToken = t; CheckDeclModifiers(dmod, caption, allowed); .) { Attribute } [ FuMe_Ident (. hasName = true; .) ] (. if (!hasName) { id = keywordToken; if (!isConstructor && !isDestructor) { SemErr(la, "a method must be given a name (expecting identifier)"); } } .) ( [ GenericParameters ] [ KType (. if (!(isCoLemma || isIndLemma)) { SemErr(t, "type of _k can only be specified for inductive lemmas and co-lemmas"); } .) ] (. var isCompilable = (isPlainOlMethod && !dmod.IsGhost) || isConstructor || isDestructor; .) Formals (. if (isDestructor && ins.Count > 0) { SemErr(t, "destructors cannot have in-parameters"); } .) [ "returns" (. if (isConstructor) { SemErr(t, "constructors cannot have out-parameters"); } if (isDestructor) { SemErr(t, "destructors cannot have out-parameters"); } .) Formals ] | "..." (. signatureEllipsis = t; .) ) { MethodSpec } [ IF(isConstructor) (. DividedBlockStmt dividedBody; .) DividedBlockStmt (. body = dividedBody; .) | BlockStmt ] (. if (!isWithinAbstractModule && ArmadaOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { SemErr(t, "a method with an ensures clause must have a body, unless given the :axiom attribute"); } IToken tok = id; if (isConstructor) { m = new Constructor(tok, hasName ? id.val : "_ctor", typeArgs, ins, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), (DividedBlockStmt)body, attrs, signatureEllipsis); } else if (isDestructor) { m = new Destructor(tok, hasName ? id.val : "_dtor", typeArgs, ins, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isIndLemma) { m = new InductiveLemma(tok, id.val, dmod.IsStatic, kType, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isCoLemma) { m = new CoLemma(tok, id.val, dmod.IsStatic, kType, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isLemma) { m = new Lemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isTwoStateLemma) { m = new TwoStateLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else { m = new Method(tok, id.val, dmod.IsStatic, dmod.IsGhost, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), new Specification(reads, null), awaits, undefinedUnless, body, attrs, signatureEllipsis); } m.BodyStartTok = bodyStart; m.BodyEndTok = bodyEnd; .) . KType = "[" ( "nat" (. kType = FixpointPredicate.KType.Nat; .) | "ORDINAL" (. kType = FixpointPredicate.KType.ORDINAL; .) ) "]" . MethodSpec<.List req, List mod, List ens, List decreases, List reads, List awaits, List undefinedUnless, ref Attributes decAttrs, ref Attributes modAttrs, string caption, bool performThisDeprecatedCheck.> = (. Contract.Requires(cce.NonNullElements(req)); Contract.Requires(cce.NonNullElements(mod)); Contract.Requires(cce.NonNullElements(ens)); Contract.Requires(cce.NonNullElements(decreases)); Expression e; FrameExpression fe; bool isFree = false; Attributes ensAttrs = null; Attributes reqAttrs = null; IToken lbl = null; .) SYNC ( "modifies" { Attribute } FrameExpression (. Util.AddFrameExpression(mod, fe, performThisDeprecatedCheck, errors); .) { "," FrameExpression (. Util.AddFrameExpression(mod, fe, performThisDeprecatedCheck, errors); .) } OldSemi | [ "free" (. isFree = true; errors.Deprecated(t, "the 'free' keyword is soon to be deprecated"); .) ] ( "requires" { Attribute } [ IF(IsLabel(true)) LabelIdent ":" ] Expression OldSemi (. req.Add(new MaybeFreeExpression(e, isFree, lbl == null ? null : new AssertLabel(lbl, lbl.val), reqAttrs)); .) | "ensures" { Attribute } Expression OldSemi (. ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs)); .) ) | "decreases" { Attribute } DecreasesList OldSemi | "reads" Expression (. reads.Add(e); .) { "," Expression (. reads.Add(e); .) } OldSemi | "awaits" Expression (. awaits.Add(e); .) { "," Expression (. awaits.Add(e); .) } OldSemi | "undefined_unless" Expression (. undefinedUnless.Add(e); .) { "," Expression (. undefinedUnless.Add(e); .) } OldSemi ) . IteratorSpec<.List/*!*/ reads, List/*!*/ mod, List decreases, List/*!*/ req, List/*!*/ ens, List/*!*/ yieldReq, List/*!*/ yieldEns, ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs.> = (. Expression/*!*/ e; FrameExpression/*!*/ fe; bool isFree = false; bool isYield = false; Attributes ensAttrs = null; IToken lbl = null; .) SYNC ( "reads" { Attribute } FrameExpression (. reads.Add(fe); .) { "," FrameExpression (. reads.Add(fe); .) } OldSemi | "modifies" { Attribute } FrameExpression (. mod.Add(fe); .) { "," FrameExpression (. mod.Add(fe); .) } OldSemi | [ "free" (. isFree = true; errors.Deprecated(t, "the 'free' keyword is soon to be deprecated"); .) ] [ "yield" (. isYield = true; .) ] ( "requires" [ IF(IsLabel(!isYield)) LabelIdent ":" ] Expression OldSemi (. AssertLabel al = lbl == null ? null : new AssertLabel(lbl, lbl.val); if (isYield) { yieldReq.Add(new MaybeFreeExpression(e, isFree, al, null)); } else { req.Add(new MaybeFreeExpression(e, isFree, al, null)); } .) | "ensures" { Attribute } Expression OldSemi (. if (isYield) { yieldEns.Add(new MaybeFreeExpression(e, isFree, ensAttrs)); } else { ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs)); } .) ) | "decreases" { Attribute } DecreasesList OldSemi ) . Formals<.bool incoming, bool allowGhostKeyword, bool allowNewKeyword, List formals.> = (. Contract.Requires(cce.NonNullElements(formals)); IToken id; Type ty; bool isGhost; bool isOld; .) "(" [ GIdentType (. formals.Add(new Formal(id, id.val, ty, incoming, isGhost, isOld)); .) { "," GIdentType (. formals.Add(new Formal(id, id.val, ty, incoming, isGhost, isOld)); .) } ] ")" . FormalsOptionalIds<.List/*!*/ formals.> = (. Contract.Requires(cce.NonNullElements(formals)); IToken/*!*/ id; Type/*!*/ ty; string/*!*/ name; bool isGhost; .) "(" [ TypeIdentOptional (. formals.Add(new Formal(id, name, ty, true, isGhost)); .) { "," TypeIdentOptional (. formals.Add(new Formal(id, name, ty, true, isGhost)); .) } ] ")" . /*------------------------------------------------------------------------*/ Type = (. Contract.Ensures(Contract.ValueAtReturn(out ty) != null); IToken/*!*/ tok; .) TypeAndToken . TypeAndToken = (. Contract.Ensures(Contract.ValueAtReturn(out tok)!=null); Contract.Ensures(Contract.ValueAtReturn(out ty) != null); tok = Token.NoToken; ty = new BoolType(); /*keep compiler happy*/ List gt; List tupleArgTypes = null; BigInteger n; .) ( "bool" (. tok = t; .) | "char" (. tok = t; ty = new CharType(); .) | "int" (. tok = t; ty = new IntType(); .) | "nat" (. tok = t; ty = new UserDefinedType(tok, tok.val, null); .) | "real" (. tok = t; ty = new RealType(); .) | "ORDINAL" (. tok = t; ty = new BigOrdinalType(); .) | bvToken (. tok = t; int w = StringToInt(tok.val.Substring(2), 0, "bitvectors that wide"); ty = new BitvectorType(w); .) | "set" (. tok = t;.) OptGenericInstantiation (. if (gt != null && gt.Count > 1) { SemErr("set type expects only one type argument"); } ty = new SetType(true, gt != null ?gt[0] : null); .) | "iset" (. tok = t; .) OptGenericInstantiation (. if (gt != null && gt.Count > 1) { SemErr("set type expects only one type argument"); } ty = new SetType(false, gt != null ? gt[0] : null); .) | "multiset" (. tok = t; .) OptGenericInstantiation (. if (gt != null && gt.Count > 1) { SemErr("multiset type expects only one type argument"); } ty = new MultiSetType(gt != null ? gt[0] : null); .) | "seq" (. tok = t; .) OptGenericInstantiation (. if (gt != null && gt.Count > 1) { SemErr("seq type expects only one type argument"); } ty = new SeqType(gt != null ? gt[0] : null); .) | "ptr" (. tok = t; .) OptGenericInstantiation (. if (gt != null && gt.Count > 1) { SemErr("seq type expects only one type argument"); } ty = new PointerType(gt != null ? gt[0] : null); .) | "string" (. tok = t; ty = new UserDefinedType(tok, tok.val, null); .) | "object" (. tok = t; ty = new UserDefinedType(tok, tok.val, null); .) | "object?" (. tok = t; ty = new UserDefinedType(tok, tok.val, null); .) | "map" (. tok = t; .) OptGenericInstantiation (. if (gt == null) { ty = new MapType(true, null, null); } else if (gt.Count != 2) { SemErr("map type expects two type arguments"); ty = new MapType(true, gt[0], gt.Count == 1 ? new InferredTypeProxy() : gt[1]); } else { ty = new MapType(true, gt[0], gt[1]); } .) | "imap" (. tok = t; .) OptGenericInstantiation (. if (gt == null) { ty = new MapType(false, null, null); } else if (gt.Count != 2) { SemErr("imap type expects two type arguments"); ty = new MapType(false, gt[0], gt.Count == 1 ? new InferredTypeProxy() : gt[1]); } else { ty = new MapType(false, gt[0], gt[1]); } .) | arrayToken (. tok = t; .) OptGenericInstantiation (. var dimString = tok.val.Substring(5); int dims = StringToInt(dimString, 1, "arrays of that many dimensions"); ty = theBuiltIns.ArrayType(tok, dims, gt, true); .) | arrayToken_q (. tok = t; .) OptGenericInstantiation (. var dimString = tok.val.Substring(5); dimString = dimString.Substring(0, dimString.Length - 1); int dims = StringToInt(dimString, 1, "arrays of that many dimensions"); ty = theBuiltIns.ArrayType(tok, dims, gt, true, true); .) | "(" (. tok = t; tupleArgTypes = new List(); .) [ Type (. tupleArgTypes.Add(ty); .) { "," Type (. tupleArgTypes.Add(ty); .) } ] ")" (. if (tupleArgTypes.Count == 1) { // just return the type 'ty' } else { var dims = tupleArgTypes.Count; var tmp = theBuiltIns.TupleType(tok, dims, true); // make sure the tuple type exists ty = new UserDefinedType(tok, BuiltIns.TupleTypeName(dims), dims == 0 ? null : tupleArgTypes); } .) | (. Expression e; .) NameSegmentForTypeName (. tok = t; .) { "." TypeNameOrCtorSuffix (. List typeArgs; .) OptGenericInstantiation (. e = new ExprDotName(tok, e, tok.val, typeArgs); .) } (. ty = new UserDefinedType(e.tok, e); .) ) [ IF(IsStaticArrayBracket()) "[" Nat "]" (. ty = new SizedArrayType(ty, new LiteralExpr(Token.NoToken, n)); .) ] [ (. int arrowKind = 0; /* 0: any, 1: partial, 2: total */ Type t2; .) ( "~>" (. tok = t; arrowKind = 0; .) | "-->" (. tok = t; arrowKind = 1; .) | "->" (. tok = t; arrowKind = 2; .) ) Type (. if (tupleArgTypes != null) { gt = tupleArgTypes; } else { gt = new List{ ty }; } var arity = gt.Count; theBuiltIns.CreateArrowTypeDecl(arity); if (arrowKind == 0) { ty = new ArrowType(tok, gt, t2); } else { gt.Add(t2); if (arrowKind == 1) { ty = new UserDefinedType(tok, ArrowType.PartialArrowTypeName(arity), gt); } else { ty = new UserDefinedType(tok, ArrowType.TotalArrowTypeName(arity), gt); } } .) ] . OptGenericInstantiation<.out List gt, bool inExpressionContext.> /* NOTE: Coco complains about "OptGenericInstantiation deletable". That's okay. */ = (. gt = null; .) [ IF(IsGenericInstantiation(inExpressionContext)) /* be greedy -- if it looks like a type instantiation, take it */ (. gt = new List(); .) GenericInstantiation ] . GenericInstantiation<.List gt.> = (. Contract.Requires(cce.NonNullElements(gt)); Type/*!*/ ty; .) "<" Type (. gt.Add(ty); .) { "," Type (. gt.Add(ty); .) } ">" . /*------------------------------------------------------------------------*/ FunctionDecl = (. Contract.Ensures(Contract.ValueAtReturn(out f)!=null); Attributes attrs = null; IToken/*!*/ id = Token.NoToken; // to please compiler List typeArgs = new List(); List formals = new List(); List outs = new List(); Formal/*!*/ result = null; Type/*!*/ returnType = new BoolType(); List reqs = new List(); List ens = new List(); List reads = new List(); List decreases; List mod; Expression body = null; bool isPredicate = false; bool isIndPredicate = false; bool isCoPredicate = false; bool isFunctionMethod = false; IToken bodyStart = Token.NoToken; IToken bodyEnd = Token.NoToken; IToken signatureEllipsis = null; bool missingOpenParen; bool isTwoState = false; FixpointPredicate.KType kType = FixpointPredicate.KType.Unspecified; .) /* ----- function ----- */ [ "twostate" (. isTwoState = true; .) ] ( "function" [ "method" (. if (isTwoState) { SemErr(t, "twostate functions are supported only as a ghosts, not as function methods"); } else { isFunctionMethod = true; } .) ] (. AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static; if (!isTwoState) { allowed |= AllowedDeclModifiers.Protected; } string caption = "Functions"; if (isFunctionMethod) { caption = "Function methods"; } CheckDeclModifiers(dmod, caption, allowed); .) { Attribute } FuMe_Ident ( [ GenericParameters ] Formals ":" ( IF(FollowedByColon()) "(" (. IToken resultId; Type ty; bool isGhost; bool isOld; .) GIdentType (. Contract.Assert(!isGhost && !isOld); result = new Formal(resultId, resultId.val, ty, false, false, false); .) ")" | Type ) | "..." (. signatureEllipsis = t; .) ) /* ----- predicate ----- */ | "predicate" (. isPredicate = true; .) [ "method" (. if (isTwoState) { SemErr(t, "twostate predicates are supported only as a ghosts, not as predicate methods"); } else { isFunctionMethod = true; } .) ] (. AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static; if (!isTwoState) { allowed |= AllowedDeclModifiers.Protected; } string caption = "Predicates"; if (isFunctionMethod) { caption = "Predicate methods"; } CheckDeclModifiers(dmod, caption, allowed); .) { Attribute } NoUSIdent ( [ GenericParameters ] (. missingOpenParen = true; .) [ Formals (. missingOpenParen = false; .) ] (. if (missingOpenParen) { errors.Warning(t, "with the new support of higher-order functions in Dafny, parentheses-less predicates are no longer supported; in the new syntax, parentheses are required for the declaration and uses of predicates, even if the predicate takes no additional arguments"); } .) [ ":" (. SemErr(t, "predicates do not have an explicitly declared return type; it is always bool"); .) ] | "..." (. signatureEllipsis = t; .) ) /* ----- inductive predicate ----- */ | (. Contract.Assert(!isTwoState); // the IsFunctionDecl check checks that "twostate" is not followed by "inductive" .) "inductive" "predicate" (. isIndPredicate = true; .) (. CheckDeclModifiers(dmod, "Inductive predicates", AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected); .) { Attribute } FuMe_Ident ( [ GenericParameters ] [ KType ] Formals [ ":" (. SemErr(t, "inductive predicates do not have an explicitly declared return type; it is always bool"); .) ] | "..." (. signatureEllipsis = t; .) ) /* ----- copredicate ----- */ | (. Contract.Assert(!isTwoState); // the IsFunctionDecl check checks that "twostate" is not followed by "copredicate" .) "copredicate" (. isCoPredicate = true; .) (. CheckDeclModifiers(dmod, "Copredicates", AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected); .) { Attribute } NoUSIdent ( [ GenericParameters ] [ KType ] Formals [ ":" (. SemErr(t, "copredicates do not have an explicitly declared return type; it is always bool"); .) ] | "..." (. signatureEllipsis = t; .) ) ) (. decreases = isIndPredicate || isCoPredicate ? null : new List(); .) (. mod = null; .) { FunctionSpec } [ FunctionBody ] (. if (!isWithinAbstractModule && ArmadaOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) { SemErr(t, "a function with an ensures clause must have a body, unless given the :axiom attribute"); } IToken tok = id; if (isTwoState && isPredicate) { f = new TwoStatePredicate(tok, id.val, dmod.IsStatic, typeArgs, formals, reqs, reads, ens, new Specification(decreases, null), body, attrs, signatureEllipsis); } else if (isTwoState) { f = new TwoStateFunction(tok, id.val, dmod.IsStatic, typeArgs, formals, result, returnType, reqs, reads, ens, new Specification(decreases, null), body, attrs, signatureEllipsis); } else if (isPredicate) { f = new Predicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals, reqs, reads, ens, new Specification(decreases, null), body, Predicate.BodyOriginKind.OriginalOrInherited, attrs, signatureEllipsis); } else if (isIndPredicate) { f = new InductivePredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, kType, typeArgs, formals, reqs, reads, ens, body, attrs, signatureEllipsis); } else if (isCoPredicate) { f = new CoPredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, kType, typeArgs, formals, reqs, reads, ens, body, attrs, signatureEllipsis); } else { f = new Function(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals, result, returnType, reqs, reads, ens, new Specification(decreases, null), body, attrs, signatureEllipsis); } f.BodyStartTok = bodyStart; f.BodyEndTok = bodyEnd; theBuiltIns.CreateArrowTypeDecl(formals.Count); if (isIndPredicate || isCoPredicate) { // also create an arrow type for the corresponding prefix predicate theBuiltIns.CreateArrowTypeDecl(formals.Count + 1); } .) . FunctionSpec<.List/*!*/ reqs, List/*!*/ reads, List/*!*/ ens, List decreases, List mod.> = (. Contract.Requires(cce.NonNullElements(reqs)); Contract.Requires(cce.NonNullElements(reads)); Contract.Requires(decreases == null || cce.NonNullElements(decreases)); Expression/*!*/ e; FrameExpression/*!*/ fe; /* NOTE (Luke): I'm not certain about this one... Attributes ensAttrs = null; Attributes reqAttrs = null; .) SYNC ( "requires" { IF(IsAttribute()) Attribute } Expression OldSemi (. reqs.Add(new MaybeFreeExpression(e, false, reqAttrs)); .) */ Attributes ensAttrs = null; Attributes reqAttrs = null; .) SYNC ( "requires" { Attribute } Expression OldSemi (. reqs.Add(new MaybeFreeExpression(e, false, reqAttrs)); .) | "reads" PossiblyWildFrameExpression (. reads.Add(fe); .) { "," PossiblyWildFrameExpression (. reads.Add(fe); .) } OldSemi | "ensures" { Attribute } Expression OldSemi (. ens.Add(new MaybeFreeExpression(e, false, ensAttrs)); .) | "decreases" (. if (decreases == null) { SemErr(t, "'decreases' clauses are meaningless for copredicates, so they are not allowed"); decreases = new List(); } .) DecreasesList OldSemi ) . PossiblyWildExpression = (. Contract.Ensures(Contract.ValueAtReturn(out e)!=null); e = dummyExpr; .) /* A decreases clause on a loop asks that no termination check be performed. * Use of this feature is sound only with respect to partial correctness. */ ( IF(StarFollowedByCommaSemiOrOpenBrace()) "*" (. e = new WildcardExpr(t); .) | Expression ) . PossiblyWildFrameExpression = (. Contract.Ensures(Contract.ValueAtReturn(out fe) != null); fe = dummyFrameExpr; .) /* A reads clause can list a wildcard, which allows the enclosing function to * read anything. In many cases, and in particular in all cases where * the function is defined recursively, this makes it next to impossible to make * any use of the function. Nevertheless, as an experimental feature, the * language allows it (and it is sound). */ ( IF(StarFollowedByCommaSemiOrOpenBrace()) "*" (. fe = new FrameExpression(t, new WildcardExpr(t), null); .) | FrameExpression ) . FrameExpression = (. Contract.Ensures(Contract.ValueAtReturn(out fe) != null); Expression/*!*/ e; IToken/*!*/ id; string fieldName = null; IToken feTok = null; fe = dummyFrameExpr; .) ( Expression (. feTok = e.tok; .) [ "`" Ident (. fieldName = id.val; feTok = id; .) ] (. fe = new FrameExpression(feTok, e, fieldName); .) | "`" Ident (. fieldName = id.val; .) (. fe = new FrameExpression(id, new ImplicitThisExpr(id), fieldName); .) ) . FunctionBody = (. Contract.Ensures(Contract.ValueAtReturn(out e) != null); e = dummyExpr; .) "{" (. bodyStart = t; .) Expression "}" (. bodyEnd = t; .) . /*------------------------------------------------------------------------*/ BlockStmt = (. Contract.Ensures(Contract.ValueAtReturn(out block) != null); List body = new List(); .) "{" (. bodyStart = t; .) { Stmt } "}" (. bodyEnd = t; block = new BlockStmt(bodyStart, bodyEnd, body); .) . DividedBlockStmt = (. Contract.Ensures(Contract.ValueAtReturn(out body) != null); List bodyInit = new List(); IToken separatorTok = null; List bodyProper = new List(); .) "{" (. bodyStart = t; .) { Stmt } [ "new" (. separatorTok = t; .) ";" { Stmt } ] "}" (. bodyEnd = t; .) (. body = new DividedBlockStmt(bodyStart, bodyEnd, bodyInit, separatorTok, bodyProper); .) . ExplicitYieldBlockStmt = (. IToken bodyStart; IToken bodyEnd; List body = new List(); .) ("explicit_yield" | "atomic") "{" (. bodyStart = t; .) { Stmt } "}" (. bodyEnd = t; s = new ExplicitYieldBlockStmt(bodyStart, bodyEnd, body); .) . Stmt<.List/*!*/ ss.> = (. Statement/*!*/ s; .) OneStmt (. ss.Add(s); .) . OneStmt = (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); IToken/*!*/ x; IToken/*!*/ id; string label = null; s = dummyStmt; /* to please the compiler */ BlockStmt bs; IToken bodyStart, bodyEnd; int breakCount; .) SYNC ( BlockStmt (. s = bs; .) | AssertStmt | AssumeStmt | PrintStmt | RevealStmt | SomehowStmt | FenceStmt | GotoStmt | CompareAndSwapStmt | AtomicExchangeStmt | DeallocStmt | CreateThreadStmt | UpdateStmt | VarDeclStatement | IfStmt | WhileStmt | MatchStmt | ForallStmt | CalcStmt | ModifyStmt | "label" LabelIdent ":" OneStmt (. s.Labels = new LList