From efb92f7211582afe0d7de4089da9490287869ac7 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 10 Jul 2024 21:36:42 -0700 Subject: [PATCH 01/52] Make some BoogieGenerator methods public --- .../Verifier/BoogieGenerator.BoogieFactory.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs b/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs index f707a486cd8..f480fd0803e 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs @@ -684,7 +684,7 @@ static Bpl.Expr BplForall(Bpl.IToken tok, List typeParams, : new Bpl.ForallExpr(tok, typeParams, formals, kv, triggers, body, immutable); } - static Bpl.Expr BplAnd(IEnumerable conjuncts) { + public static Bpl.Expr BplAnd(IEnumerable conjuncts) { Contract.Requires(conjuncts != null); Bpl.Expr eq = Bpl.Expr.True; foreach (var c in conjuncts) { @@ -693,7 +693,7 @@ static Bpl.Expr BplAnd(IEnumerable conjuncts) { return eq; } - static Bpl.Expr BplAnd(Bpl.Expr a, Bpl.Expr b) { + public static Bpl.Expr BplAnd(Bpl.Expr a, Bpl.Expr b) { Contract.Requires(a != null); Contract.Requires(b != null); Contract.Ensures(Contract.Result() != null); @@ -712,7 +712,7 @@ static Bpl.Expr BplAnd(Bpl.Expr a, Bpl.Expr b) { } } - static Bpl.Expr BplOr(IEnumerable disjuncts) { + public static Bpl.Expr BplOr(IEnumerable disjuncts) { Contract.Requires(disjuncts != null); Bpl.Expr eq = Bpl.Expr.False; foreach (var d in disjuncts) { @@ -721,7 +721,7 @@ static Bpl.Expr BplOr(IEnumerable disjuncts) { return eq; } - static Bpl.Expr BplOr(Bpl.Expr a, Bpl.Expr b) { + public static Bpl.Expr BplOr(Bpl.Expr a, Bpl.Expr b) { Contract.Requires(a != null); Contract.Requires(b != null); Contract.Ensures(Contract.Result() != null); @@ -742,7 +742,7 @@ static Bpl.Expr BplOr(Bpl.Expr a, Bpl.Expr b) { } } - static Bpl.Expr BplIff(Bpl.Expr a, Bpl.Expr b) { + public static Bpl.Expr BplIff(Bpl.Expr a, Bpl.Expr b) { Contract.Requires(a != null); Contract.Requires(b != null); Contract.Ensures(Contract.Result() != null); @@ -767,7 +767,7 @@ static Bpl.Expr BplIff(Bpl.Expr a, Bpl.Expr b) { } } - static Bpl.Expr BplImp(Bpl.Expr a, Bpl.Expr b) { + public static Bpl.Expr BplImp(Bpl.Expr a, Bpl.Expr b) { Contract.Requires(a != null); Contract.Requires(b != null); Contract.Ensures(Contract.Result() != null); From 655583a7a39d8b139f14541acd9fa0946c335bcc Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 10 Jul 2024 21:38:00 -0700 Subject: [PATCH 02/52] =?UTF-8?q?Add=20=E2=80=94extract=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/DafnyOptions.cs | 1 + Source/DafnyDriver/Commands/VerifyCommand.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/Source/DafnyCore/DafnyOptions.cs b/Source/DafnyCore/DafnyOptions.cs index 6ef92d58f0c..e9716ff2c42 100644 --- a/Source/DafnyCore/DafnyOptions.cs +++ b/Source/DafnyCore/DafnyOptions.cs @@ -320,6 +320,7 @@ public enum ContractTestingMode { public PrintModes PrintMode = PrintModes.Everything; // Default to printing everything public bool DafnyVerify = true; public string DafnyPrintResolvedFile = null; + public string BoogieExtractionTargetFile = null; public List DafnyPrintExportedViews = new List(); public bool Compile = true; public List MainArgs = new List(); diff --git a/Source/DafnyDriver/Commands/VerifyCommand.cs b/Source/DafnyDriver/Commands/VerifyCommand.cs index 293951b034e..516f163ce07 100644 --- a/Source/DafnyDriver/Commands/VerifyCommand.cs +++ b/Source/DafnyDriver/Commands/VerifyCommand.cs @@ -10,6 +10,7 @@ using DafnyCore.Options; using DafnyDriver.Commands; using Microsoft.Boogie; +using Microsoft.Dafny.Compilers; namespace Microsoft.Dafny; @@ -20,6 +21,13 @@ static VerifyCommand() { // they can't be specified when building a doo file. DooFile.RegisterNoChecksNeeded(FilterSymbol, false); DooFile.RegisterNoChecksNeeded(FilterPosition, false); + DooFile.RegisterNoChecksNeeded(ExtractTarget, false); + + DafnyOptions.RegisterLegacyBinding(ExtractTarget, (options, f) => { + options.BoogieExtractionTargetFile = f; + options.ExpandFilename(options.BoogieExtractionTargetFile, x => options.BoogieExtractionTargetFile = x, options.LogPrefix, + options.FileTimestamp); + }); } public static readonly Option FilterSymbol = new("--filter-symbol", @@ -28,6 +36,13 @@ static VerifyCommand() { public static readonly Option FilterPosition = new("--filter-position", @"Filter what gets verified based on a source location. The location is specified as a file path suffix, optionally followed by a colon and a line number. For example, `dafny verify dfyconfig.toml --filter-position=source1.dfy:5` will only verify things that range over line 5 in the file `source1.dfy`. In combination with `--isolate-assertions`, individual assertions can be verified by filtering on the line that contains them. When processing a single file, the filename can be skipped, for example: `dafny verify MyFile.dfy --filter-position=:23`"); + public static readonly Option ExtractTarget = new("--extract", @" +Extract Dafny types, functions, and lemmas to Boogie. +(use - as to output to console.)".TrimStart()) { + IsHidden = true, + ArgumentHelpName = "file", + }; + public static Command Create() { var result = new Command("verify", "Verify the program."); result.AddArgument(DafnyCommands.FilesArgument); @@ -42,6 +57,7 @@ public static Command Create() { new Option[] { FilterSymbol, FilterPosition, + ExtractTarget, DafnyFile.DoNotVerifyDependencies }.Concat(DafnyCommands.VerificationOptions). Concat(DafnyCommands.ConsoleOutputOptions). From 69a233ab91fa49e92d5af8ddc4148928fd5a7f93 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 10 Jul 2024 21:39:01 -0700 Subject: [PATCH 03/52] Add beginning of extractor --- Source/DafnyCore/Backends/Extractor.cs | 134 +++++++++++++++++++ Source/DafnyDriver/Commands/VerifyCommand.cs | 7 + 2 files changed, 141 insertions(+) create mode 100644 Source/DafnyCore/Backends/Extractor.cs diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs new file mode 100644 index 00000000000..e8a0c01a6a9 --- /dev/null +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// +// Copyright by the contributors to the Dafny Project +// SPDX-License-Identifier: MIT +// +//----------------------------------------------------------------------------- + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.IO; +using System.Diagnostics.Contracts; +using DafnyCore; +using JetBrains.Annotations; +using Microsoft.BaseTypes; +using Microsoft.Boogie; +using Microsoft.Dafny; +using Microsoft.Dafny.ProofObligationDescription; +using static Microsoft.Dafny.GeneratorErrors; + +namespace Microsoft.Dafny.Compilers { + public class Extractor : ASTVisitor { + public static Boogie.Program Extract(Program program) { + var extractor = new Extractor(); + extractor.VisitDeclarations(program.DefaultModule.Signature.TopLevels.Values.ToList()); + return extractor.extractedProgram; + } + + private readonly Boogie.Program extractedProgram = new Boogie.Program(); + + private Extractor() { + } + + public override IASTVisitorContext GetContext(IASTVisitorContext astVisitorContext, bool inFunctionPostcondition) { + return astVisitorContext; + } + + protected override void VisitOneDeclaration(TopLevelDecl decl) { + if (decl is ModuleDecl moduleDecl) { + // TODO: look for {:extract} attribute on module + VisitDeclarations(moduleDecl.Signature.TopLevels.Values.ToList()); + return; + } + + if (GetExtractName(decl.Attributes) is { } extractName) { + var ty = new Boogie.TypeCtorDecl(decl.tok, extractName, decl.TypeArgs.Count); + extractedProgram.AddTopLevelDeclaration(ty); + } + + base.VisitOneDeclaration(decl); // this will visit the declaration's members + } + + public override void VisitMethod(Method method) { + if (method is not Lemma lemma) { + return; + } + + var patterns = Attributes.FindAllExpressions(lemma.Attributes, "extract_pattern"); + if (patterns == null) { + return; + } + + Contract.Assert(lemma.TypeArgs.Count == 0); // TODO: fail more gently + Contract.Assert(lemma.Outs.Count == 0); // TODO: fail more gently + + var tok = lemma.tok; + + var boundVars = lemma.Ins.ConvertAll(formal => + (Boogie.Variable)new Boogie.BoundVariable(tok, new TypedIdent(tok, formal.Name, ExtractType(formal.Type))) + ); + + Boogie.Trigger? triggers = null; + for (var i = patterns.Count; 0 <= --i;) { + var terms = patterns[i].ConvertAll(ExtractExpr); + triggers = new Boogie.Trigger(tok, true, terms, triggers); + } + + var ante = BoogieGenerator.BplAnd(lemma.Req.ConvertAll(req => ExtractExpr(req.E))); + var post = BoogieGenerator.BplAnd(lemma.Ens.ConvertAll(ens => ExtractExpr(ens.E))); + var body = BoogieGenerator.BplImp(ante, post); + + var quantifier = new Boogie.ForallExpr(tok, boundVars, triggers, body); + var ax = new Boogie.Axiom(tok, quantifier, $"axiom generated from lemma {method.Name}"); + extractedProgram.AddTopLevelDeclaration(ax); + + // TODO: look for {:extract_attribute "weight", 25} + // TODO: look for {:extract_used_by Empty} + } + + public override void VisitFunction(Function function) { + if (GetExtractName(function.Attributes) is { } extractName) { + var tok = function.tok; + Contract.Assert(function.TypeArgs.Count == 0); // TODO: throw an exception or something more gentle + var inParams = function.Ins.ConvertAll(formal => + (Boogie.Variable)new Boogie.Formal(tok, new TypedIdent(tok, formal.Name, ExtractType(formal.Type)), true) + ); + var result = new Boogie.Formal(tok, new TypedIdent(tok, TypedIdent.NoName, ExtractType(function.ResultType)), false); + var fn = new Boogie.Function(tok, extractName, inParams, result); + extractedProgram.AddTopLevelDeclaration(fn); + } + } + + private Boogie.Type ExtractType(Type type) { + if (type is IntType) { + return Boogie.Type.Int; + } else if (type is BoolType) { + return Boogie.Type.Bool; + } else if (type is UserDefinedType udt) { + var cl = udt.ResolvedClass; + var name = GetExtractName(cl.Attributes) ?? cl.Name; + return new Boogie.UnresolvedTypeIdentifier(Boogie.Token.NoToken, name, udt.TypeArgs.ConvertAll(ExtractType)); + } else { + Contract.Assert(false); // TODO: fail more gently + return null; // to please compiler + } + } + + private string? GetExtractName(Attributes attributes) { + if (Attributes.Find(attributes, "extract_name") is { } extractNameAttribute) { + if (extractNameAttribute.Args.Count == 1 && extractNameAttribute.Args[0] is StringLiteralExpr { Value: string extractName }) { + return extractName; + } + } + return null; + } + + private Boogie.Expr ExtractExpr(Expression expr) { + return Boogie.Expr.True; + } + } +} diff --git a/Source/DafnyDriver/Commands/VerifyCommand.cs b/Source/DafnyDriver/Commands/VerifyCommand.cs index 516f163ce07..3ced4b5e23a 100644 --- a/Source/DafnyDriver/Commands/VerifyCommand.cs +++ b/Source/DafnyDriver/Commands/VerifyCommand.cs @@ -84,6 +84,13 @@ public static async Task HandleVerification(DafnyOptions options) { await verificationSummarized; await verificationResultsLogged; await proofDependenciesReported; + + if (options.BoogieExtractionTargetFile != null) { + using (var engine = ExecutionEngine.CreateWithoutSharedCache(options)) { + var extractedProgram = Extractor.Extract(resolution.ResolvedProgram); + engine.PrintBplFile(options.BoogieExtractionTargetFile, extractedProgram, true, pretty: true); + } + } } return await compilation.GetAndReportExitCode(); From 22f1043760b5a848121e9ab573fa0a95a3c27dd0 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 10 Jul 2024 21:54:41 -0700 Subject: [PATCH 04/52] Add support for :extract_attribute --- Source/DafnyCore/Backends/Extractor.cs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index e8a0c01a6a9..c10c97782f6 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -82,11 +82,28 @@ public override void VisitMethod(Method method) { var post = BoogieGenerator.BplAnd(lemma.Ens.ConvertAll(ens => ExtractExpr(ens.E))); var body = BoogieGenerator.BplImp(ante, post); - var quantifier = new Boogie.ForallExpr(tok, boundVars, triggers, body); + Boogie.QKeyValue kv = null; + var extractAttributes = Attributes.FindAllExpressions(lemma.Attributes, "extract_attribute"); + if (extractAttributes != null) { + for (var i = extractAttributes.Count; 0 <= --i;) { + string? attrName = null; + var parameters = new List(); + foreach (var argument in extractAttributes[i]) { + if (attrName == null) { + attrName = (string)((StringLiteralExpr)argument).Value; // TODO: do error checking + } else { + parameters.Add(ExtractExpr(argument)); + } + } + Contract.Assert(attrName != null); // TODO: fail more gently + kv = new Boogie.QKeyValue(tok, attrName, parameters, kv); + } + } + + var quantifier = new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); var ax = new Boogie.Axiom(tok, quantifier, $"axiom generated from lemma {method.Name}"); extractedProgram.AddTopLevelDeclaration(ax); - // TODO: look for {:extract_attribute "weight", 25} // TODO: look for {:extract_used_by Empty} } From 8a09af08836457e8a5498980e35d10b9e3ce7db8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 10 Jul 2024 22:15:52 -0700 Subject: [PATCH 05/52] Add support for :extract_used_by --- Source/DafnyCore/Backends/Extractor.cs | 36 ++++++++++++++++++++------ 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index c10c97782f6..6b71da479a0 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -26,14 +26,25 @@ public class Extractor : ASTVisitor { public static Boogie.Program Extract(Program program) { var extractor = new Extractor(); extractor.VisitDeclarations(program.DefaultModule.Signature.TopLevels.Values.ToList()); + extractor.FixUpUsedByInformation(); return extractor.extractedProgram; } - private readonly Boogie.Program extractedProgram = new Boogie.Program(); + private readonly Boogie.Program extractedProgram = new(); + private readonly Dictionary functionExtractions = new(); + private readonly List<(Boogie.Axiom, Function)> axiomUsedBy = new(); private Extractor() { } + void FixUpUsedByInformation() { + foreach (var (axiom, function) in axiomUsedBy) { + var boogieFunction = functionExtractions[function]; // TODO: do error checking + boogieFunction.OtherDefinitionAxioms.Add(axiom); + } + axiomUsedBy.Clear(); + } + public override IASTVisitorContext GetContext(IASTVisitorContext astVisitorContext, bool inFunctionPostcondition) { return astVisitorContext; } @@ -59,7 +70,8 @@ public override void VisitMethod(Method method) { } var patterns = Attributes.FindAllExpressions(lemma.Attributes, "extract_pattern"); - if (patterns == null) { + var usedByInfo = Attributes.Find(lemma.Attributes, "extract_used_by"); + if (patterns == null & usedByInfo == null) { return; } @@ -73,8 +85,9 @@ public override void VisitMethod(Method method) { ); Boogie.Trigger? triggers = null; - for (var i = patterns.Count; 0 <= --i;) { - var terms = patterns[i].ConvertAll(ExtractExpr); + Contract.Assert(boundVars.Count != 0 || patterns == null); + for (var i = patterns == null ? 0 : patterns.Count; 0 <= --i;) { + var terms = patterns![i].ConvertAll(ExtractExpr); triggers = new Boogie.Trigger(tok, true, terms, triggers); } @@ -84,6 +97,7 @@ public override void VisitMethod(Method method) { Boogie.QKeyValue kv = null; var extractAttributes = Attributes.FindAllExpressions(lemma.Attributes, "extract_attribute"); + Contract.Assert(boundVars.Count != 0 || extractAttributes == null); if (extractAttributes != null) { for (var i = extractAttributes.Count; 0 <= --i;) { string? attrName = null; @@ -100,11 +114,16 @@ public override void VisitMethod(Method method) { } } - var quantifier = new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); - var ax = new Boogie.Axiom(tok, quantifier, $"axiom generated from lemma {method.Name}"); - extractedProgram.AddTopLevelDeclaration(ax); + var axiomBody = boundVars.Count == 0 ? body : new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); + var axiom = new Boogie.Axiom(tok, axiomBody, $"axiom generated from lemma {method.Name}"); + extractedProgram.AddTopLevelDeclaration(axiom); - // TODO: look for {:extract_used_by Empty} + if (usedByInfo != null) { + Contract.Assert(usedByInfo.Args.Count == 1); + var argument = (MemberSelectExpr)usedByInfo.Args[0].Resolved; // TODO: do error checking + var function = (Function)argument.Member; // TODO: do error checking + axiomUsedBy.Add((axiom, function)); + } } public override void VisitFunction(Function function) { @@ -117,6 +136,7 @@ public override void VisitFunction(Function function) { var result = new Boogie.Formal(tok, new TypedIdent(tok, TypedIdent.NoName, ExtractType(function.ResultType)), false); var fn = new Boogie.Function(tok, extractName, inParams, result); extractedProgram.AddTopLevelDeclaration(fn); + functionExtractions.Add(function, fn); } } From 740cf7411908fa6f087d375ae10e3e5614425bf4 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 10:40:38 -0700 Subject: [PATCH 06/52] Translate expressions --- Source/DafnyCore/Backends/Extractor.cs | 133 +++++++++++++++++++++---- 1 file changed, 111 insertions(+), 22 deletions(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index 6b71da479a0..621556d30b5 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -7,19 +7,12 @@ #nullable enable -using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.IO; using System.Diagnostics.Contracts; -using DafnyCore; -using JetBrains.Annotations; using Microsoft.BaseTypes; using Microsoft.Boogie; -using Microsoft.Dafny; -using Microsoft.Dafny.ProofObligationDescription; -using static Microsoft.Dafny.GeneratorErrors; namespace Microsoft.Dafny.Compilers { public class Extractor : ASTVisitor { @@ -84,6 +77,31 @@ public override void VisitMethod(Method method) { (Boogie.Variable)new Boogie.BoundVariable(tok, new TypedIdent(tok, formal.Name, ExtractType(formal.Type))) ); + var triggers = GetTriggers(tok, boundVars, patterns); + + var ante = BoogieGenerator.BplAnd(lemma.Req.ConvertAll(req => ExtractExpr(req.E))); + var post = BoogieGenerator.BplAnd(lemma.Ens.ConvertAll(ens => ExtractExpr(ens.E))); + var body = BoogieGenerator.BplImp(ante, post); + + Boogie.Expr axiomBody; + if (boundVars.Count == 0) { + axiomBody = body; + } else { + var kv = GetKeyValues(tok, lemma.Attributes); + axiomBody = new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); + } + var axiom = new Boogie.Axiom(tok, axiomBody, $"axiom generated from lemma {method.Name}"); + extractedProgram.AddTopLevelDeclaration(axiom); + + if (usedByInfo != null) { + Contract.Assert(usedByInfo.Args.Count == 1); + var argument = (MemberSelectExpr)usedByInfo.Args[0].Resolved; // TODO: do error checking + var function = (Function)argument.Member; // TODO: do error checking + axiomUsedBy.Add((axiom, function)); + } + } + + private Trigger? GetTriggers(IToken tok, List boundVars, List>? patterns) { Boogie.Trigger? triggers = null; Contract.Assert(boundVars.Count != 0 || patterns == null); for (var i = patterns == null ? 0 : patterns.Count; 0 <= --i;) { @@ -91,13 +109,12 @@ public override void VisitMethod(Method method) { triggers = new Boogie.Trigger(tok, true, terms, triggers); } - var ante = BoogieGenerator.BplAnd(lemma.Req.ConvertAll(req => ExtractExpr(req.E))); - var post = BoogieGenerator.BplAnd(lemma.Ens.ConvertAll(ens => ExtractExpr(ens.E))); - var body = BoogieGenerator.BplImp(ante, post); + return triggers; + } + private QKeyValue? GetKeyValues(IToken tok, Attributes attributes) { Boogie.QKeyValue kv = null; - var extractAttributes = Attributes.FindAllExpressions(lemma.Attributes, "extract_attribute"); - Contract.Assert(boundVars.Count != 0 || extractAttributes == null); + var extractAttributes = Attributes.FindAllExpressions(attributes, "extract_attribute"); if (extractAttributes != null) { for (var i = extractAttributes.Count; 0 <= --i;) { string? attrName = null; @@ -109,21 +126,13 @@ public override void VisitMethod(Method method) { parameters.Add(ExtractExpr(argument)); } } + Contract.Assert(attrName != null); // TODO: fail more gently kv = new Boogie.QKeyValue(tok, attrName, parameters, kv); } } - var axiomBody = boundVars.Count == 0 ? body : new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); - var axiom = new Boogie.Axiom(tok, axiomBody, $"axiom generated from lemma {method.Name}"); - extractedProgram.AddTopLevelDeclaration(axiom); - - if (usedByInfo != null) { - Contract.Assert(usedByInfo.Args.Count == 1); - var argument = (MemberSelectExpr)usedByInfo.Args[0].Resolved; // TODO: do error checking - var function = (Function)argument.Member; // TODO: do error checking - axiomUsedBy.Add((axiom, function)); - } + return kv; } public override void VisitFunction(Function function) { @@ -165,6 +174,86 @@ private Boogie.Type ExtractType(Type type) { } private Boogie.Expr ExtractExpr(Expression expr) { + expr = expr.Resolved; + var tok = expr.tok; + switch (expr) { + case LiteralExpr literalExpr: { + if (literalExpr.Value is bool boolValue) { + return new Boogie.LiteralExpr(tok, boolValue); + } else if (literalExpr.Value is BigInteger intValue) { + var n = BigNum.FromBigInt(intValue); + return Boogie.Expr.Literal(n); + } + break; + } + + case IdentifierExpr identifierExpr: + return new Boogie.IdentifierExpr(tok, identifierExpr.Name); + + case FunctionCallExpr functionCallExpr: { + var function = functionCallExpr.Function; + var functionName = GetExtractName(function.Attributes) ?? function.Name; + Contract.Assert(function.IsStatic); + var arguments = functionCallExpr.Args.ConvertAll(ExtractExpr); + return new Boogie.NAryExpr(tok, new Boogie.FunctionCall(new Boogie.IdentifierExpr(tok, functionName)), arguments); + } + + case BinaryExpr binaryExpr: { + var e0 = ExtractExpr(binaryExpr.E0); + var e1 = ExtractExpr(binaryExpr.E1); + switch (binaryExpr.ResolvedOp) { + case BinaryExpr.ResolvedOpcode.EqCommon: + return Boogie.Expr.Eq(e0, e1); + case BinaryExpr.ResolvedOpcode.NeqCommon: + return Boogie.Expr.Neq(e0, e1); + case BinaryExpr.ResolvedOpcode.Iff: + return BoogieGenerator.BplIff(e0, e1); + case BinaryExpr.ResolvedOpcode.Imp: + return BoogieGenerator.BplImp(e0, e1); + case BinaryExpr.ResolvedOpcode.And: + return BoogieGenerator.BplAnd(e0, e1); + case BinaryExpr.ResolvedOpcode.Or: + return BoogieGenerator.BplOr(e0, e1); + case BinaryExpr.ResolvedOpcode.Le: + return Boogie.Expr.Le(e0, e1); + case BinaryExpr.ResolvedOpcode.Lt: + return Boogie.Expr.Lt(e0, e1); + case BinaryExpr.ResolvedOpcode.Add: + return Boogie.Expr.Add(e0, e1); + case BinaryExpr.ResolvedOpcode.Sub: + return Boogie.Expr.Sub(e0, e1); + default: + break; + } + break; + } + + case UnaryOpExpr unaryOpExpr: { + Contract.Assert(unaryOpExpr.ResolvedOp == UnaryOpExpr.ResolvedOpcode.BoolNot); // TODO: fail more gently + var e = ExtractExpr(unaryOpExpr.E); + return Boogie.Expr.Neg(e); + } + + case QuantifierExpr quantifierExpr: { + // TODO: look for :extract_pattern + + var boundVars = quantifierExpr.BoundVars.ConvertAll(boundVar => + (Boogie.Variable)new Boogie.BoundVariable(tok, new TypedIdent(tok, boundVar.Name, ExtractType(boundVar.Type))) + ); + + var patterns = Attributes.FindAllExpressions(quantifierExpr.Attributes, "extract_pattern"); + Contract.Assert(patterns.Count != 0); // don't support pattern-less quantifiers // TODO: fail more gracefully + var triggers = GetTriggers(tok, boundVars, patterns); + + var kv = GetKeyValues(tok, quantifierExpr.Attributes); + var body = ExtractExpr(quantifierExpr.LogicalBody()); + return new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); + } + + default: + break; + } + Contract.Assert(false, $"ExtractExpr TODO: {expr.GetType()}: {expr}"); // TODO: fail more gently return Boogie.Expr.True; } } From eaf91d82e706b63b2768cbcfa785c2ffbee1f72d Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 11:51:19 -0700 Subject: [PATCH 07/52] Look for :extract on module --- Source/DafnyCore/Backends/Extractor.cs | 31 +++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index 621556d30b5..3a214e3de1a 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -18,12 +18,16 @@ namespace Microsoft.Dafny.Compilers { public class Extractor : ASTVisitor { public static Boogie.Program Extract(Program program) { var extractor = new Extractor(); - extractor.VisitDeclarations(program.DefaultModule.Signature.TopLevels.Values.ToList()); + extractor.VisitModule(program.DefaultModule); extractor.FixUpUsedByInformation(); - return extractor.extractedProgram; + + var extractedProgram = new Boogie.Program(); + extractedProgram.AddTopLevelDeclarations(extractor.allDeclarations); + return extractedProgram; } - private readonly Boogie.Program extractedProgram = new(); + private List declarations = new(); // for the current module + private List allDeclarations = new(); // these are the declarations for all modules marked with {:extract} private readonly Dictionary functionExtractions = new(); private readonly List<(Boogie.Axiom, Function)> axiomUsedBy = new(); @@ -42,16 +46,27 @@ public override IASTVisitorContext GetContext(IASTVisitorContext astVisitorConte return astVisitorContext; } + void VisitModule(ModuleDecl module) { + var previousDeclarations = declarations; + declarations = new(); + + VisitDeclarations(module.Signature.TopLevels.Values.ToList()); + + if (Attributes.Contains(module.Signature.ModuleDef.Attributes, "extract")) { + allDeclarations.AddRange(declarations); + } + declarations = previousDeclarations; + } + protected override void VisitOneDeclaration(TopLevelDecl decl) { if (decl is ModuleDecl moduleDecl) { - // TODO: look for {:extract} attribute on module - VisitDeclarations(moduleDecl.Signature.TopLevels.Values.ToList()); + VisitModule(moduleDecl); return; } if (GetExtractName(decl.Attributes) is { } extractName) { var ty = new Boogie.TypeCtorDecl(decl.tok, extractName, decl.TypeArgs.Count); - extractedProgram.AddTopLevelDeclaration(ty); + declarations.Add(ty); } base.VisitOneDeclaration(decl); // this will visit the declaration's members @@ -91,7 +106,7 @@ public override void VisitMethod(Method method) { axiomBody = new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); } var axiom = new Boogie.Axiom(tok, axiomBody, $"axiom generated from lemma {method.Name}"); - extractedProgram.AddTopLevelDeclaration(axiom); + declarations.Add(axiom); if (usedByInfo != null) { Contract.Assert(usedByInfo.Args.Count == 1); @@ -144,7 +159,7 @@ public override void VisitFunction(Function function) { ); var result = new Boogie.Formal(tok, new TypedIdent(tok, TypedIdent.NoName, ExtractType(function.ResultType)), false); var fn = new Boogie.Function(tok, extractName, inParams, result); - extractedProgram.AddTopLevelDeclaration(fn); + declarations.Add(fn); functionExtractions.Add(function, fn); } } From c8c76aca8c7ada7e7bcbbca47023d56c27dfd989 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 11:51:37 -0700 Subject: [PATCH 08/52] Sort declarations from each module --- Source/DafnyCore/Backends/Extractor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index 3a214e3de1a..b93ca4aefd7 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -53,6 +53,7 @@ void VisitModule(ModuleDecl module) { VisitDeclarations(module.Signature.TopLevels.Values.ToList()); if (Attributes.Contains(module.Signature.ModuleDef.Attributes, "extract")) { + declarations.Sort((d0, d1) => d0.tok.pos - d1.tok.pos); allDeclarations.AddRange(declarations); } declarations = previousDeclarations; From 0f2b6e2ba69175c0a41bc6715fd26961d55646eb Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 11:52:52 -0700 Subject: [PATCH 09/52] Remove debugging comment --- Source/DafnyCore/Backends/Extractor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index b93ca4aefd7..51944973b97 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -106,7 +106,7 @@ public override void VisitMethod(Method method) { var kv = GetKeyValues(tok, lemma.Attributes); axiomBody = new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); } - var axiom = new Boogie.Axiom(tok, axiomBody, $"axiom generated from lemma {method.Name}"); + var axiom = new Boogie.Axiom(tok, axiomBody); declarations.Add(axiom); if (usedByInfo != null) { From 1fc1aaaed6f8459507bb27b28c38cdca8290bab0 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 13:40:11 -0700 Subject: [PATCH 10/52] Add scripts to generate prelude --- Source/DafnyCore/Makefile | 4 + Source/DafnyCore/Prelude/DafnyPrelude.bpl | 1469 +++++++++++++++++ Source/DafnyCore/Prelude/DafnyPreludeCore.bpl | 1290 +++++++++++++++ Source/DafnyCore/Prelude/Makefile | 16 + Source/DafnyCore/Prelude/SequenceModel.dfy | 981 +++++++++++ Source/DafnyCore/Prelude/Sequences.bpl | 177 ++ 6 files changed, 3937 insertions(+) create mode 100644 Source/DafnyCore/Prelude/DafnyPrelude.bpl create mode 100644 Source/DafnyCore/Prelude/DafnyPreludeCore.bpl create mode 100644 Source/DafnyCore/Prelude/Makefile create mode 100644 Source/DafnyCore/Prelude/SequenceModel.dfy create mode 100644 Source/DafnyCore/Prelude/Sequences.bpl diff --git a/Source/DafnyCore/Makefile b/Source/DafnyCore/Makefile index e05f6554ae2..32190f29bb0 100644 --- a/Source/DafnyCore/Makefile +++ b/Source/DafnyCore/Makefile @@ -27,3 +27,7 @@ format: check-format: ../../Scripts/dafny format . --check + +extract: + (cd Prelude; make; cd ..) + cp Prelude/DafnyPrelude.bpl . diff --git a/Source/DafnyCore/Prelude/DafnyPrelude.bpl b/Source/DafnyCore/Prelude/DafnyPrelude.bpl new file mode 100644 index 00000000000..0b17328b65d --- /dev/null +++ b/Source/DafnyCore/Prelude/DafnyPrelude.bpl @@ -0,0 +1,1469 @@ +// Dafny prelude +// Created 9 February 2008 by Rustan Leino. +// Converted to Boogie 2 on 28 June 2008. +// Edited sequence axioms 20 October 2009 by Alex Summers. +// Modified 2014 by Dan Rosen. +// Copyright (c) 2008-2014, Microsoft. +// Copyright by the contributors to the Dafny Project +// SPDX-License-Identifier: MIT + +const $$Language$Dafny: bool uses { // To be recognizable to the ModelViewer as + axiom $$Language$Dafny; // coming from a Dafny program. +} + +// --------------------------------------------------------------- +// -- Types ------------------------------------------------------ +// --------------------------------------------------------------- + +type Ty; +type Bv0 = int; + +const unique TBool : Ty uses { + axiom Tag(TBool) == TagBool; +} +const unique TChar : Ty uses { + axiom Tag(TChar) == TagChar; +} +const unique TInt : Ty uses { + axiom Tag(TInt) == TagInt; +} +const unique TReal : Ty uses { + axiom Tag(TReal) == TagReal; +} +const unique TORDINAL : Ty uses { + axiom Tag(TORDINAL) == TagORDINAL; +} +// See for which axioms we can make use of the trigger to determine the connection. +function TBitvector(int) : Ty; +axiom (forall w: int :: { TBitvector(w) } Inv0_TBitvector(TBitvector(w)) == w); + +function TSet(Ty) : Ty; +axiom (forall t: Ty :: { TSet(t) } Inv0_TSet(TSet(t)) == t); +axiom (forall t: Ty :: { TSet(t) } Tag(TSet(t)) == TagSet); + +function TISet(Ty) : Ty; +axiom (forall t: Ty :: { TISet(t) } Inv0_TISet(TISet(t)) == t); +axiom (forall t: Ty :: { TISet(t) } Tag(TISet(t)) == TagISet); + +function TMultiSet(Ty) : Ty; +axiom (forall t: Ty :: { TMultiSet(t) } Inv0_TMultiSet(TMultiSet(t)) == t); +axiom (forall t: Ty :: { TMultiSet(t) } Tag(TMultiSet(t)) == TagMultiSet); + +function TSeq(Ty) : Ty; +axiom (forall t: Ty :: { TSeq(t) } Inv0_TSeq(TSeq(t)) == t); +axiom (forall t: Ty :: { TSeq(t) } Tag(TSeq(t)) == TagSeq); + +function TMap(Ty, Ty) : Ty; +axiom (forall t, u: Ty :: { TMap(t,u) } Inv0_TMap(TMap(t,u)) == t); +axiom (forall t, u: Ty :: { TMap(t,u) } Inv1_TMap(TMap(t,u)) == u); +axiom (forall t, u: Ty :: { TMap(t,u) } Tag(TMap(t,u)) == TagMap); + +function TIMap(Ty, Ty) : Ty; +axiom (forall t, u: Ty :: { TIMap(t,u) } Inv0_TIMap(TIMap(t,u)) == t); +axiom (forall t, u: Ty :: { TIMap(t,u) } Inv1_TIMap(TIMap(t,u)) == u); +axiom (forall t, u: Ty :: { TIMap(t,u) } Tag(TIMap(t,u)) == TagIMap); + + +function Inv0_TBitvector(Ty) : int; +function Inv0_TSet(Ty) : Ty; +function Inv0_TISet(Ty) : Ty; +function Inv0_TSeq(Ty) : Ty; +function Inv0_TMultiSet(Ty) : Ty; +function Inv0_TMap(Ty) : Ty; +function Inv1_TMap(Ty) : Ty; +function Inv0_TIMap(Ty) : Ty; +function Inv1_TIMap(Ty) : Ty; + +// -- Classes and Datatypes -- + +// -- Type Tags -- +type TyTag; +function Tag(Ty) : TyTag; + +const unique TagBool : TyTag; +const unique TagChar : TyTag; +const unique TagInt : TyTag; +const unique TagReal : TyTag; +const unique TagORDINAL : TyTag; +const unique TagSet : TyTag; +const unique TagISet : TyTag; +const unique TagMultiSet : TyTag; +const unique TagSeq : TyTag; +const unique TagMap : TyTag; +const unique TagIMap : TyTag; +const unique TagClass : TyTag; + +type TyTagFamily; +function TagFamily(Ty): TyTagFamily; + +// --------------------------------------------------------------- +// -- Literals --------------------------------------------------- +// --------------------------------------------------------------- +function {:identity} Lit(x: T): T { x } +axiom (forall x: T :: { $Box(Lit(x)) } $Box(Lit(x)) == Lit($Box(x)) ); + +// Specialize Lit to concrete types. +// These aren't logically required, but on some examples improve +// verification speed +function {:identity} LitInt(x: int): int { x } +axiom (forall x: int :: { $Box(LitInt(x)) } $Box(LitInt(x)) == Lit($Box(x)) ); + +function {:identity} LitReal(x: real): real { x } +axiom (forall x: real :: { $Box(LitReal(x)) } $Box(LitReal(x)) == Lit($Box(x)) ); + +// --------------------------------------------------------------- +// -- Characters ------------------------------------------------- +// --------------------------------------------------------------- + +#if UNICODE_CHAR +function {:inline} char#IsChar(n: int): bool { + (0 <= n && n < 55296 /* 0xD800 */) || + (57344 /* 0xE000 */ <= n && n < 1114112 /* 0x11_0000 */ ) +} +#else +function {:inline} char#IsChar(n: int): bool { + 0 <= n && n < 65536 +} +#endif + +type char; +function char#FromInt(int): char; +axiom (forall n: int :: + { char#FromInt(n) } + char#IsChar(n) ==> char#ToInt(char#FromInt(n)) == n); + +function char#ToInt(char): int; +axiom (forall ch: char :: + { char#ToInt(ch) } + char#FromInt(char#ToInt(ch)) == ch && + char#IsChar(char#ToInt(ch))); + +function char#Plus(char, char): char; +axiom (forall a: char, b: char :: + { char#Plus(a, b) } + char#Plus(a, b) == char#FromInt(char#ToInt(a) + char#ToInt(b))); + +function char#Minus(char, char): char; +axiom (forall a: char, b: char :: + { char#Minus(a, b) } + char#Minus(a, b) == char#FromInt(char#ToInt(a) - char#ToInt(b))); + +// --------------------------------------------------------------- +// -- References ------------------------------------------------- +// --------------------------------------------------------------- + +type ref; +const null: ref; + +// --------------------------------------------------------------- +// -- Boxing and unboxing ---------------------------------------- +// --------------------------------------------------------------- + +type Box; +const $ArbitraryBoxValue: Box; + +function $Box(T): Box; +function $Unbox(Box): T; +axiom (forall x : T :: { $Box(x) } $Unbox($Box(x)) == x); +axiom (forall x : Box :: { $Unbox(x): T} $Box($Unbox(x): T) == x); + + +// Corresponding entries for boxes... +// This could probably be solved by having Box also inhabit Ty +function $IsBox(Box,Ty): bool; +function $IsAllocBox(Box,Ty,Heap): bool; + +axiom (forall bx : Box :: + { $IsBox(bx, TInt) } + ( $IsBox(bx, TInt) ==> $Box($Unbox(bx) : int) == bx && $Is($Unbox(bx) : int, TInt))); +axiom (forall bx : Box :: + { $IsBox(bx, TReal) } + ( $IsBox(bx, TReal) ==> $Box($Unbox(bx) : real) == bx && $Is($Unbox(bx) : real, TReal))); +axiom (forall bx : Box :: + { $IsBox(bx, TBool) } + ( $IsBox(bx, TBool) ==> $Box($Unbox(bx) : bool) == bx && $Is($Unbox(bx) : bool, TBool))); +axiom (forall bx : Box :: + { $IsBox(bx, TChar) } + ( $IsBox(bx, TChar) ==> $Box($Unbox(bx) : char) == bx && $Is($Unbox(bx) : char, TChar))); + +// Since each bitvector type is a separate type in Boogie, the Box/Unbox axioms for bitvectors are +// generated programmatically. Except, Bv0 is given here. +axiom (forall bx : Box :: + { $IsBox(bx, TBitvector(0)) } + ( $IsBox(bx, TBitvector(0)) ==> $Box($Unbox(bx) : Bv0) == bx && $Is($Unbox(bx) : Bv0, TBitvector(0)))); + +axiom (forall bx : Box, t : Ty :: + { $IsBox(bx, TSet(t)) } + ( $IsBox(bx, TSet(t)) ==> $Box($Unbox(bx) : Set) == bx && $Is($Unbox(bx) : Set, TSet(t)))); +axiom (forall bx : Box, t : Ty :: + { $IsBox(bx, TISet(t)) } + ( $IsBox(bx, TISet(t)) ==> $Box($Unbox(bx) : ISet) == bx && $Is($Unbox(bx) : ISet, TISet(t)))); +axiom (forall bx : Box, t : Ty :: + { $IsBox(bx, TMultiSet(t)) } + ( $IsBox(bx, TMultiSet(t)) ==> $Box($Unbox(bx) : MultiSet) == bx && $Is($Unbox(bx) : MultiSet, TMultiSet(t)))); +axiom (forall bx : Box, t : Ty :: + { $IsBox(bx, TSeq(t)) } + ( $IsBox(bx, TSeq(t)) ==> $Box($Unbox(bx) : Seq) == bx && $Is($Unbox(bx) : Seq, TSeq(t)))); +axiom (forall bx : Box, s : Ty, t : Ty :: + { $IsBox(bx, TMap(s, t)) } + ( $IsBox(bx, TMap(s, t)) ==> $Box($Unbox(bx) : Map) == bx && $Is($Unbox(bx) : Map, TMap(s, t)))); +axiom (forall bx : Box, s : Ty, t : Ty :: + { $IsBox(bx, TIMap(s, t)) } + ( $IsBox(bx, TIMap(s, t)) ==> $Box($Unbox(bx) : IMap) == bx && $Is($Unbox(bx) : IMap, TIMap(s, t)))); + +axiom (forall v : T, t : Ty :: + { $IsBox($Box(v), t) } + ( $IsBox($Box(v), t) <==> $Is(v,t) )); +axiom (forall v : T, t : Ty, h : Heap :: + { $IsAllocBox($Box(v), t, h) } + ( $IsAllocBox($Box(v), t, h) <==> $IsAlloc(v,t,h) )); + +// --------------------------------------------------------------- +// -- Is and IsAlloc --------------------------------------------- +// --------------------------------------------------------------- + +// Type-argument to $Is is the /representation type/, +// the second value argument to $Is is the actual type. +function $Is(T,Ty): bool; // no heap for now +axiom(forall v : int :: { $Is(v,TInt) } $Is(v,TInt)); +axiom(forall v : real :: { $Is(v,TReal) } $Is(v,TReal)); +axiom(forall v : bool :: { $Is(v,TBool) } $Is(v,TBool)); +axiom(forall v : char :: { $Is(v,TChar) } $Is(v,TChar)); +axiom(forall v : ORDINAL :: { $Is(v,TORDINAL) } $Is(v,TORDINAL)); + +// Since every bitvector type is a separate type in Boogie, the $Is/$IsAlloc axioms +// for bitvectors are generated programatically. Except, TBitvector(0) is given here. +axiom (forall v: Bv0 :: { $Is(v, TBitvector(0)) } $Is(v, TBitvector(0))); + +axiom (forall v: Set, t0: Ty :: { $Is(v, TSet(t0)) } + $Is(v, TSet(t0)) <==> + (forall bx: Box :: { v[bx] } + v[bx] ==> $IsBox(bx, t0))); +axiom (forall v: ISet, t0: Ty :: { $Is(v, TISet(t0)) } + $Is(v, TISet(t0)) <==> + (forall bx: Box :: { v[bx] } + v[bx] ==> $IsBox(bx, t0))); +axiom (forall v: MultiSet, t0: Ty :: { $Is(v, TMultiSet(t0)) } + $Is(v, TMultiSet(t0)) <==> + (forall bx: Box :: { v[bx] } + 0 < v[bx] ==> $IsBox(bx, t0))); +axiom (forall v: MultiSet, t0: Ty :: { $Is(v, TMultiSet(t0)) } + $Is(v, TMultiSet(t0)) ==> $IsGoodMultiSet(v)); +axiom (forall v: Seq, t0: Ty :: { $Is(v, TSeq(t0)) } + $Is(v, TSeq(t0)) <==> + (forall i : int :: { Seq#Index(v, i) } + 0 <= i && i < Seq#Length(v) ==> + $IsBox(Seq#Index(v, i), t0))); + +axiom (forall v: Map, t0: Ty, t1: Ty :: + { $Is(v, TMap(t0, t1)) } + $Is(v, TMap(t0, t1)) + <==> (forall bx: Box :: + { Map#Elements(v)[bx] } { Map#Domain(v)[bx] } + Map#Domain(v)[bx] ==> + $IsBox(Map#Elements(v)[bx], t1) && + $IsBox(bx, t0))); + +axiom (forall v: Map, t0: Ty, t1: Ty :: + { $Is(v, TMap(t0, t1)) } + $Is(v, TMap(t0, t1)) ==> + $Is(Map#Domain(v), TSet(t0)) && + $Is(Map#Values(v), TSet(t1)) && + $Is(Map#Items(v), TSet(Tclass._System.Tuple2(t0, t1)))); +axiom (forall v: IMap, t0: Ty, t1: Ty :: + { $Is(v, TIMap(t0, t1)) } + $Is(v, TIMap(t0, t1)) + <==> (forall bx: Box :: + { IMap#Elements(v)[bx] } { IMap#Domain(v)[bx] } + IMap#Domain(v)[bx] ==> + $IsBox(IMap#Elements(v)[bx], t1) && + $IsBox(bx, t0))); +axiom (forall v: IMap, t0: Ty, t1: Ty :: + { $Is(v, TIMap(t0, t1)) } + $Is(v, TIMap(t0, t1)) ==> + $Is(IMap#Domain(v), TISet(t0)) && + $Is(IMap#Values(v), TISet(t1)) && + $Is(IMap#Items(v), TISet(Tclass._System.Tuple2(t0, t1)))); + +function $IsAlloc(T,Ty,Heap): bool; +axiom(forall h : Heap, v : int :: { $IsAlloc(v,TInt,h) } $IsAlloc(v,TInt,h)); +axiom(forall h : Heap, v : real :: { $IsAlloc(v,TReal,h) } $IsAlloc(v,TReal,h)); +axiom(forall h : Heap, v : bool :: { $IsAlloc(v,TBool,h) } $IsAlloc(v,TBool,h)); +axiom(forall h : Heap, v : char :: { $IsAlloc(v,TChar,h) } $IsAlloc(v,TChar,h)); +axiom(forall h : Heap, v : ORDINAL :: { $IsAlloc(v,TORDINAL,h) } $IsAlloc(v,TORDINAL,h)); + +axiom (forall v: Bv0, h: Heap :: { $IsAlloc(v, TBitvector(0), h) } $IsAlloc(v, TBitvector(0), h)); + +axiom (forall v: Set, t0: Ty, h: Heap :: { $IsAlloc(v, TSet(t0), h) } + $IsAlloc(v, TSet(t0), h) <==> + (forall bx: Box :: { v[bx] } + v[bx] ==> $IsAllocBox(bx, t0, h))); +axiom (forall v: ISet, t0: Ty, h: Heap :: { $IsAlloc(v, TISet(t0), h) } + $IsAlloc(v, TISet(t0), h) <==> + (forall bx: Box :: { v[bx] } + v[bx] ==> $IsAllocBox(bx, t0, h))); +axiom (forall v: MultiSet, t0: Ty, h: Heap :: { $IsAlloc(v, TMultiSet(t0), h) } + $IsAlloc(v, TMultiSet(t0), h) <==> + (forall bx: Box :: { v[bx] } + 0 < v[bx] ==> $IsAllocBox(bx, t0, h))); +axiom (forall v: Seq, t0: Ty, h: Heap :: { $IsAlloc(v, TSeq(t0), h) } + $IsAlloc(v, TSeq(t0), h) <==> + (forall i : int :: { Seq#Index(v, i) } + 0 <= i && i < Seq#Length(v) ==> + $IsAllocBox(Seq#Index(v, i), t0, h))); + +axiom (forall v: Map, t0: Ty, t1: Ty, h: Heap :: + { $IsAlloc(v, TMap(t0, t1), h) } + $IsAlloc(v, TMap(t0, t1), h) + <==> (forall bx: Box :: + { Map#Elements(v)[bx] } { Map#Domain(v)[bx] } + Map#Domain(v)[bx] ==> + $IsAllocBox(Map#Elements(v)[bx], t1, h) && + $IsAllocBox(bx, t0, h))); + +axiom (forall v: IMap, t0: Ty, t1: Ty, h: Heap :: + { $IsAlloc(v, TIMap(t0, t1), h) } + $IsAlloc(v, TIMap(t0, t1), h) + <==> (forall bx: Box :: + { IMap#Elements(v)[bx] } { IMap#Domain(v)[bx] } + IMap#Domain(v)[bx] ==> + $IsAllocBox(IMap#Elements(v)[bx], t1, h) && + $IsAllocBox(bx, t0, h))); + + +function $AlwaysAllocated(Ty): bool; + axiom (forall ty: Ty :: { $AlwaysAllocated(ty) } + $AlwaysAllocated(ty) ==> + (forall h: Heap, v: Box :: { $IsAllocBox(v, ty, h) } $IsBox(v, ty) ==> $IsAllocBox(v, ty, h))); + +function $OlderTag(Heap): bool; + +// --------------------------------------------------------------- +// -- Encoding of type names ------------------------------------- +// --------------------------------------------------------------- + +type ClassName; +const unique class._System.int: ClassName; +const unique class._System.bool: ClassName; +const unique class._System.set: ClassName; +const unique class._System.seq: ClassName; +const unique class._System.multiset: ClassName; + +function Tclass._System.object?(): Ty; +function Tclass._System.Tuple2(Ty, Ty): Ty; + +function /*{:never_pattern true}*/ dtype(ref): Ty; // changed from ClassName to Ty + +function TypeTuple(a: ClassName, b: ClassName): ClassName; +function TypeTupleCar(ClassName): ClassName; +function TypeTupleCdr(ClassName): ClassName; +// TypeTuple is injective in both arguments: +axiom (forall a: ClassName, b: ClassName :: { TypeTuple(a,b) } + TypeTupleCar(TypeTuple(a,b)) == a && + TypeTupleCdr(TypeTuple(a,b)) == b); + +// -- Function handles ------------------------------------------- + +type HandleType; + +function SetRef_to_SetBox(s: [ref]bool): Set; +axiom (forall s: [ref]bool, bx: Box :: { SetRef_to_SetBox(s)[bx] } + SetRef_to_SetBox(s)[bx] == s[$Unbox(bx): ref]); +axiom (forall s: [ref]bool :: { SetRef_to_SetBox(s) } + $Is(SetRef_to_SetBox(s), TSet(Tclass._System.object?()))); + +// Functions ApplyN, RequiresN, and ReadsN are generated on demand by the translator, +// but Apply1 is referred to in the prelude, so its definition is hardcoded here. +function Apply1(Ty, Ty, Heap, HandleType, Box): Box; + +// --------------------------------------------------------------- +// -- Datatypes -------------------------------------------------- +// --------------------------------------------------------------- + +type DatatypeType; + +type DtCtorId; +function DatatypeCtorId(DatatypeType): DtCtorId; + +function DtRank(DatatypeType): int; +function BoxRank(Box): int; + +axiom (forall d: DatatypeType :: {BoxRank($Box(d))} BoxRank($Box(d)) == DtRank(d)); + +// --------------------------------------------------------------- +// -- Big Ordinals ----------------------------------------------- +// --------------------------------------------------------------- + +type ORDINAL = Box; // :| There are more big ordinals than boxes + +// The following two functions give an abstracton over all ordinals. +// Function ORD#IsNat returns true when the ordinal is one of the natural +// numbers. Function ORD#Offset gives how many successors (that is, +// +1 operations) an ordinal is above the nearest lower limit ordinal. +// That is, if the ordinal is \lambda+n, then ORD#Offset returns n. +function ORD#IsNat(ORDINAL): bool; +function ORD#Offset(ORDINAL): int; +axiom (forall o:ORDINAL :: { ORD#Offset(o) } 0 <= ORD#Offset(o)); + +function {:inline} ORD#IsLimit(o: ORDINAL): bool { ORD#Offset(o) == 0 } +function {:inline} ORD#IsSucc(o: ORDINAL): bool { 0 < ORD#Offset(o) } + +function ORD#FromNat(int): ORDINAL; +axiom (forall n:int :: { ORD#FromNat(n) } + 0 <= n ==> ORD#IsNat(ORD#FromNat(n)) && ORD#Offset(ORD#FromNat(n)) == n); +axiom (forall o:ORDINAL :: { ORD#Offset(o) } { ORD#IsNat(o) } + ORD#IsNat(o) ==> o == ORD#FromNat(ORD#Offset(o))); + +function ORD#Less(ORDINAL, ORDINAL): bool; +axiom (forall o,p: ORDINAL :: { ORD#Less(o,p) } + (ORD#Less(o,p) ==> o != p) && // irreflexivity + (ORD#IsNat(o) && !ORD#IsNat(p) ==> ORD#Less(o,p)) && + (ORD#IsNat(o) && ORD#IsNat(p) ==> ORD#Less(o,p) == (ORD#Offset(o) < ORD#Offset(p))) && + (ORD#Less(o,p) && ORD#IsNat(p) ==> ORD#IsNat(o))); +// ORD#Less is trichotomous: +axiom (forall o,p: ORDINAL :: { ORD#Less(o,p), ORD#Less(p,o) } + ORD#Less(o,p) || o == p || ORD#Less(p,o)); +// ORD#Less is transitive: +axiom (forall o,p,r: ORDINAL :: + { ORD#Less(o,p), ORD#Less(p,r) } + { ORD#Less(o,p), ORD#Less(o,r) } + ORD#Less(o,p) && ORD#Less(p,r) ==> ORD#Less(o,r)); + +// ORD#LessThanLimit is a synonym of ORD#Less, introduced for more selected triggering +function ORD#LessThanLimit(ORDINAL, ORDINAL): bool; +axiom (forall o,p: ORDINAL :: { ORD#LessThanLimit(o, p) } + ORD#LessThanLimit(o, p) == ORD#Less(o, p)); + +function ORD#Plus(ORDINAL, ORDINAL): ORDINAL; +axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } + (ORD#IsNat(ORD#Plus(o,p)) ==> ORD#IsNat(o) && ORD#IsNat(p)) && + (ORD#IsNat(p) ==> + ORD#IsNat(ORD#Plus(o,p)) == ORD#IsNat(o) && + ORD#Offset(ORD#Plus(o,p)) == ORD#Offset(o) + ORD#Offset(p))); +axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } + (o == ORD#Plus(o, p) || ORD#Less(o, ORD#Plus(o, p))) && + (p == ORD#Plus(o, p) || ORD#Less(p, ORD#Plus(o, p)))); +axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } + (o == ORD#FromNat(0) ==> ORD#Plus(o, p) == p) && + (p == ORD#FromNat(0) ==> ORD#Plus(o, p) == o)); + +function ORD#Minus(ORDINAL, ORDINAL): ORDINAL; +axiom (forall o,p: ORDINAL :: { ORD#Minus(o,p) } + ORD#IsNat(p) && ORD#Offset(p) <= ORD#Offset(o) ==> + ORD#IsNat(ORD#Minus(o,p)) == ORD#IsNat(o) && + ORD#Offset(ORD#Minus(o,p)) == ORD#Offset(o) - ORD#Offset(p)); +axiom (forall o,p: ORDINAL :: { ORD#Minus(o,p) } + ORD#IsNat(p) && ORD#Offset(p) <= ORD#Offset(o) ==> + (p == ORD#FromNat(0) && ORD#Minus(o, p) == o) || + (p != ORD#FromNat(0) && ORD#Less(ORD#Minus(o, p), o))); + +// o+m+n == o+(m+n) +axiom (forall o: ORDINAL, m,n: int :: + { ORD#Plus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) } + 0 <= m && 0 <= n ==> + ORD#Plus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(m+n))); +// o-m-n == o+(m+n) +axiom (forall o: ORDINAL, m,n: int :: + { ORD#Minus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) } + 0 <= m && 0 <= n && m+n <= ORD#Offset(o) ==> + ORD#Minus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(m+n))); +// o+m-n == EITHER o+(m-n) OR o-(n-m) +axiom (forall o: ORDINAL, m,n: int :: + { ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) } + 0 <= m && 0 <= n && n <= ORD#Offset(o) + m ==> + (0 <= m - n ==> ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(m-n))) && + (m - n <= 0 ==> ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(n-m)))); +// o-m+n == EITHER o-(m-n) OR o+(n-m) +axiom (forall o: ORDINAL, m,n: int :: + { ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) } + 0 <= m && 0 <= n && n <= ORD#Offset(o) + m ==> + (0 <= m - n ==> ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(m-n))) && + (m - n <= 0 ==> ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(n-m)))); + +// --------------------------------------------------------------- +// -- Axiom contexts --------------------------------------------- +// --------------------------------------------------------------- + +// used to make sure function axioms are not used while their consistency is being checked +const $ModuleContextHeight: int; +const $FunctionContextHeight: int; + +// --------------------------------------------------------------- +// -- Layers of function encodings ------------------------------- +// --------------------------------------------------------------- + +type LayerType; +const $LZ: LayerType; +function $LS(LayerType): LayerType; +function AsFuelBottom(LayerType) : LayerType; + +function AtLayer([LayerType]A, LayerType): A; +axiom (forall f : [LayerType]A, ly : LayerType :: { AtLayer(f,ly) } AtLayer(f,ly) == f[ly]); +axiom (forall f : [LayerType]A, ly : LayerType :: { AtLayer(f,$LS(ly)) } AtLayer(f,$LS(ly)) == AtLayer(f,ly)); + +// --------------------------------------------------------------- +// -- Fields ----------------------------------------------------- +// --------------------------------------------------------------- + +type Field; + +function FDim(Field): int uses { + axiom FDim(alloc) == 0; +} + +function IndexField(int): Field; +axiom (forall i: int :: { IndexField(i) } FDim(IndexField(i)) == 1); +function IndexField_Inverse(Field): int; +axiom (forall i: int :: { IndexField(i) } IndexField_Inverse(IndexField(i)) == i); + +function MultiIndexField(Field, int): Field; +axiom (forall f: Field, i: int :: { MultiIndexField(f,i) } FDim(MultiIndexField(f,i)) == FDim(f) + 1); +function MultiIndexField_Inverse0(Field): Field; +function MultiIndexField_Inverse1(Field): int; +axiom (forall f: Field, i: int :: { MultiIndexField(f,i) } + MultiIndexField_Inverse0(MultiIndexField(f,i)) == f && + MultiIndexField_Inverse1(MultiIndexField(f,i)) == i); + +function DeclType(Field): ClassName; + +type NameFamily; +function DeclName(Field): NameFamily uses { + axiom DeclName(alloc) == allocName; +} +function FieldOfDecl(ClassName, NameFamily): Field; +axiom (forall cl : ClassName, nm: NameFamily :: + {FieldOfDecl(cl, nm): Field} + DeclType(FieldOfDecl(cl, nm): Field) == cl && DeclName(FieldOfDecl(cl, nm): Field) == nm); + +function $IsGhostField(Field): bool uses { + axiom $IsGhostField(alloc); // treat as ghost field, since it is allowed to be changed by ghost code +} +axiom (forall h: Heap, k: Heap :: { $HeapSuccGhost(h,k) } + $HeapSuccGhost(h,k) ==> + $HeapSucc(h,k) && + (forall o: ref, f: Field :: { read(k, o, f) } + !$IsGhostField(f) ==> read(h, o, f) == read(k, o, f))); + +// --------------------------------------------------------------- +// -- Allocatedness and Heap Succession -------------------------- +// --------------------------------------------------------------- + + +// $IsAlloc and $IsAllocBox are monotonic + +axiom (forall h, k : Heap, v : T, t : Ty :: + { $HeapSucc(h, k), $IsAlloc(v, t, h) } + $HeapSucc(h, k) ==> $IsAlloc(v, t, h) ==> $IsAlloc(v, t, k)); +axiom (forall h, k : Heap, bx : Box, t : Ty :: + { $HeapSucc(h, k), $IsAllocBox(bx, t, h) } + $HeapSucc(h, k) ==> $IsAllocBox(bx, t, h) ==> $IsAllocBox(bx, t, k)); + +// No axioms for $Is and $IsBox since they don't talk about the heap. + +const unique alloc: Field; +const unique allocName: NameFamily; + +// --------------------------------------------------------------- +// -- Arrays ----------------------------------------------------- +// --------------------------------------------------------------- + +function _System.array.Length(a: ref): int; +axiom (forall o: ref :: {_System.array.Length(o)} 0 <= _System.array.Length(o)); + + +// --------------------------------------------------------------- +// -- Reals ------------------------------------------------------ +// --------------------------------------------------------------- + +function Int(x: real): int { int(x) } +function Real(x: int): real { real(x) } +axiom (forall i: int :: { Int(Real(i)) } Int(Real(i)) == i); + +function {:inline} _System.real.Floor(x: real): int { Int(x) } + +// --------------------------------------------------------------- +// -- The heap --------------------------------------------------- +// --------------------------------------------------------------- +type Heap = [ref][Field]Box; +function {:inline} read(H: Heap, r: ref, f: Field) : Box { H[r][f] } +function {:inline} update(H:Heap, r:ref, f: Field, v: Box) : Heap { H[r := H[r][f := v]] } + +function $IsGoodHeap(Heap): bool; +function $IsHeapAnchor(Heap): bool; +var $Heap: Heap where $IsGoodHeap($Heap) && $IsHeapAnchor($Heap); + +// The following is used as a reference heap in places where the translation needs a heap +// but the expression generated is really one that is (at least in a correct program) +// independent of the heap. +const $OneHeap: Heap uses { + axiom $IsGoodHeap($OneHeap); +} + +function $HeapSucc(Heap, Heap): bool; +axiom (forall h: Heap, r: ref, f: Field, x: Box :: { update(h, r, f, x) } + $IsGoodHeap(update(h, r, f, x)) ==> + $HeapSucc(h, update(h, r, f, x))); +axiom (forall a,b,c: Heap :: { $HeapSucc(a,b), $HeapSucc(b,c) } + a != c ==> $HeapSucc(a,b) && $HeapSucc(b,c) ==> $HeapSucc(a,c)); +axiom (forall h: Heap, k: Heap :: { $HeapSucc(h,k) } + $HeapSucc(h,k) ==> (forall o: ref :: { read(k, o, alloc) } $Unbox(read(h, o, alloc)) ==> $Unbox(read(k, o, alloc)))); + +function $HeapSuccGhost(Heap, Heap): bool; + +// --------------------------------------------------------------- +// -- Useful macros ---------------------------------------------- +// --------------------------------------------------------------- + +// havoc everything in $Heap, except {this}+rds+nw +procedure $YieldHavoc(this: ref, rds: Set, nw: Set); + modifies $Heap; + ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } + $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> + $o == this || rds[$Box($o)] || nw[$Box($o)] ==> + read($Heap, $o, $f) == read(old($Heap), $o, $f)); + ensures $HeapSucc(old($Heap), $Heap); + +// havoc everything in $Heap, except rds-modi-{this} +procedure $IterHavoc0(this: ref, rds: Set, modi: Set); + modifies $Heap; + ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } + $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> + rds[$Box($o)] && !modi[$Box($o)] && $o != this ==> + read($Heap, $o, $f) == read(old($Heap), $o, $f)); + ensures $HeapSucc(old($Heap), $Heap); + +// havoc $Heap at {this}+modi+nw +procedure $IterHavoc1(this: ref, modi: Set, nw: Set); + modifies $Heap; + ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } + $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> + read($Heap, $o, $f) == read(old($Heap), $o, $f) || + $o == this || modi[$Box($o)] || nw[$Box($o)]); + ensures $HeapSucc(old($Heap), $Heap); + +procedure $IterCollectNewObjects(prevHeap: Heap, newHeap: Heap, this: ref, NW: Field) + returns (s: Set); + ensures (forall bx: Box :: { s[bx] } s[bx] <==> + ($Unbox(read(newHeap, this, NW)) : Set)[bx] || + ($Unbox(bx) != null && !$Unbox(read(prevHeap, $Unbox(bx):ref, alloc)) && $Unbox(read(newHeap, $Unbox(bx):ref, alloc)))); + +// --------------------------------------------------------------- +// -- Axiomatizations -------------------------------------------- +// --------------------------------------------------------------- + +// --------------------------------------------------------------- +// -- Axiomatization of sets ------------------------------------- +// --------------------------------------------------------------- + +type Set = [Box]bool; + +function Set#Card(Set): int; +axiom (forall s: Set :: { Set#Card(s) } 0 <= Set#Card(s)); + +function Set#Empty(): Set; +axiom (forall o: Box :: { Set#Empty()[o] } !Set#Empty()[o]); +axiom (forall s: Set :: { Set#Card(s) } + (Set#Card(s) == 0 <==> s == Set#Empty()) && + (Set#Card(s) != 0 ==> (exists x: Box :: s[x]))); + +// the empty set could be of anything +//axiom (forall t: Ty :: { $Is(Set#Empty() : [Box]bool, TSet(t)) } $Is(Set#Empty() : [Box]bool, TSet(t))); + +function Set#Singleton(Box): Set; +axiom (forall r: Box :: { Set#Singleton(r) } Set#Singleton(r)[r]); +axiom (forall r: Box, o: Box :: { Set#Singleton(r)[o] } Set#Singleton(r)[o] <==> r == o); +axiom (forall r: Box :: { Set#Card(Set#Singleton(r)) } Set#Card(Set#Singleton(r)) == 1); + +function Set#UnionOne(Set, Box): Set; +axiom (forall a: Set, x: Box, o: Box :: { Set#UnionOne(a,x)[o] } + Set#UnionOne(a,x)[o] <==> o == x || a[o]); +axiom (forall a: Set, x: Box :: { Set#UnionOne(a, x) } + Set#UnionOne(a, x)[x]); +axiom (forall a: Set, x: Box, y: Box :: { Set#UnionOne(a, x), a[y] } + a[y] ==> Set#UnionOne(a, x)[y]); +axiom (forall a: Set, x: Box :: { Set#Card(Set#UnionOne(a, x)) } + a[x] ==> Set#Card(Set#UnionOne(a, x)) == Set#Card(a)); +axiom (forall a: Set, x: Box :: { Set#Card(Set#UnionOne(a, x)) } + !a[x] ==> Set#Card(Set#UnionOne(a, x)) == Set#Card(a) + 1); + +function Set#Union(Set, Set): Set; +axiom (forall a: Set, b: Set, o: Box :: { Set#Union(a,b)[o] } + Set#Union(a,b)[o] <==> a[o] || b[o]); +axiom (forall a, b: Set, y: Box :: { Set#Union(a, b), a[y] } + a[y] ==> Set#Union(a, b)[y]); +axiom (forall a, b: Set, y: Box :: { Set#Union(a, b), b[y] } + b[y] ==> Set#Union(a, b)[y]); +axiom (forall a, b: Set :: { Set#Union(a, b) } + Set#Disjoint(a, b) ==> + Set#Difference(Set#Union(a, b), a) == b && + Set#Difference(Set#Union(a, b), b) == a); +// Follows from the general union axiom, but might be still worth including, because disjoint union is a common case: +// axiom (forall a, b: Set :: { Set#Card(Set#Union(a, b)) } +// Set#Disjoint(a, b) ==> +// Set#Card(Set#Union(a, b)) == Set#Card(a) + Set#Card(b)); + +function Set#Intersection(Set, Set): Set; +axiom (forall a: Set, b: Set, o: Box :: { Set#Intersection(a,b)[o] } + Set#Intersection(a,b)[o] <==> a[o] && b[o]); + +axiom (forall a, b: Set :: { Set#Union(Set#Union(a, b), b) } + Set#Union(Set#Union(a, b), b) == Set#Union(a, b)); +axiom (forall a, b: Set :: { Set#Union(a, Set#Union(a, b)) } + Set#Union(a, Set#Union(a, b)) == Set#Union(a, b)); +axiom (forall a, b: Set :: { Set#Intersection(Set#Intersection(a, b), b) } + Set#Intersection(Set#Intersection(a, b), b) == Set#Intersection(a, b)); +axiom (forall a, b: Set :: { Set#Intersection(a, Set#Intersection(a, b)) } + Set#Intersection(a, Set#Intersection(a, b)) == Set#Intersection(a, b)); +axiom (forall a, b: Set :: { Set#Card(Set#Union(a, b)) }{ Set#Card(Set#Intersection(a, b)) } + Set#Card(Set#Union(a, b)) + Set#Card(Set#Intersection(a, b)) == Set#Card(a) + Set#Card(b)); + +function Set#Difference(Set, Set): Set; +axiom (forall a: Set, b: Set, o: Box :: { Set#Difference(a,b)[o] } + Set#Difference(a,b)[o] <==> a[o] && !b[o]); +axiom (forall a, b: Set, y: Box :: { Set#Difference(a, b), b[y] } + b[y] ==> !Set#Difference(a, b)[y] ); +axiom (forall a, b: Set :: + { Set#Card(Set#Difference(a, b)) } + Set#Card(Set#Difference(a, b)) + Set#Card(Set#Difference(b, a)) + + Set#Card(Set#Intersection(a, b)) + == Set#Card(Set#Union(a, b)) && + Set#Card(Set#Difference(a, b)) == Set#Card(a) - Set#Card(Set#Intersection(a, b))); + +function Set#Subset(Set, Set): bool; +axiom (forall a: Set, b: Set :: { Set#Subset(a,b) } + Set#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] ==> b[o])); +// axiom(forall a: Set, b: Set :: +// { Set#Subset(a,b), Set#Card(a), Set#Card(b) } // very restrictive trigger +// Set#Subset(a,b) ==> Set#Card(a) <= Set#Card(b)); + + +function Set#Equal(Set, Set): bool; +axiom (forall a: Set, b: Set :: { Set#Equal(a,b) } + Set#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <==> b[o])); +axiom (forall a: Set, b: Set :: { Set#Equal(a,b) } // extensionality axiom for sets + Set#Equal(a,b) ==> a == b); + +function Set#Disjoint(Set, Set): bool; +axiom (forall a: Set, b: Set :: { Set#Disjoint(a,b) } + Set#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} !a[o] || !b[o])); + +// --------------------------------------------------------------- +// -- Axiomatization of isets ------------------------------------- +// --------------------------------------------------------------- + +type ISet = [Box]bool; + +function ISet#Empty(): Set; +axiom (forall o: Box :: { ISet#Empty()[o] } !ISet#Empty()[o]); + +// the empty set could be of anything +//axiom (forall t: Ty :: { $Is(ISet#Empty() : [Box]bool, TISet(t)) } $Is(ISet#Empty() : [Box]bool, TISet(t))); + + +function ISet#UnionOne(ISet, Box): ISet; +axiom (forall a: ISet, x: Box, o: Box :: { ISet#UnionOne(a,x)[o] } + ISet#UnionOne(a,x)[o] <==> o == x || a[o]); +axiom (forall a: ISet, x: Box :: { ISet#UnionOne(a, x) } + ISet#UnionOne(a, x)[x]); +axiom (forall a: ISet, x: Box, y: Box :: { ISet#UnionOne(a, x), a[y] } + a[y] ==> ISet#UnionOne(a, x)[y]); + +function ISet#Union(ISet, ISet): ISet; +axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Union(a,b)[o] } + ISet#Union(a,b)[o] <==> a[o] || b[o]); +axiom (forall a, b: ISet, y: Box :: { ISet#Union(a, b), a[y] } + a[y] ==> ISet#Union(a, b)[y]); +axiom (forall a, b: Set, y: Box :: { ISet#Union(a, b), b[y] } + b[y] ==> ISet#Union(a, b)[y]); +axiom (forall a, b: ISet :: { ISet#Union(a, b) } + ISet#Disjoint(a, b) ==> + ISet#Difference(ISet#Union(a, b), a) == b && + ISet#Difference(ISet#Union(a, b), b) == a); + +function ISet#Intersection(ISet, ISet): ISet; +axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Intersection(a,b)[o] } + ISet#Intersection(a,b)[o] <==> a[o] && b[o]); + +axiom (forall a, b: ISet :: { ISet#Union(ISet#Union(a, b), b) } + ISet#Union(ISet#Union(a, b), b) == ISet#Union(a, b)); +axiom (forall a, b: Set :: { ISet#Union(a, ISet#Union(a, b)) } + ISet#Union(a, ISet#Union(a, b)) == ISet#Union(a, b)); +axiom (forall a, b: ISet :: { ISet#Intersection(ISet#Intersection(a, b), b) } + ISet#Intersection(ISet#Intersection(a, b), b) == ISet#Intersection(a, b)); +axiom (forall a, b: ISet :: { ISet#Intersection(a, ISet#Intersection(a, b)) } + ISet#Intersection(a, ISet#Intersection(a, b)) == ISet#Intersection(a, b)); + + +function ISet#Difference(ISet, ISet): ISet; +axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Difference(a,b)[o] } + ISet#Difference(a,b)[o] <==> a[o] && !b[o]); +axiom (forall a, b: ISet, y: Box :: { ISet#Difference(a, b), b[y] } + b[y] ==> !ISet#Difference(a, b)[y] ); + +function ISet#Subset(ISet, ISet): bool; +axiom (forall a: ISet, b: ISet :: { ISet#Subset(a,b) } + ISet#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] ==> b[o])); + +function ISet#Equal(ISet, ISet): bool; +axiom (forall a: ISet, b: ISet :: { ISet#Equal(a,b) } + ISet#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <==> b[o])); +axiom (forall a: ISet, b: ISet :: { ISet#Equal(a,b) } // extensionality axiom for sets + ISet#Equal(a,b) ==> a == b); + +function ISet#Disjoint(ISet, ISet): bool; +axiom (forall a: ISet, b: ISet :: { ISet#Disjoint(a,b) } + ISet#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} !a[o] || !b[o])); + +// --------------------------------------------------------------- +// -- Axiomatization of multisets -------------------------------- +// --------------------------------------------------------------- + +function Math#min(a: int, b: int): int; +axiom (forall a: int, b: int :: { Math#min(a, b) } a <= b <==> Math#min(a, b) == a); +axiom (forall a: int, b: int :: { Math#min(a, b) } b <= a <==> Math#min(a, b) == b); +axiom (forall a: int, b: int :: { Math#min(a, b) } Math#min(a, b) == a || Math#min(a, b) == b); + +function Math#clip(a: int): int; +axiom (forall a: int :: { Math#clip(a) } 0 <= a ==> Math#clip(a) == a); +axiom (forall a: int :: { Math#clip(a) } a < 0 ==> Math#clip(a) == 0); + +type MultiSet = [Box]int; + +function $IsGoodMultiSet(ms: MultiSet): bool; +// ints are non-negative, used after havocing, and for conversion from sequences to multisets. +axiom (forall ms: MultiSet :: { $IsGoodMultiSet(ms) } + $IsGoodMultiSet(ms) <==> + (forall bx: Box :: { ms[bx] } 0 <= ms[bx] && ms[bx] <= MultiSet#Card(ms))); + +function MultiSet#Card(MultiSet): int; +axiom (forall s: MultiSet :: { MultiSet#Card(s) } 0 <= MultiSet#Card(s)); +axiom (forall s: MultiSet, x: Box, n: int :: { MultiSet#Card(s[x := n]) } + 0 <= n ==> MultiSet#Card(s[x := n]) == MultiSet#Card(s) - s[x] + n); + +function MultiSet#Empty(): MultiSet; +axiom (forall o: Box :: { MultiSet#Empty()[o] } MultiSet#Empty()[o] == 0); +axiom (forall s: MultiSet :: { MultiSet#Card(s) } + (MultiSet#Card(s) == 0 <==> s == MultiSet#Empty()) && + (MultiSet#Card(s) != 0 ==> (exists x: Box :: 0 < s[x]))); + +function MultiSet#Singleton(Box): MultiSet; +axiom (forall r: Box, o: Box :: { MultiSet#Singleton(r)[o] } (MultiSet#Singleton(r)[o] == 1 <==> r == o) && + (MultiSet#Singleton(r)[o] == 0 <==> r != o)); +axiom (forall r: Box :: { MultiSet#Singleton(r) } MultiSet#Singleton(r) == MultiSet#UnionOne(MultiSet#Empty(), r)); + +function MultiSet#UnionOne(MultiSet, Box): MultiSet; +// pure containment axiom (in the original multiset or is the added element) +axiom (forall a: MultiSet, x: Box, o: Box :: { MultiSet#UnionOne(a,x)[o] } + 0 < MultiSet#UnionOne(a,x)[o] <==> o == x || 0 < a[o]); +// union-ing increases count by one +axiom (forall a: MultiSet, x: Box :: { MultiSet#UnionOne(a, x) } + MultiSet#UnionOne(a, x)[x] == a[x] + 1); +// non-decreasing +axiom (forall a: MultiSet, x: Box, y: Box :: { MultiSet#UnionOne(a, x), a[y] } + 0 < a[y] ==> 0 < MultiSet#UnionOne(a, x)[y]); +// other elements unchanged +axiom (forall a: MultiSet, x: Box, y: Box :: { MultiSet#UnionOne(a, x), a[y] } + x != y ==> a[y] == MultiSet#UnionOne(a, x)[y]); +axiom (forall a: MultiSet, x: Box :: { MultiSet#Card(MultiSet#UnionOne(a, x)) } + MultiSet#Card(MultiSet#UnionOne(a, x)) == MultiSet#Card(a) + 1); + + +function MultiSet#Union(MultiSet, MultiSet): MultiSet; +// union-ing is the sum of the contents +axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Union(a,b)[o] } + MultiSet#Union(a,b)[o] == a[o] + b[o]); +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Card(MultiSet#Union(a,b)) } + MultiSet#Card(MultiSet#Union(a,b)) == MultiSet#Card(a) + MultiSet#Card(b)); + +function MultiSet#Intersection(MultiSet, MultiSet): MultiSet; +axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Intersection(a,b)[o] } + MultiSet#Intersection(a,b)[o] == Math#min(a[o], b[o])); + +// left and right pseudo-idempotence +axiom (forall a, b: MultiSet :: { MultiSet#Intersection(MultiSet#Intersection(a, b), b) } + MultiSet#Intersection(MultiSet#Intersection(a, b), b) == MultiSet#Intersection(a, b)); +axiom (forall a, b: MultiSet :: { MultiSet#Intersection(a, MultiSet#Intersection(a, b)) } + MultiSet#Intersection(a, MultiSet#Intersection(a, b)) == MultiSet#Intersection(a, b)); + +// multiset difference, a - b. clip() makes it positive. +function MultiSet#Difference(MultiSet, MultiSet): MultiSet; +axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Difference(a,b)[o] } + MultiSet#Difference(a,b)[o] == Math#clip(a[o] - b[o])); +axiom (forall a, b: MultiSet, y: Box :: { MultiSet#Difference(a, b), b[y], a[y] } + a[y] <= b[y] ==> MultiSet#Difference(a, b)[y] == 0 ); +axiom (forall a, b: MultiSet :: + { MultiSet#Card(MultiSet#Difference(a, b)) } + MultiSet#Card(MultiSet#Difference(a, b)) + MultiSet#Card(MultiSet#Difference(b, a)) + + 2 * MultiSet#Card(MultiSet#Intersection(a, b)) + == MultiSet#Card(MultiSet#Union(a, b)) && + MultiSet#Card(MultiSet#Difference(a, b)) == MultiSet#Card(a) - MultiSet#Card(MultiSet#Intersection(a, b))); + +// multiset subset means a must have at most as many of each element as b +function MultiSet#Subset(MultiSet, MultiSet): bool; +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Subset(a,b) } + MultiSet#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <= b[o])); + +function MultiSet#Equal(MultiSet, MultiSet): bool; +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Equal(a,b) } + MultiSet#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] == b[o])); +// extensionality axiom for multisets +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Equal(a,b) } + MultiSet#Equal(a,b) ==> a == b); + +function MultiSet#Disjoint(MultiSet, MultiSet): bool; +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Disjoint(a,b) } + MultiSet#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] == 0 || b[o] == 0)); + +// conversion to a multiset. each element in the original set has duplicity 1. +function MultiSet#FromSet(Set): MultiSet; +axiom (forall s: Set, a: Box :: { MultiSet#FromSet(s)[a] } + (MultiSet#FromSet(s)[a] == 0 <==> !s[a]) && + (MultiSet#FromSet(s)[a] == 1 <==> s[a])); +axiom (forall s: Set :: { MultiSet#Card(MultiSet#FromSet(s)) } + MultiSet#Card(MultiSet#FromSet(s)) == Set#Card(s)); + +// conversion to a multiset, from a sequence. +function MultiSet#FromSeq(Seq): MultiSet uses { + axiom MultiSet#FromSeq(Seq#Empty(): Seq) == MultiSet#Empty(): MultiSet; +} + +// conversion produces a good map. +axiom (forall s: Seq :: { MultiSet#FromSeq(s) } $IsGoodMultiSet(MultiSet#FromSeq(s)) ); +// cardinality axiom +axiom (forall s: Seq :: + { MultiSet#Card(MultiSet#FromSeq(s)) } + MultiSet#Card(MultiSet#FromSeq(s)) == Seq#Length(s)); +// building axiom +axiom (forall s: Seq, v: Box :: + { MultiSet#FromSeq(Seq#Build(s, v)) } + MultiSet#FromSeq(Seq#Build(s, v)) == MultiSet#UnionOne(MultiSet#FromSeq(s), v) + ); + +// concatenation axiom +axiom (forall a: Seq, b: Seq :: + { MultiSet#FromSeq(Seq#Append(a, b)) } + MultiSet#FromSeq(Seq#Append(a, b)) == MultiSet#Union(MultiSet#FromSeq(a), MultiSet#FromSeq(b)) ); + +// update axiom +axiom (forall s: Seq, i: int, v: Box, x: Box :: + { MultiSet#FromSeq(Seq#Update(s, i, v))[x] } + 0 <= i && i < Seq#Length(s) ==> + MultiSet#FromSeq(Seq#Update(s, i, v))[x] == + MultiSet#Union(MultiSet#Difference(MultiSet#FromSeq(s), MultiSet#Singleton(Seq#Index(s,i))), MultiSet#Singleton(v))[x] ); + // i.e. MS(Update(s, i, v)) == MS(s) - {{s[i]}} + {{v}} +axiom (forall s: Seq, x: Box :: { MultiSet#FromSeq(s)[x] } + (exists i : int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && x == Seq#Index(s,i)) <==> 0 < MultiSet#FromSeq(s)[x] ); + +// --------------------------------------------------------------- +// -- Axiomatization of sequences -------------------------------- +// --------------------------------------------------------------- + + + +type Seq; + +function Seq#Length(s: Seq) : int; + +axiom (forall s: Seq :: { Seq#Length(s) } 0 <= Seq#Length(s)); + +function Seq#Empty() : Seq +uses { +axiom Seq#Length(Seq#Empty()) == 0; +} + +axiom (forall s: Seq :: { Seq#Length(s) } Seq#Length(s) == 0 ==> s == Seq#Empty()); + +function Seq#Append(s0: Seq, s1: Seq) : Seq; + +axiom (forall s0: Seq, s1: Seq :: + { Seq#Length(Seq#Append(s0, s1)) } + Seq#Length(Seq#Append(s0, s1)) == Seq#Length(s0) + Seq#Length(s1)); + +axiom (forall s0: Seq, s1: Seq, n: int :: + { Index(Seq#Append(s0, s1), n) } + (n < Seq#Length(s0) ==> Index(Seq#Append(s0, s1), n) == Index(s0, n)) + && (Seq#Length(s0) <= n + ==> Index(Seq#Append(s0, s1), n) == Index(s1, n - Seq#Length(s0)))); + +function Seq#Build(s: Seq, val: Box) : Seq; + +function Seq#Build_inv0(s: Seq) : Seq; + +function Seq#Build_inv1(s: Seq) : Box; + +axiom (forall s: Seq, val: Box :: + { Seq#Build(s, val) } + Seq#Build_inv0(Seq#Build(s, val)) == s + && Seq#Build_inv1(Seq#Build(s, val)) == val); + +axiom (forall s: Seq, v: Box :: + { Seq#Build(s, v) } + Seq#Length(Seq#Build(s, v)) == 1 + Seq#Length(s)); + +axiom (forall s: Seq, i: int, v: Box :: + { Index(Seq#Build(s, v), i) } + (i == Seq#Length(s) ==> Index(Seq#Build(s, v), i) == v) + && (i != Seq#Length(s) ==> Index(Seq#Build(s, v), i) == Index(s, i))); + +function Seq#Update(s: Seq, i: int, val: Box) : Seq; + +axiom (forall s: Seq, i: int, v: Box :: + { Seq#Length(Seq#Update(s, i, v)) } + 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s, i, v)) == Seq#Length(s)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Index(Seq#Update(s, i, v), n) } + 0 <= n && n < Seq#Length(s) + ==> (i == n ==> Index(Seq#Update(s, i, v), n) == v) + && (i != n ==> Index(Seq#Update(s, i, v), n) == Index(s, n))); + +function Seq#Contains(s: Seq, val: Box) : bool; + +axiom (forall s: Seq, x: Box :: + { Seq#Contains(s, x) } + Seq#Contains(s, x) + <==> (forall i: int :: + { Index(s, i) } + 0 <= i && i < Seq#Length(s) && Index(s, i) == x)); + +axiom (forall x: Box :: + { Seq#Contains(Seq#Empty(), x) } + -Seq#Contains(Seq#Empty(), x)); + +axiom (forall s0: Seq, s1: Seq, x: Box :: + { Seq#Contains(Seq#Append(s0, s1), x) } + Seq#Contains(Seq#Append(s0, s1), x) + <==> Seq#Contains(s0, x) || Seq#Contains(s1, x)); + +axiom (forall s: Seq, v: Box, x: Box :: + { Seq#Contains(Seq#Build(s, v), x) } + Seq#Contains(Seq#Build(s, v), x) <==> v == x || Seq#Contains(s, x)); + +function Seq#Equal(s0: Seq, s1: Seq) : bool; + +axiom (forall s0: Seq, s1: Seq :: + { Seq#Equal(s0, s1) } + Seq#Equal(s0, s1) + <==> Seq#Length(s0) == Seq#Length(s1) + && (forall j: int :: + { Index(s1, j) } { Index(s0, j) } + 0 <= j && j < Seq#Length(s0) ==> Index(s0, j) == Index(s1, j))); + +axiom (forall a: Seq, b: Seq :: { Seq#Equal(a, b) } Seq#Equal(a, b) ==> a == b); + +function Seq#SameUntil(s0: Seq, s1: Seq, n: int) : bool; + +axiom (forall s0: Seq, s1: Seq, n: int :: + { Seq#SameUntil(s0, s1, n) } + Seq#SameUntil(s0, s1, n) + <==> (forall j: int :: + { Index(s1, j) } { Index(s0, j) } + 0 <= j && j < n ==> Index(s0, j) == Index(s1, j))); + +function Seq#Take(s: Seq, howMany: int) : Seq; + +axiom (forall s: Seq, n: int, j: int :: + {:weight 25} { Index(s, j), Seq#Take(s, n) } { Index(Seq#Take(s, n), j) } + 0 <= j && j < n && j < Seq#Length(s) ==> Index(Seq#Take(s, n), j) == Index(s, j)); + +function Seq#Drop(s: Seq, howMany: int) : Seq; + +axiom (forall s: Seq, n: int :: + { Seq#Length(Seq#Drop(s, n)) } + 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s, n)) == Seq#Length(s) - n); + +axiom (forall s: Seq, n: int, j: int :: + {:weight 25} { Index(Seq#Drop(s, n), j) } + 0 <= n && 0 <= j && j < Seq#Length(s) - n + ==> Index(Seq#Drop(s, n), j) == Index(s, j + n)); + +axiom (forall s: Seq, n: int, k: int :: + {:weight 25} { Index(s, k), Seq#Drop(s, n) } + 0 <= n && n <= k && k < Seq#Length(s) + ==> Index(Seq#Drop(s, n), k - n) == Index(s, k)); + +axiom (forall s: Seq, n: int, x: Box :: + { Seq#Contains(Seq#Take(s, n), x) } + Seq#Contains(Seq#Take(s, n), x) + <==> (forall i: int :: + { Index(s, i) } + 0 <= i && i < n && i < Seq#Length(s) && Index(s, i) == x)); + +axiom (forall s: Seq, n: int, x: Box :: + { Seq#Contains(Seq#Drop(s, n), x) } + Seq#Contains(Seq#Drop(s, n), x) + <==> (forall i: int :: + { Index(s, i) } + 0 <= n && n <= i && i < Seq#Length(s) && Index(s, i) == x)); + +axiom (forall s: Seq, t: Seq, n: int :: + { Seq#Drop(Seq#Append(s, t), n) } { Seq#Take(Seq#Append(s, t), n) } + n == Seq#Length(s) + ==> Seq#Take(Seq#Append(s, t), n) == s && Seq#Drop(Seq#Append(s, t), n) == t); + +axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } n == 0 ==> Seq#Drop(s, n) == s); + +axiom (forall s: Seq, n: int :: + { Seq#Take(s, n) } + n == 0 ==> Seq#Take(s, n) == Seq#Empty()); + +axiom (forall s: Seq, m: int, n: int :: + { Seq#Drop(Seq#Drop(s, m), n) } + 0 <= m && 0 <= n && m + n <= Seq#Length(s) + ==> Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m + n)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Take(Seq#Update(s, i, v), n) } + 0 <= i && i < n && n <= Seq#Length(s) + ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Take(Seq#Update(s, i, v), n) } + n <= i && i < Seq#Length(s) + ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Drop(Seq#Update(s, i, v), n) } + 0 <= n && n <= i && i < Seq#Length(s) + ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i - n, v)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Drop(Seq#Update(s, i, v), n) } + 0 <= i && i < n && n <= Seq#Length(s) + ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); + +axiom (forall s: Seq, v: Box, n: int :: + { Seq#Drop(Seq#Build(s, v), n) } + 0 <= n && n <= Seq#Length(s) + ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v)); + + +// --------------------------------------------------------------- +// -- Axiomatization of Maps ------------------------------------- +// --------------------------------------------------------------- + +type Map; + +// A Map is defined by three functions, Map#Domain, Map#Elements, and #Map#Card. + +function Map#Domain(Map) : Set; + +function Map#Elements(Map) : [Box]Box; + +function Map#Card(Map) : int; + +axiom (forall m: Map :: { Map#Card(m) } 0 <= Map#Card(m)); + +axiom (forall m: Map :: + { Map#Card(m) } + Map#Card(m) == 0 <==> m == Map#Empty()); + +axiom (forall m: Map :: + { Map#Domain(m) } + m == Map#Empty() || (exists k: Box :: Map#Domain(m)[k])); +axiom (forall m: Map :: + { Map#Values(m) } + m == Map#Empty() || (exists v: Box :: Map#Values(m)[v])); +axiom (forall m: Map :: + { Map#Items(m) } + m == Map#Empty() || (exists k, v: Box :: Map#Items(m)[$Box(#_System._tuple#2._#Make2(k, v))])); + +axiom (forall m: Map :: + { Set#Card(Map#Domain(m)) } { Map#Card(m) } + Set#Card(Map#Domain(m)) == Map#Card(m)); +axiom (forall m: Map :: + { Set#Card(Map#Values(m)) } { Map#Card(m) } + Set#Card(Map#Values(m)) <= Map#Card(m)); +axiom (forall m: Map :: + { Set#Card(Map#Items(m)) } { Map#Card(m) } + Set#Card(Map#Items(m)) == Map#Card(m)); + +// The set of Values of a Map can be obtained by the function Map#Values, which is +// defined as follows. Remember, a Set is defined by membership (using Boogie's +// square brackets) and Map#Card, so we need to define what these mean for the Set +// returned by Map#Values. + +function Map#Values(Map) : Set; + +axiom (forall m: Map, v: Box :: { Map#Values(m)[v] } + Map#Values(m)[v] == + (exists u: Box :: { Map#Domain(m)[u] } { Map#Elements(m)[u] } + Map#Domain(m)[u] && + v == Map#Elements(m)[u])); + +// The set of Items--that is, (key,value) pairs--of a Map can be obtained by the +// function Map#Items. Again, we need to define membership of Set#Card for this +// set. Everywhere else in this axiomatization, Map is parameterized by types U V, +// even though Dafny only ever instantiates U V with Box Box. This makes the +// axiomatization more generic. Function Map#Items, however, returns a set of +// pairs, and the axiomatization of pairs is Dafny specific. Therefore, the +// definition of Map#Items here is to be considered Dafny specific. Also, note +// that it relies on the two destructors for 2-tuples. + +function Map#Items(Map) : Set; + +function #_System._tuple#2._#Make2(Box, Box) : DatatypeType; +function _System.Tuple2._0(DatatypeType) : Box; +function _System.Tuple2._1(DatatypeType) : Box; + +axiom (forall m: Map, item: Box :: { Map#Items(m)[item] } + Map#Items(m)[item] <==> + Map#Domain(m)[_System.Tuple2._0($Unbox(item))] && + Map#Elements(m)[_System.Tuple2._0($Unbox(item))] == _System.Tuple2._1($Unbox(item))); + +// Here are the operations that produce Map values. + +function Map#Empty(): Map; +axiom (forall u: Box :: + { Map#Domain(Map#Empty(): Map)[u] } + !Map#Domain(Map#Empty(): Map)[u]); + +function Map#Glue([Box]bool, [Box]Box, Ty): Map; +axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: + { Map#Domain(Map#Glue(a, b, t)) } + Map#Domain(Map#Glue(a, b, t)) == a); +axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: + { Map#Elements(Map#Glue(a, b, t)) } + Map#Elements(Map#Glue(a, b, t)) == b); +axiom (forall a: [Box]bool, b: [Box]Box, t0, t1: Ty :: + { Map#Glue(a, b, TMap(t0, t1)) } + // In the following line, no trigger needed, since the quantifier only gets used in negative contexts + (forall bx: Box :: a[bx] ==> $IsBox(bx, t0) && $IsBox(b[bx], t1)) + ==> + $Is(Map#Glue(a, b, TMap(t0, t1)), TMap(t0, t1))); + + +//Build is used in displays, and for map updates +function Map#Build(Map, Box, Box): Map; +/*axiom (forall m: Map, u: Box, v: Box :: + { Map#Domain(Map#Build(m, u, v))[u] } { Map#Elements(Map#Build(m, u, v))[u] } + Map#Domain(Map#Build(m, u, v))[u] && Map#Elements(Map#Build(m, u, v))[u] == v);*/ + +axiom (forall m: Map, u: Box, u': Box, v: Box :: + { Map#Domain(Map#Build(m, u, v))[u'] } { Map#Elements(Map#Build(m, u, v))[u'] } + (u' == u ==> Map#Domain(Map#Build(m, u, v))[u'] && + Map#Elements(Map#Build(m, u, v))[u'] == v) && + (u' != u ==> Map#Domain(Map#Build(m, u, v))[u'] == Map#Domain(m)[u'] && + Map#Elements(Map#Build(m, u, v))[u'] == Map#Elements(m)[u'])); +axiom (forall m: Map, u: Box, v: Box :: { Map#Card(Map#Build(m, u, v)) } + Map#Domain(m)[u] ==> Map#Card(Map#Build(m, u, v)) == Map#Card(m)); +axiom (forall m: Map, u: Box, v: Box :: { Map#Card(Map#Build(m, u, v)) } + !Map#Domain(m)[u] ==> Map#Card(Map#Build(m, u, v)) == Map#Card(m) + 1); + +// Map operations +function Map#Merge(Map, Map): Map; +axiom (forall m: Map, n: Map :: + { Map#Domain(Map#Merge(m, n)) } + Map#Domain(Map#Merge(m, n)) == Set#Union(Map#Domain(m), Map#Domain(n))); +axiom (forall m: Map, n: Map, u: Box :: + { Map#Elements(Map#Merge(m, n))[u] } + Map#Domain(Map#Merge(m, n))[u] ==> + (!Map#Domain(n)[u] ==> Map#Elements(Map#Merge(m, n))[u] == Map#Elements(m)[u]) && + (Map#Domain(n)[u] ==> Map#Elements(Map#Merge(m, n))[u] == Map#Elements(n)[u])); + +function Map#Subtract(Map, Set): Map; +axiom (forall m: Map, s: Set :: + { Map#Domain(Map#Subtract(m, s)) } + Map#Domain(Map#Subtract(m, s)) == Set#Difference(Map#Domain(m), s)); +axiom (forall m: Map, s: Set, u: Box :: + { Map#Elements(Map#Subtract(m, s))[u] } + Map#Domain(Map#Subtract(m, s))[u] ==> + Map#Elements(Map#Subtract(m, s))[u] == Map#Elements(m)[u]); + +//equality for maps +function Map#Equal(Map, Map): bool; +axiom (forall m: Map, m': Map:: + { Map#Equal(m, m') } + Map#Equal(m, m') <==> (forall u : Box :: Map#Domain(m)[u] == Map#Domain(m')[u]) && + (forall u : Box :: Map#Domain(m)[u] ==> Map#Elements(m)[u] == Map#Elements(m')[u])); +// extensionality +axiom (forall m: Map, m': Map:: + { Map#Equal(m, m') } + Map#Equal(m, m') ==> m == m'); + +function Map#Disjoint(Map, Map): bool; +axiom (forall m: Map, m': Map :: + { Map#Disjoint(m, m') } + Map#Disjoint(m, m') <==> (forall o: Box :: {Map#Domain(m)[o]} {Map#Domain(m')[o]} !Map#Domain(m)[o] || !Map#Domain(m')[o])); + +// --------------------------------------------------------------- +// -- Axiomatization of IMaps ------------------------------------ +// --------------------------------------------------------------- + +type IMap; + +// A IMap is defined by two functions, Map#Domain and Map#Elements. + +function IMap#Domain(IMap) : Set; + +function IMap#Elements(IMap) : [Box]Box; + +axiom (forall m: IMap :: + { IMap#Domain(m) } + m == IMap#Empty() || (exists k: Box :: IMap#Domain(m)[k])); +axiom (forall m: IMap :: + { IMap#Values(m) } + m == IMap#Empty() || (exists v: Box :: IMap#Values(m)[v])); +axiom (forall m: IMap :: + { IMap#Items(m) } + m == IMap#Empty() || (exists k, v: Box :: IMap#Items(m)[$Box(#_System._tuple#2._#Make2(k, v))])); + +axiom (forall m: IMap :: + { IMap#Domain(m) } + m == IMap#Empty() <==> IMap#Domain(m) == ISet#Empty()); +axiom (forall m: IMap :: + { IMap#Values(m) } + m == IMap#Empty() <==> IMap#Values(m) == ISet#Empty()); +axiom (forall m: IMap :: + { IMap#Items(m) } + m == IMap#Empty() <==> IMap#Items(m) == ISet#Empty()); + +// The set of Values of a IMap can be obtained by the function IMap#Values, which is +// defined as follows. Remember, a ISet is defined by membership (using Boogie's +// square brackets) so we need to define what these mean for the Set +// returned by Map#Values. + +function IMap#Values(IMap) : Set; + +axiom (forall m: IMap, v: Box :: { IMap#Values(m)[v] } + IMap#Values(m)[v] == + (exists u: Box :: { IMap#Domain(m)[u] } { IMap#Elements(m)[u] } + IMap#Domain(m)[u] && + v == IMap#Elements(m)[u])); + +// The set of Items--that is, (key,value) pairs--of a Map can be obtained by the +// function IMap#Items. +// Everywhere else in this axiomatization, IMap is parameterized by types U V, +// even though Dafny only ever instantiates U V with Box Box. This makes the +// axiomatization more generic. Function IMap#Items, however, returns a set of +// pairs, and the axiomatization of pairs is Dafny specific. Therefore, the +// definition of IMap#Items here is to be considered Dafny specific. Also, note +// that it relies on the two destructors for 2-tuples. + +function IMap#Items(IMap) : Set; + +axiom (forall m: IMap, item: Box :: { IMap#Items(m)[item] } + IMap#Items(m)[item] <==> + IMap#Domain(m)[_System.Tuple2._0($Unbox(item))] && + IMap#Elements(m)[_System.Tuple2._0($Unbox(item))] == _System.Tuple2._1($Unbox(item))); + +// Here are the operations that produce Map values. +function IMap#Empty(): IMap; +axiom (forall u: Box :: + { IMap#Domain(IMap#Empty(): IMap)[u] } + !IMap#Domain(IMap#Empty(): IMap)[u]); + +function IMap#Glue([Box] bool, [Box]Box, Ty): IMap; +axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: + { IMap#Domain(IMap#Glue(a, b, t)) } + IMap#Domain(IMap#Glue(a, b, t)) == a); +axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: + { IMap#Elements(IMap#Glue(a, b, t)) } + IMap#Elements(IMap#Glue(a, b, t)) == b); +axiom (forall a: [Box]bool, b: [Box]Box, t0, t1: Ty :: + { IMap#Glue(a, b, TIMap(t0, t1)) } + // In the following line, no trigger needed, since the quantifier only gets used in negative contexts + (forall bx: Box :: a[bx] ==> $IsBox(bx, t0) && $IsBox(b[bx], t1)) + ==> + $Is(Map#Glue(a, b, TIMap(t0, t1)), TIMap(t0, t1))); + +//Build is used in displays +function IMap#Build(IMap, Box, Box): IMap; +/*axiom (forall m: IMap, u: Box, v: Box :: + { IMap#Domain(IMap#Build(m, u, v))[u] } { IMap#Elements(IMap#Build(m, u, v))[u] } + IMap#Domain(IMap#Build(m, u, v))[u] && IMap#Elements(IMap#Build(m, u, v))[u] == v);*/ + +axiom (forall m: IMap, u: Box, u': Box, v: Box :: + { IMap#Domain(IMap#Build(m, u, v))[u'] } { IMap#Elements(IMap#Build(m, u, v))[u'] } + (u' == u ==> IMap#Domain(IMap#Build(m, u, v))[u'] && + IMap#Elements(IMap#Build(m, u, v))[u'] == v) && + (u' != u ==> IMap#Domain(IMap#Build(m, u, v))[u'] == IMap#Domain(m)[u'] && + IMap#Elements(IMap#Build(m, u, v))[u'] == IMap#Elements(m)[u'])); + +//equality for imaps +function IMap#Equal(IMap, IMap): bool; +axiom (forall m: IMap, m': IMap:: + { IMap#Equal(m, m') } + IMap#Equal(m, m') <==> (forall u : Box :: IMap#Domain(m)[u] == IMap#Domain(m')[u]) && + (forall u : Box :: IMap#Domain(m)[u] ==> IMap#Elements(m)[u] == IMap#Elements(m')[u])); +// extensionality +axiom (forall m: IMap, m': IMap:: + { IMap#Equal(m, m') } + IMap#Equal(m, m') ==> m == m'); + +// IMap operations +function IMap#Merge(IMap, IMap): IMap; +axiom (forall m: IMap, n: IMap :: + { IMap#Domain(IMap#Merge(m, n)) } + IMap#Domain(IMap#Merge(m, n)) == Set#Union(IMap#Domain(m), IMap#Domain(n))); +axiom (forall m: IMap, n: IMap, u: Box :: + { IMap#Elements(IMap#Merge(m, n))[u] } + IMap#Domain(IMap#Merge(m, n))[u] ==> + (!IMap#Domain(n)[u] ==> IMap#Elements(IMap#Merge(m, n))[u] == IMap#Elements(m)[u]) && + (IMap#Domain(n)[u] ==> IMap#Elements(IMap#Merge(m, n))[u] == IMap#Elements(n)[u])); + +function IMap#Subtract(IMap, Set): IMap; +axiom (forall m: IMap, s: Set :: + { IMap#Domain(IMap#Subtract(m, s)) } + IMap#Domain(IMap#Subtract(m, s)) == Set#Difference(IMap#Domain(m), s)); +axiom (forall m: IMap, s: Set, u: Box :: + { IMap#Elements(IMap#Subtract(m, s))[u] } + IMap#Domain(IMap#Subtract(m, s))[u] ==> + IMap#Elements(IMap#Subtract(m, s))[u] == IMap#Elements(m)[u]); + +// ------------------------------------------------------------------------- +// -- Provide arithmetic wrappers to improve triggering and non-linear math +// ------------------------------------------------------------------------- + +function INTERNAL_add_boogie(x:int, y:int) : int { x + y } +function INTERNAL_sub_boogie(x:int, y:int) : int { x - y } +function INTERNAL_mul_boogie(x:int, y:int) : int { x * y } +function INTERNAL_div_boogie(x:int, y:int) : int { x div y } +function INTERNAL_mod_boogie(x:int, y:int) : int { x mod y } +function {:never_pattern true} INTERNAL_lt_boogie(x:int, y:int) : bool { x < y } +function {:never_pattern true} INTERNAL_le_boogie(x:int, y:int) : bool { x <= y } +function {:never_pattern true} INTERNAL_gt_boogie(x:int, y:int) : bool { x > y } +function {:never_pattern true} INTERNAL_ge_boogie(x:int, y:int) : bool { x >= y } + +function Mul(x, y: int): int { x * y } +function Div(x, y: int): int { x div y } +function Mod(x, y: int): int { x mod y } +function Add(x, y: int): int { x + y } +function Sub(x, y: int): int { x - y } + +#if ARITH_DISTR +axiom (forall x, y, z: int :: + { Mul(Add(x, y), z) } + Mul(Add(x, y), z) == Add(Mul(x, z), Mul(y, z))); +axiom (forall x,y,z: int :: + { Mul(x, Add(y, z)) } + Mul(x, Add(y, z)) == Add(Mul(x, y), Mul(x, z))); +//axiom (forall x, y, z: int :: +// { Mul(Sub(x, y), z) } +// Mul(Sub(x, y), z) == Sub(Mul(x, z), Mul(y, z))); +#endif +#if ARITH_MUL_DIV_MOD +axiom (forall x, y: int :: + { Div(x, y), Mod(x, y) } + { Mul(Div(x, y), y) } + y != 0 ==> + Mul(Div(x, y), y) + Mod(x, y) == x); +#endif +#if ARITH_MUL_SIGN +axiom (forall x, y: int :: + { Mul(x, y) } + ((0 <= x && 0 <= y) || (x <= 0 && y <= 0) ==> 0 <= Mul(x, y))); +#endif +#if ARITH_MUL_COMM +axiom (forall x, y: int :: + { Mul(x, y) } + Mul(x, y) == Mul(y, x)); +#endif +#if ARITH_MUL_ASSOC +axiom (forall x, y, z: int :: + { Mul(x, Mul(y, z)) } + Mul(y, z) != z && Mul(y, z) != y ==> Mul(x, Mul(y, z)) == Mul(Mul(x, y), z)); +#endif + +// ------------------------------------------------------------------------- + diff --git a/Source/DafnyCore/Prelude/DafnyPreludeCore.bpl b/Source/DafnyCore/Prelude/DafnyPreludeCore.bpl new file mode 100644 index 00000000000..0c43c370b3d --- /dev/null +++ b/Source/DafnyCore/Prelude/DafnyPreludeCore.bpl @@ -0,0 +1,1290 @@ +// Dafny prelude +// Created 9 February 2008 by Rustan Leino. +// Converted to Boogie 2 on 28 June 2008. +// Edited sequence axioms 20 October 2009 by Alex Summers. +// Modified 2014 by Dan Rosen. +// Copyright (c) 2008-2014, Microsoft. +// Copyright by the contributors to the Dafny Project +// SPDX-License-Identifier: MIT + +const $$Language$Dafny: bool uses { // To be recognizable to the ModelViewer as + axiom $$Language$Dafny; // coming from a Dafny program. +} + +// --------------------------------------------------------------- +// -- Types ------------------------------------------------------ +// --------------------------------------------------------------- + +type Ty; +type Bv0 = int; + +const unique TBool : Ty uses { + axiom Tag(TBool) == TagBool; +} +const unique TChar : Ty uses { + axiom Tag(TChar) == TagChar; +} +const unique TInt : Ty uses { + axiom Tag(TInt) == TagInt; +} +const unique TReal : Ty uses { + axiom Tag(TReal) == TagReal; +} +const unique TORDINAL : Ty uses { + axiom Tag(TORDINAL) == TagORDINAL; +} +// See for which axioms we can make use of the trigger to determine the connection. +function TBitvector(int) : Ty; +axiom (forall w: int :: { TBitvector(w) } Inv0_TBitvector(TBitvector(w)) == w); + +function TSet(Ty) : Ty; +axiom (forall t: Ty :: { TSet(t) } Inv0_TSet(TSet(t)) == t); +axiom (forall t: Ty :: { TSet(t) } Tag(TSet(t)) == TagSet); + +function TISet(Ty) : Ty; +axiom (forall t: Ty :: { TISet(t) } Inv0_TISet(TISet(t)) == t); +axiom (forall t: Ty :: { TISet(t) } Tag(TISet(t)) == TagISet); + +function TMultiSet(Ty) : Ty; +axiom (forall t: Ty :: { TMultiSet(t) } Inv0_TMultiSet(TMultiSet(t)) == t); +axiom (forall t: Ty :: { TMultiSet(t) } Tag(TMultiSet(t)) == TagMultiSet); + +function TSeq(Ty) : Ty; +axiom (forall t: Ty :: { TSeq(t) } Inv0_TSeq(TSeq(t)) == t); +axiom (forall t: Ty :: { TSeq(t) } Tag(TSeq(t)) == TagSeq); + +function TMap(Ty, Ty) : Ty; +axiom (forall t, u: Ty :: { TMap(t,u) } Inv0_TMap(TMap(t,u)) == t); +axiom (forall t, u: Ty :: { TMap(t,u) } Inv1_TMap(TMap(t,u)) == u); +axiom (forall t, u: Ty :: { TMap(t,u) } Tag(TMap(t,u)) == TagMap); + +function TIMap(Ty, Ty) : Ty; +axiom (forall t, u: Ty :: { TIMap(t,u) } Inv0_TIMap(TIMap(t,u)) == t); +axiom (forall t, u: Ty :: { TIMap(t,u) } Inv1_TIMap(TIMap(t,u)) == u); +axiom (forall t, u: Ty :: { TIMap(t,u) } Tag(TIMap(t,u)) == TagIMap); + + +function Inv0_TBitvector(Ty) : int; +function Inv0_TSet(Ty) : Ty; +function Inv0_TISet(Ty) : Ty; +function Inv0_TSeq(Ty) : Ty; +function Inv0_TMultiSet(Ty) : Ty; +function Inv0_TMap(Ty) : Ty; +function Inv1_TMap(Ty) : Ty; +function Inv0_TIMap(Ty) : Ty; +function Inv1_TIMap(Ty) : Ty; + +// -- Classes and Datatypes -- + +// -- Type Tags -- +type TyTag; +function Tag(Ty) : TyTag; + +const unique TagBool : TyTag; +const unique TagChar : TyTag; +const unique TagInt : TyTag; +const unique TagReal : TyTag; +const unique TagORDINAL : TyTag; +const unique TagSet : TyTag; +const unique TagISet : TyTag; +const unique TagMultiSet : TyTag; +const unique TagSeq : TyTag; +const unique TagMap : TyTag; +const unique TagIMap : TyTag; +const unique TagClass : TyTag; + +type TyTagFamily; +function TagFamily(Ty): TyTagFamily; + +// --------------------------------------------------------------- +// -- Literals --------------------------------------------------- +// --------------------------------------------------------------- +function {:identity} Lit(x: T): T { x } +axiom (forall x: T :: { $Box(Lit(x)) } $Box(Lit(x)) == Lit($Box(x)) ); + +// Specialize Lit to concrete types. +// These aren't logically required, but on some examples improve +// verification speed +function {:identity} LitInt(x: int): int { x } +axiom (forall x: int :: { $Box(LitInt(x)) } $Box(LitInt(x)) == Lit($Box(x)) ); + +function {:identity} LitReal(x: real): real { x } +axiom (forall x: real :: { $Box(LitReal(x)) } $Box(LitReal(x)) == Lit($Box(x)) ); + +// --------------------------------------------------------------- +// -- Characters ------------------------------------------------- +// --------------------------------------------------------------- + +#if UNICODE_CHAR +function {:inline} char#IsChar(n: int): bool { + (0 <= n && n < 55296 /* 0xD800 */) || + (57344 /* 0xE000 */ <= n && n < 1114112 /* 0x11_0000 */ ) +} +#else +function {:inline} char#IsChar(n: int): bool { + 0 <= n && n < 65536 +} +#endif + +type char; +function char#FromInt(int): char; +axiom (forall n: int :: + { char#FromInt(n) } + char#IsChar(n) ==> char#ToInt(char#FromInt(n)) == n); + +function char#ToInt(char): int; +axiom (forall ch: char :: + { char#ToInt(ch) } + char#FromInt(char#ToInt(ch)) == ch && + char#IsChar(char#ToInt(ch))); + +function char#Plus(char, char): char; +axiom (forall a: char, b: char :: + { char#Plus(a, b) } + char#Plus(a, b) == char#FromInt(char#ToInt(a) + char#ToInt(b))); + +function char#Minus(char, char): char; +axiom (forall a: char, b: char :: + { char#Minus(a, b) } + char#Minus(a, b) == char#FromInt(char#ToInt(a) - char#ToInt(b))); + +// --------------------------------------------------------------- +// -- References ------------------------------------------------- +// --------------------------------------------------------------- + +type ref; +const null: ref; + +// --------------------------------------------------------------- +// -- Boxing and unboxing ---------------------------------------- +// --------------------------------------------------------------- + +type Box; +const $ArbitraryBoxValue: Box; + +function $Box(T): Box; +function $Unbox(Box): T; +axiom (forall x : T :: { $Box(x) } $Unbox($Box(x)) == x); +axiom (forall x : Box :: { $Unbox(x): T} $Box($Unbox(x): T) == x); + + +// Corresponding entries for boxes... +// This could probably be solved by having Box also inhabit Ty +function $IsBox(Box,Ty): bool; +function $IsAllocBox(Box,Ty,Heap): bool; + +axiom (forall bx : Box :: + { $IsBox(bx, TInt) } + ( $IsBox(bx, TInt) ==> $Box($Unbox(bx) : int) == bx && $Is($Unbox(bx) : int, TInt))); +axiom (forall bx : Box :: + { $IsBox(bx, TReal) } + ( $IsBox(bx, TReal) ==> $Box($Unbox(bx) : real) == bx && $Is($Unbox(bx) : real, TReal))); +axiom (forall bx : Box :: + { $IsBox(bx, TBool) } + ( $IsBox(bx, TBool) ==> $Box($Unbox(bx) : bool) == bx && $Is($Unbox(bx) : bool, TBool))); +axiom (forall bx : Box :: + { $IsBox(bx, TChar) } + ( $IsBox(bx, TChar) ==> $Box($Unbox(bx) : char) == bx && $Is($Unbox(bx) : char, TChar))); + +// Since each bitvector type is a separate type in Boogie, the Box/Unbox axioms for bitvectors are +// generated programmatically. Except, Bv0 is given here. +axiom (forall bx : Box :: + { $IsBox(bx, TBitvector(0)) } + ( $IsBox(bx, TBitvector(0)) ==> $Box($Unbox(bx) : Bv0) == bx && $Is($Unbox(bx) : Bv0, TBitvector(0)))); + +axiom (forall bx : Box, t : Ty :: + { $IsBox(bx, TSet(t)) } + ( $IsBox(bx, TSet(t)) ==> $Box($Unbox(bx) : Set) == bx && $Is($Unbox(bx) : Set, TSet(t)))); +axiom (forall bx : Box, t : Ty :: + { $IsBox(bx, TISet(t)) } + ( $IsBox(bx, TISet(t)) ==> $Box($Unbox(bx) : ISet) == bx && $Is($Unbox(bx) : ISet, TISet(t)))); +axiom (forall bx : Box, t : Ty :: + { $IsBox(bx, TMultiSet(t)) } + ( $IsBox(bx, TMultiSet(t)) ==> $Box($Unbox(bx) : MultiSet) == bx && $Is($Unbox(bx) : MultiSet, TMultiSet(t)))); +axiom (forall bx : Box, t : Ty :: + { $IsBox(bx, TSeq(t)) } + ( $IsBox(bx, TSeq(t)) ==> $Box($Unbox(bx) : Seq) == bx && $Is($Unbox(bx) : Seq, TSeq(t)))); +axiom (forall bx : Box, s : Ty, t : Ty :: + { $IsBox(bx, TMap(s, t)) } + ( $IsBox(bx, TMap(s, t)) ==> $Box($Unbox(bx) : Map) == bx && $Is($Unbox(bx) : Map, TMap(s, t)))); +axiom (forall bx : Box, s : Ty, t : Ty :: + { $IsBox(bx, TIMap(s, t)) } + ( $IsBox(bx, TIMap(s, t)) ==> $Box($Unbox(bx) : IMap) == bx && $Is($Unbox(bx) : IMap, TIMap(s, t)))); + +axiom (forall v : T, t : Ty :: + { $IsBox($Box(v), t) } + ( $IsBox($Box(v), t) <==> $Is(v,t) )); +axiom (forall v : T, t : Ty, h : Heap :: + { $IsAllocBox($Box(v), t, h) } + ( $IsAllocBox($Box(v), t, h) <==> $IsAlloc(v,t,h) )); + +// --------------------------------------------------------------- +// -- Is and IsAlloc --------------------------------------------- +// --------------------------------------------------------------- + +// Type-argument to $Is is the /representation type/, +// the second value argument to $Is is the actual type. +function $Is(T,Ty): bool; // no heap for now +axiom(forall v : int :: { $Is(v,TInt) } $Is(v,TInt)); +axiom(forall v : real :: { $Is(v,TReal) } $Is(v,TReal)); +axiom(forall v : bool :: { $Is(v,TBool) } $Is(v,TBool)); +axiom(forall v : char :: { $Is(v,TChar) } $Is(v,TChar)); +axiom(forall v : ORDINAL :: { $Is(v,TORDINAL) } $Is(v,TORDINAL)); + +// Since every bitvector type is a separate type in Boogie, the $Is/$IsAlloc axioms +// for bitvectors are generated programatically. Except, TBitvector(0) is given here. +axiom (forall v: Bv0 :: { $Is(v, TBitvector(0)) } $Is(v, TBitvector(0))); + +axiom (forall v: Set, t0: Ty :: { $Is(v, TSet(t0)) } + $Is(v, TSet(t0)) <==> + (forall bx: Box :: { v[bx] } + v[bx] ==> $IsBox(bx, t0))); +axiom (forall v: ISet, t0: Ty :: { $Is(v, TISet(t0)) } + $Is(v, TISet(t0)) <==> + (forall bx: Box :: { v[bx] } + v[bx] ==> $IsBox(bx, t0))); +axiom (forall v: MultiSet, t0: Ty :: { $Is(v, TMultiSet(t0)) } + $Is(v, TMultiSet(t0)) <==> + (forall bx: Box :: { v[bx] } + 0 < v[bx] ==> $IsBox(bx, t0))); +axiom (forall v: MultiSet, t0: Ty :: { $Is(v, TMultiSet(t0)) } + $Is(v, TMultiSet(t0)) ==> $IsGoodMultiSet(v)); +axiom (forall v: Seq, t0: Ty :: { $Is(v, TSeq(t0)) } + $Is(v, TSeq(t0)) <==> + (forall i : int :: { Seq#Index(v, i) } + 0 <= i && i < Seq#Length(v) ==> + $IsBox(Seq#Index(v, i), t0))); + +axiom (forall v: Map, t0: Ty, t1: Ty :: + { $Is(v, TMap(t0, t1)) } + $Is(v, TMap(t0, t1)) + <==> (forall bx: Box :: + { Map#Elements(v)[bx] } { Map#Domain(v)[bx] } + Map#Domain(v)[bx] ==> + $IsBox(Map#Elements(v)[bx], t1) && + $IsBox(bx, t0))); + +axiom (forall v: Map, t0: Ty, t1: Ty :: + { $Is(v, TMap(t0, t1)) } + $Is(v, TMap(t0, t1)) ==> + $Is(Map#Domain(v), TSet(t0)) && + $Is(Map#Values(v), TSet(t1)) && + $Is(Map#Items(v), TSet(Tclass._System.Tuple2(t0, t1)))); +axiom (forall v: IMap, t0: Ty, t1: Ty :: + { $Is(v, TIMap(t0, t1)) } + $Is(v, TIMap(t0, t1)) + <==> (forall bx: Box :: + { IMap#Elements(v)[bx] } { IMap#Domain(v)[bx] } + IMap#Domain(v)[bx] ==> + $IsBox(IMap#Elements(v)[bx], t1) && + $IsBox(bx, t0))); +axiom (forall v: IMap, t0: Ty, t1: Ty :: + { $Is(v, TIMap(t0, t1)) } + $Is(v, TIMap(t0, t1)) ==> + $Is(IMap#Domain(v), TISet(t0)) && + $Is(IMap#Values(v), TISet(t1)) && + $Is(IMap#Items(v), TISet(Tclass._System.Tuple2(t0, t1)))); + +function $IsAlloc(T,Ty,Heap): bool; +axiom(forall h : Heap, v : int :: { $IsAlloc(v,TInt,h) } $IsAlloc(v,TInt,h)); +axiom(forall h : Heap, v : real :: { $IsAlloc(v,TReal,h) } $IsAlloc(v,TReal,h)); +axiom(forall h : Heap, v : bool :: { $IsAlloc(v,TBool,h) } $IsAlloc(v,TBool,h)); +axiom(forall h : Heap, v : char :: { $IsAlloc(v,TChar,h) } $IsAlloc(v,TChar,h)); +axiom(forall h : Heap, v : ORDINAL :: { $IsAlloc(v,TORDINAL,h) } $IsAlloc(v,TORDINAL,h)); + +axiom (forall v: Bv0, h: Heap :: { $IsAlloc(v, TBitvector(0), h) } $IsAlloc(v, TBitvector(0), h)); + +axiom (forall v: Set, t0: Ty, h: Heap :: { $IsAlloc(v, TSet(t0), h) } + $IsAlloc(v, TSet(t0), h) <==> + (forall bx: Box :: { v[bx] } + v[bx] ==> $IsAllocBox(bx, t0, h))); +axiom (forall v: ISet, t0: Ty, h: Heap :: { $IsAlloc(v, TISet(t0), h) } + $IsAlloc(v, TISet(t0), h) <==> + (forall bx: Box :: { v[bx] } + v[bx] ==> $IsAllocBox(bx, t0, h))); +axiom (forall v: MultiSet, t0: Ty, h: Heap :: { $IsAlloc(v, TMultiSet(t0), h) } + $IsAlloc(v, TMultiSet(t0), h) <==> + (forall bx: Box :: { v[bx] } + 0 < v[bx] ==> $IsAllocBox(bx, t0, h))); +axiom (forall v: Seq, t0: Ty, h: Heap :: { $IsAlloc(v, TSeq(t0), h) } + $IsAlloc(v, TSeq(t0), h) <==> + (forall i : int :: { Seq#Index(v, i) } + 0 <= i && i < Seq#Length(v) ==> + $IsAllocBox(Seq#Index(v, i), t0, h))); + +axiom (forall v: Map, t0: Ty, t1: Ty, h: Heap :: + { $IsAlloc(v, TMap(t0, t1), h) } + $IsAlloc(v, TMap(t0, t1), h) + <==> (forall bx: Box :: + { Map#Elements(v)[bx] } { Map#Domain(v)[bx] } + Map#Domain(v)[bx] ==> + $IsAllocBox(Map#Elements(v)[bx], t1, h) && + $IsAllocBox(bx, t0, h))); + +axiom (forall v: IMap, t0: Ty, t1: Ty, h: Heap :: + { $IsAlloc(v, TIMap(t0, t1), h) } + $IsAlloc(v, TIMap(t0, t1), h) + <==> (forall bx: Box :: + { IMap#Elements(v)[bx] } { IMap#Domain(v)[bx] } + IMap#Domain(v)[bx] ==> + $IsAllocBox(IMap#Elements(v)[bx], t1, h) && + $IsAllocBox(bx, t0, h))); + + +function $AlwaysAllocated(Ty): bool; + axiom (forall ty: Ty :: { $AlwaysAllocated(ty) } + $AlwaysAllocated(ty) ==> + (forall h: Heap, v: Box :: { $IsAllocBox(v, ty, h) } $IsBox(v, ty) ==> $IsAllocBox(v, ty, h))); + +function $OlderTag(Heap): bool; + +// --------------------------------------------------------------- +// -- Encoding of type names ------------------------------------- +// --------------------------------------------------------------- + +type ClassName; +const unique class._System.int: ClassName; +const unique class._System.bool: ClassName; +const unique class._System.set: ClassName; +const unique class._System.seq: ClassName; +const unique class._System.multiset: ClassName; + +function Tclass._System.object?(): Ty; +function Tclass._System.Tuple2(Ty, Ty): Ty; + +function /*{:never_pattern true}*/ dtype(ref): Ty; // changed from ClassName to Ty + +function TypeTuple(a: ClassName, b: ClassName): ClassName; +function TypeTupleCar(ClassName): ClassName; +function TypeTupleCdr(ClassName): ClassName; +// TypeTuple is injective in both arguments: +axiom (forall a: ClassName, b: ClassName :: { TypeTuple(a,b) } + TypeTupleCar(TypeTuple(a,b)) == a && + TypeTupleCdr(TypeTuple(a,b)) == b); + +// -- Function handles ------------------------------------------- + +type HandleType; + +function SetRef_to_SetBox(s: [ref]bool): Set; +axiom (forall s: [ref]bool, bx: Box :: { SetRef_to_SetBox(s)[bx] } + SetRef_to_SetBox(s)[bx] == s[$Unbox(bx): ref]); +axiom (forall s: [ref]bool :: { SetRef_to_SetBox(s) } + $Is(SetRef_to_SetBox(s), TSet(Tclass._System.object?()))); + +// Functions ApplyN, RequiresN, and ReadsN are generated on demand by the translator, +// but Apply1 is referred to in the prelude, so its definition is hardcoded here. +function Apply1(Ty, Ty, Heap, HandleType, Box): Box; + +// --------------------------------------------------------------- +// -- Datatypes -------------------------------------------------- +// --------------------------------------------------------------- + +type DatatypeType; + +type DtCtorId; +function DatatypeCtorId(DatatypeType): DtCtorId; + +function DtRank(DatatypeType): int; +function BoxRank(Box): int; + +axiom (forall d: DatatypeType :: {BoxRank($Box(d))} BoxRank($Box(d)) == DtRank(d)); + +// --------------------------------------------------------------- +// -- Big Ordinals ----------------------------------------------- +// --------------------------------------------------------------- + +type ORDINAL = Box; // :| There are more big ordinals than boxes + +// The following two functions give an abstracton over all ordinals. +// Function ORD#IsNat returns true when the ordinal is one of the natural +// numbers. Function ORD#Offset gives how many successors (that is, +// +1 operations) an ordinal is above the nearest lower limit ordinal. +// That is, if the ordinal is \lambda+n, then ORD#Offset returns n. +function ORD#IsNat(ORDINAL): bool; +function ORD#Offset(ORDINAL): int; +axiom (forall o:ORDINAL :: { ORD#Offset(o) } 0 <= ORD#Offset(o)); + +function {:inline} ORD#IsLimit(o: ORDINAL): bool { ORD#Offset(o) == 0 } +function {:inline} ORD#IsSucc(o: ORDINAL): bool { 0 < ORD#Offset(o) } + +function ORD#FromNat(int): ORDINAL; +axiom (forall n:int :: { ORD#FromNat(n) } + 0 <= n ==> ORD#IsNat(ORD#FromNat(n)) && ORD#Offset(ORD#FromNat(n)) == n); +axiom (forall o:ORDINAL :: { ORD#Offset(o) } { ORD#IsNat(o) } + ORD#IsNat(o) ==> o == ORD#FromNat(ORD#Offset(o))); + +function ORD#Less(ORDINAL, ORDINAL): bool; +axiom (forall o,p: ORDINAL :: { ORD#Less(o,p) } + (ORD#Less(o,p) ==> o != p) && // irreflexivity + (ORD#IsNat(o) && !ORD#IsNat(p) ==> ORD#Less(o,p)) && + (ORD#IsNat(o) && ORD#IsNat(p) ==> ORD#Less(o,p) == (ORD#Offset(o) < ORD#Offset(p))) && + (ORD#Less(o,p) && ORD#IsNat(p) ==> ORD#IsNat(o))); +// ORD#Less is trichotomous: +axiom (forall o,p: ORDINAL :: { ORD#Less(o,p), ORD#Less(p,o) } + ORD#Less(o,p) || o == p || ORD#Less(p,o)); +// ORD#Less is transitive: +axiom (forall o,p,r: ORDINAL :: + { ORD#Less(o,p), ORD#Less(p,r) } + { ORD#Less(o,p), ORD#Less(o,r) } + ORD#Less(o,p) && ORD#Less(p,r) ==> ORD#Less(o,r)); + +// ORD#LessThanLimit is a synonym of ORD#Less, introduced for more selected triggering +function ORD#LessThanLimit(ORDINAL, ORDINAL): bool; +axiom (forall o,p: ORDINAL :: { ORD#LessThanLimit(o, p) } + ORD#LessThanLimit(o, p) == ORD#Less(o, p)); + +function ORD#Plus(ORDINAL, ORDINAL): ORDINAL; +axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } + (ORD#IsNat(ORD#Plus(o,p)) ==> ORD#IsNat(o) && ORD#IsNat(p)) && + (ORD#IsNat(p) ==> + ORD#IsNat(ORD#Plus(o,p)) == ORD#IsNat(o) && + ORD#Offset(ORD#Plus(o,p)) == ORD#Offset(o) + ORD#Offset(p))); +axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } + (o == ORD#Plus(o, p) || ORD#Less(o, ORD#Plus(o, p))) && + (p == ORD#Plus(o, p) || ORD#Less(p, ORD#Plus(o, p)))); +axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } + (o == ORD#FromNat(0) ==> ORD#Plus(o, p) == p) && + (p == ORD#FromNat(0) ==> ORD#Plus(o, p) == o)); + +function ORD#Minus(ORDINAL, ORDINAL): ORDINAL; +axiom (forall o,p: ORDINAL :: { ORD#Minus(o,p) } + ORD#IsNat(p) && ORD#Offset(p) <= ORD#Offset(o) ==> + ORD#IsNat(ORD#Minus(o,p)) == ORD#IsNat(o) && + ORD#Offset(ORD#Minus(o,p)) == ORD#Offset(o) - ORD#Offset(p)); +axiom (forall o,p: ORDINAL :: { ORD#Minus(o,p) } + ORD#IsNat(p) && ORD#Offset(p) <= ORD#Offset(o) ==> + (p == ORD#FromNat(0) && ORD#Minus(o, p) == o) || + (p != ORD#FromNat(0) && ORD#Less(ORD#Minus(o, p), o))); + +// o+m+n == o+(m+n) +axiom (forall o: ORDINAL, m,n: int :: + { ORD#Plus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) } + 0 <= m && 0 <= n ==> + ORD#Plus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(m+n))); +// o-m-n == o+(m+n) +axiom (forall o: ORDINAL, m,n: int :: + { ORD#Minus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) } + 0 <= m && 0 <= n && m+n <= ORD#Offset(o) ==> + ORD#Minus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(m+n))); +// o+m-n == EITHER o+(m-n) OR o-(n-m) +axiom (forall o: ORDINAL, m,n: int :: + { ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) } + 0 <= m && 0 <= n && n <= ORD#Offset(o) + m ==> + (0 <= m - n ==> ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(m-n))) && + (m - n <= 0 ==> ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(n-m)))); +// o-m+n == EITHER o-(m-n) OR o+(n-m) +axiom (forall o: ORDINAL, m,n: int :: + { ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) } + 0 <= m && 0 <= n && n <= ORD#Offset(o) + m ==> + (0 <= m - n ==> ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(m-n))) && + (m - n <= 0 ==> ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(n-m)))); + +// --------------------------------------------------------------- +// -- Axiom contexts --------------------------------------------- +// --------------------------------------------------------------- + +// used to make sure function axioms are not used while their consistency is being checked +const $ModuleContextHeight: int; +const $FunctionContextHeight: int; + +// --------------------------------------------------------------- +// -- Layers of function encodings ------------------------------- +// --------------------------------------------------------------- + +type LayerType; +const $LZ: LayerType; +function $LS(LayerType): LayerType; +function AsFuelBottom(LayerType) : LayerType; + +function AtLayer([LayerType]A, LayerType): A; +axiom (forall f : [LayerType]A, ly : LayerType :: { AtLayer(f,ly) } AtLayer(f,ly) == f[ly]); +axiom (forall f : [LayerType]A, ly : LayerType :: { AtLayer(f,$LS(ly)) } AtLayer(f,$LS(ly)) == AtLayer(f,ly)); + +// --------------------------------------------------------------- +// -- Fields ----------------------------------------------------- +// --------------------------------------------------------------- + +type Field; + +function FDim(Field): int uses { + axiom FDim(alloc) == 0; +} + +function IndexField(int): Field; +axiom (forall i: int :: { IndexField(i) } FDim(IndexField(i)) == 1); +function IndexField_Inverse(Field): int; +axiom (forall i: int :: { IndexField(i) } IndexField_Inverse(IndexField(i)) == i); + +function MultiIndexField(Field, int): Field; +axiom (forall f: Field, i: int :: { MultiIndexField(f,i) } FDim(MultiIndexField(f,i)) == FDim(f) + 1); +function MultiIndexField_Inverse0(Field): Field; +function MultiIndexField_Inverse1(Field): int; +axiom (forall f: Field, i: int :: { MultiIndexField(f,i) } + MultiIndexField_Inverse0(MultiIndexField(f,i)) == f && + MultiIndexField_Inverse1(MultiIndexField(f,i)) == i); + +function DeclType(Field): ClassName; + +type NameFamily; +function DeclName(Field): NameFamily uses { + axiom DeclName(alloc) == allocName; +} +function FieldOfDecl(ClassName, NameFamily): Field; +axiom (forall cl : ClassName, nm: NameFamily :: + {FieldOfDecl(cl, nm): Field} + DeclType(FieldOfDecl(cl, nm): Field) == cl && DeclName(FieldOfDecl(cl, nm): Field) == nm); + +function $IsGhostField(Field): bool uses { + axiom $IsGhostField(alloc); // treat as ghost field, since it is allowed to be changed by ghost code +} +axiom (forall h: Heap, k: Heap :: { $HeapSuccGhost(h,k) } + $HeapSuccGhost(h,k) ==> + $HeapSucc(h,k) && + (forall o: ref, f: Field :: { read(k, o, f) } + !$IsGhostField(f) ==> read(h, o, f) == read(k, o, f))); + +// --------------------------------------------------------------- +// -- Allocatedness and Heap Succession -------------------------- +// --------------------------------------------------------------- + + +// $IsAlloc and $IsAllocBox are monotonic + +axiom (forall h, k : Heap, v : T, t : Ty :: + { $HeapSucc(h, k), $IsAlloc(v, t, h) } + $HeapSucc(h, k) ==> $IsAlloc(v, t, h) ==> $IsAlloc(v, t, k)); +axiom (forall h, k : Heap, bx : Box, t : Ty :: + { $HeapSucc(h, k), $IsAllocBox(bx, t, h) } + $HeapSucc(h, k) ==> $IsAllocBox(bx, t, h) ==> $IsAllocBox(bx, t, k)); + +// No axioms for $Is and $IsBox since they don't talk about the heap. + +const unique alloc: Field; +const unique allocName: NameFamily; + +// --------------------------------------------------------------- +// -- Arrays ----------------------------------------------------- +// --------------------------------------------------------------- + +function _System.array.Length(a: ref): int; +axiom (forall o: ref :: {_System.array.Length(o)} 0 <= _System.array.Length(o)); + + +// --------------------------------------------------------------- +// -- Reals ------------------------------------------------------ +// --------------------------------------------------------------- + +function Int(x: real): int { int(x) } +function Real(x: int): real { real(x) } +axiom (forall i: int :: { Int(Real(i)) } Int(Real(i)) == i); + +function {:inline} _System.real.Floor(x: real): int { Int(x) } + +// --------------------------------------------------------------- +// -- The heap --------------------------------------------------- +// --------------------------------------------------------------- +type Heap = [ref][Field]Box; +function {:inline} read(H: Heap, r: ref, f: Field) : Box { H[r][f] } +function {:inline} update(H:Heap, r:ref, f: Field, v: Box) : Heap { H[r := H[r][f := v]] } + +function $IsGoodHeap(Heap): bool; +function $IsHeapAnchor(Heap): bool; +var $Heap: Heap where $IsGoodHeap($Heap) && $IsHeapAnchor($Heap); + +// The following is used as a reference heap in places where the translation needs a heap +// but the expression generated is really one that is (at least in a correct program) +// independent of the heap. +const $OneHeap: Heap uses { + axiom $IsGoodHeap($OneHeap); +} + +function $HeapSucc(Heap, Heap): bool; +axiom (forall h: Heap, r: ref, f: Field, x: Box :: { update(h, r, f, x) } + $IsGoodHeap(update(h, r, f, x)) ==> + $HeapSucc(h, update(h, r, f, x))); +axiom (forall a,b,c: Heap :: { $HeapSucc(a,b), $HeapSucc(b,c) } + a != c ==> $HeapSucc(a,b) && $HeapSucc(b,c) ==> $HeapSucc(a,c)); +axiom (forall h: Heap, k: Heap :: { $HeapSucc(h,k) } + $HeapSucc(h,k) ==> (forall o: ref :: { read(k, o, alloc) } $Unbox(read(h, o, alloc)) ==> $Unbox(read(k, o, alloc)))); + +function $HeapSuccGhost(Heap, Heap): bool; + +// --------------------------------------------------------------- +// -- Useful macros ---------------------------------------------- +// --------------------------------------------------------------- + +// havoc everything in $Heap, except {this}+rds+nw +procedure $YieldHavoc(this: ref, rds: Set, nw: Set); + modifies $Heap; + ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } + $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> + $o == this || rds[$Box($o)] || nw[$Box($o)] ==> + read($Heap, $o, $f) == read(old($Heap), $o, $f)); + ensures $HeapSucc(old($Heap), $Heap); + +// havoc everything in $Heap, except rds-modi-{this} +procedure $IterHavoc0(this: ref, rds: Set, modi: Set); + modifies $Heap; + ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } + $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> + rds[$Box($o)] && !modi[$Box($o)] && $o != this ==> + read($Heap, $o, $f) == read(old($Heap), $o, $f)); + ensures $HeapSucc(old($Heap), $Heap); + +// havoc $Heap at {this}+modi+nw +procedure $IterHavoc1(this: ref, modi: Set, nw: Set); + modifies $Heap; + ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } + $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> + read($Heap, $o, $f) == read(old($Heap), $o, $f) || + $o == this || modi[$Box($o)] || nw[$Box($o)]); + ensures $HeapSucc(old($Heap), $Heap); + +procedure $IterCollectNewObjects(prevHeap: Heap, newHeap: Heap, this: ref, NW: Field) + returns (s: Set); + ensures (forall bx: Box :: { s[bx] } s[bx] <==> + ($Unbox(read(newHeap, this, NW)) : Set)[bx] || + ($Unbox(bx) != null && !$Unbox(read(prevHeap, $Unbox(bx):ref, alloc)) && $Unbox(read(newHeap, $Unbox(bx):ref, alloc)))); + +// --------------------------------------------------------------- +// -- Axiomatizations -------------------------------------------- +// --------------------------------------------------------------- + +// --------------------------------------------------------------- +// -- Axiomatization of sets ------------------------------------- +// --------------------------------------------------------------- + +type Set = [Box]bool; + +function Set#Card(Set): int; +axiom (forall s: Set :: { Set#Card(s) } 0 <= Set#Card(s)); + +function Set#Empty(): Set; +axiom (forall o: Box :: { Set#Empty()[o] } !Set#Empty()[o]); +axiom (forall s: Set :: { Set#Card(s) } + (Set#Card(s) == 0 <==> s == Set#Empty()) && + (Set#Card(s) != 0 ==> (exists x: Box :: s[x]))); + +// the empty set could be of anything +//axiom (forall t: Ty :: { $Is(Set#Empty() : [Box]bool, TSet(t)) } $Is(Set#Empty() : [Box]bool, TSet(t))); + +function Set#Singleton(Box): Set; +axiom (forall r: Box :: { Set#Singleton(r) } Set#Singleton(r)[r]); +axiom (forall r: Box, o: Box :: { Set#Singleton(r)[o] } Set#Singleton(r)[o] <==> r == o); +axiom (forall r: Box :: { Set#Card(Set#Singleton(r)) } Set#Card(Set#Singleton(r)) == 1); + +function Set#UnionOne(Set, Box): Set; +axiom (forall a: Set, x: Box, o: Box :: { Set#UnionOne(a,x)[o] } + Set#UnionOne(a,x)[o] <==> o == x || a[o]); +axiom (forall a: Set, x: Box :: { Set#UnionOne(a, x) } + Set#UnionOne(a, x)[x]); +axiom (forall a: Set, x: Box, y: Box :: { Set#UnionOne(a, x), a[y] } + a[y] ==> Set#UnionOne(a, x)[y]); +axiom (forall a: Set, x: Box :: { Set#Card(Set#UnionOne(a, x)) } + a[x] ==> Set#Card(Set#UnionOne(a, x)) == Set#Card(a)); +axiom (forall a: Set, x: Box :: { Set#Card(Set#UnionOne(a, x)) } + !a[x] ==> Set#Card(Set#UnionOne(a, x)) == Set#Card(a) + 1); + +function Set#Union(Set, Set): Set; +axiom (forall a: Set, b: Set, o: Box :: { Set#Union(a,b)[o] } + Set#Union(a,b)[o] <==> a[o] || b[o]); +axiom (forall a, b: Set, y: Box :: { Set#Union(a, b), a[y] } + a[y] ==> Set#Union(a, b)[y]); +axiom (forall a, b: Set, y: Box :: { Set#Union(a, b), b[y] } + b[y] ==> Set#Union(a, b)[y]); +axiom (forall a, b: Set :: { Set#Union(a, b) } + Set#Disjoint(a, b) ==> + Set#Difference(Set#Union(a, b), a) == b && + Set#Difference(Set#Union(a, b), b) == a); +// Follows from the general union axiom, but might be still worth including, because disjoint union is a common case: +// axiom (forall a, b: Set :: { Set#Card(Set#Union(a, b)) } +// Set#Disjoint(a, b) ==> +// Set#Card(Set#Union(a, b)) == Set#Card(a) + Set#Card(b)); + +function Set#Intersection(Set, Set): Set; +axiom (forall a: Set, b: Set, o: Box :: { Set#Intersection(a,b)[o] } + Set#Intersection(a,b)[o] <==> a[o] && b[o]); + +axiom (forall a, b: Set :: { Set#Union(Set#Union(a, b), b) } + Set#Union(Set#Union(a, b), b) == Set#Union(a, b)); +axiom (forall a, b: Set :: { Set#Union(a, Set#Union(a, b)) } + Set#Union(a, Set#Union(a, b)) == Set#Union(a, b)); +axiom (forall a, b: Set :: { Set#Intersection(Set#Intersection(a, b), b) } + Set#Intersection(Set#Intersection(a, b), b) == Set#Intersection(a, b)); +axiom (forall a, b: Set :: { Set#Intersection(a, Set#Intersection(a, b)) } + Set#Intersection(a, Set#Intersection(a, b)) == Set#Intersection(a, b)); +axiom (forall a, b: Set :: { Set#Card(Set#Union(a, b)) }{ Set#Card(Set#Intersection(a, b)) } + Set#Card(Set#Union(a, b)) + Set#Card(Set#Intersection(a, b)) == Set#Card(a) + Set#Card(b)); + +function Set#Difference(Set, Set): Set; +axiom (forall a: Set, b: Set, o: Box :: { Set#Difference(a,b)[o] } + Set#Difference(a,b)[o] <==> a[o] && !b[o]); +axiom (forall a, b: Set, y: Box :: { Set#Difference(a, b), b[y] } + b[y] ==> !Set#Difference(a, b)[y] ); +axiom (forall a, b: Set :: + { Set#Card(Set#Difference(a, b)) } + Set#Card(Set#Difference(a, b)) + Set#Card(Set#Difference(b, a)) + + Set#Card(Set#Intersection(a, b)) + == Set#Card(Set#Union(a, b)) && + Set#Card(Set#Difference(a, b)) == Set#Card(a) - Set#Card(Set#Intersection(a, b))); + +function Set#Subset(Set, Set): bool; +axiom (forall a: Set, b: Set :: { Set#Subset(a,b) } + Set#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] ==> b[o])); +// axiom(forall a: Set, b: Set :: +// { Set#Subset(a,b), Set#Card(a), Set#Card(b) } // very restrictive trigger +// Set#Subset(a,b) ==> Set#Card(a) <= Set#Card(b)); + + +function Set#Equal(Set, Set): bool; +axiom (forall a: Set, b: Set :: { Set#Equal(a,b) } + Set#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <==> b[o])); +axiom (forall a: Set, b: Set :: { Set#Equal(a,b) } // extensionality axiom for sets + Set#Equal(a,b) ==> a == b); + +function Set#Disjoint(Set, Set): bool; +axiom (forall a: Set, b: Set :: { Set#Disjoint(a,b) } + Set#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} !a[o] || !b[o])); + +// --------------------------------------------------------------- +// -- Axiomatization of isets ------------------------------------- +// --------------------------------------------------------------- + +type ISet = [Box]bool; + +function ISet#Empty(): Set; +axiom (forall o: Box :: { ISet#Empty()[o] } !ISet#Empty()[o]); + +// the empty set could be of anything +//axiom (forall t: Ty :: { $Is(ISet#Empty() : [Box]bool, TISet(t)) } $Is(ISet#Empty() : [Box]bool, TISet(t))); + + +function ISet#UnionOne(ISet, Box): ISet; +axiom (forall a: ISet, x: Box, o: Box :: { ISet#UnionOne(a,x)[o] } + ISet#UnionOne(a,x)[o] <==> o == x || a[o]); +axiom (forall a: ISet, x: Box :: { ISet#UnionOne(a, x) } + ISet#UnionOne(a, x)[x]); +axiom (forall a: ISet, x: Box, y: Box :: { ISet#UnionOne(a, x), a[y] } + a[y] ==> ISet#UnionOne(a, x)[y]); + +function ISet#Union(ISet, ISet): ISet; +axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Union(a,b)[o] } + ISet#Union(a,b)[o] <==> a[o] || b[o]); +axiom (forall a, b: ISet, y: Box :: { ISet#Union(a, b), a[y] } + a[y] ==> ISet#Union(a, b)[y]); +axiom (forall a, b: Set, y: Box :: { ISet#Union(a, b), b[y] } + b[y] ==> ISet#Union(a, b)[y]); +axiom (forall a, b: ISet :: { ISet#Union(a, b) } + ISet#Disjoint(a, b) ==> + ISet#Difference(ISet#Union(a, b), a) == b && + ISet#Difference(ISet#Union(a, b), b) == a); + +function ISet#Intersection(ISet, ISet): ISet; +axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Intersection(a,b)[o] } + ISet#Intersection(a,b)[o] <==> a[o] && b[o]); + +axiom (forall a, b: ISet :: { ISet#Union(ISet#Union(a, b), b) } + ISet#Union(ISet#Union(a, b), b) == ISet#Union(a, b)); +axiom (forall a, b: Set :: { ISet#Union(a, ISet#Union(a, b)) } + ISet#Union(a, ISet#Union(a, b)) == ISet#Union(a, b)); +axiom (forall a, b: ISet :: { ISet#Intersection(ISet#Intersection(a, b), b) } + ISet#Intersection(ISet#Intersection(a, b), b) == ISet#Intersection(a, b)); +axiom (forall a, b: ISet :: { ISet#Intersection(a, ISet#Intersection(a, b)) } + ISet#Intersection(a, ISet#Intersection(a, b)) == ISet#Intersection(a, b)); + + +function ISet#Difference(ISet, ISet): ISet; +axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Difference(a,b)[o] } + ISet#Difference(a,b)[o] <==> a[o] && !b[o]); +axiom (forall a, b: ISet, y: Box :: { ISet#Difference(a, b), b[y] } + b[y] ==> !ISet#Difference(a, b)[y] ); + +function ISet#Subset(ISet, ISet): bool; +axiom (forall a: ISet, b: ISet :: { ISet#Subset(a,b) } + ISet#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] ==> b[o])); + +function ISet#Equal(ISet, ISet): bool; +axiom (forall a: ISet, b: ISet :: { ISet#Equal(a,b) } + ISet#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <==> b[o])); +axiom (forall a: ISet, b: ISet :: { ISet#Equal(a,b) } // extensionality axiom for sets + ISet#Equal(a,b) ==> a == b); + +function ISet#Disjoint(ISet, ISet): bool; +axiom (forall a: ISet, b: ISet :: { ISet#Disjoint(a,b) } + ISet#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} !a[o] || !b[o])); + +// --------------------------------------------------------------- +// -- Axiomatization of multisets -------------------------------- +// --------------------------------------------------------------- + +function Math#min(a: int, b: int): int; +axiom (forall a: int, b: int :: { Math#min(a, b) } a <= b <==> Math#min(a, b) == a); +axiom (forall a: int, b: int :: { Math#min(a, b) } b <= a <==> Math#min(a, b) == b); +axiom (forall a: int, b: int :: { Math#min(a, b) } Math#min(a, b) == a || Math#min(a, b) == b); + +function Math#clip(a: int): int; +axiom (forall a: int :: { Math#clip(a) } 0 <= a ==> Math#clip(a) == a); +axiom (forall a: int :: { Math#clip(a) } a < 0 ==> Math#clip(a) == 0); + +type MultiSet = [Box]int; + +function $IsGoodMultiSet(ms: MultiSet): bool; +// ints are non-negative, used after havocing, and for conversion from sequences to multisets. +axiom (forall ms: MultiSet :: { $IsGoodMultiSet(ms) } + $IsGoodMultiSet(ms) <==> + (forall bx: Box :: { ms[bx] } 0 <= ms[bx] && ms[bx] <= MultiSet#Card(ms))); + +function MultiSet#Card(MultiSet): int; +axiom (forall s: MultiSet :: { MultiSet#Card(s) } 0 <= MultiSet#Card(s)); +axiom (forall s: MultiSet, x: Box, n: int :: { MultiSet#Card(s[x := n]) } + 0 <= n ==> MultiSet#Card(s[x := n]) == MultiSet#Card(s) - s[x] + n); + +function MultiSet#Empty(): MultiSet; +axiom (forall o: Box :: { MultiSet#Empty()[o] } MultiSet#Empty()[o] == 0); +axiom (forall s: MultiSet :: { MultiSet#Card(s) } + (MultiSet#Card(s) == 0 <==> s == MultiSet#Empty()) && + (MultiSet#Card(s) != 0 ==> (exists x: Box :: 0 < s[x]))); + +function MultiSet#Singleton(Box): MultiSet; +axiom (forall r: Box, o: Box :: { MultiSet#Singleton(r)[o] } (MultiSet#Singleton(r)[o] == 1 <==> r == o) && + (MultiSet#Singleton(r)[o] == 0 <==> r != o)); +axiom (forall r: Box :: { MultiSet#Singleton(r) } MultiSet#Singleton(r) == MultiSet#UnionOne(MultiSet#Empty(), r)); + +function MultiSet#UnionOne(MultiSet, Box): MultiSet; +// pure containment axiom (in the original multiset or is the added element) +axiom (forall a: MultiSet, x: Box, o: Box :: { MultiSet#UnionOne(a,x)[o] } + 0 < MultiSet#UnionOne(a,x)[o] <==> o == x || 0 < a[o]); +// union-ing increases count by one +axiom (forall a: MultiSet, x: Box :: { MultiSet#UnionOne(a, x) } + MultiSet#UnionOne(a, x)[x] == a[x] + 1); +// non-decreasing +axiom (forall a: MultiSet, x: Box, y: Box :: { MultiSet#UnionOne(a, x), a[y] } + 0 < a[y] ==> 0 < MultiSet#UnionOne(a, x)[y]); +// other elements unchanged +axiom (forall a: MultiSet, x: Box, y: Box :: { MultiSet#UnionOne(a, x), a[y] } + x != y ==> a[y] == MultiSet#UnionOne(a, x)[y]); +axiom (forall a: MultiSet, x: Box :: { MultiSet#Card(MultiSet#UnionOne(a, x)) } + MultiSet#Card(MultiSet#UnionOne(a, x)) == MultiSet#Card(a) + 1); + + +function MultiSet#Union(MultiSet, MultiSet): MultiSet; +// union-ing is the sum of the contents +axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Union(a,b)[o] } + MultiSet#Union(a,b)[o] == a[o] + b[o]); +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Card(MultiSet#Union(a,b)) } + MultiSet#Card(MultiSet#Union(a,b)) == MultiSet#Card(a) + MultiSet#Card(b)); + +function MultiSet#Intersection(MultiSet, MultiSet): MultiSet; +axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Intersection(a,b)[o] } + MultiSet#Intersection(a,b)[o] == Math#min(a[o], b[o])); + +// left and right pseudo-idempotence +axiom (forall a, b: MultiSet :: { MultiSet#Intersection(MultiSet#Intersection(a, b), b) } + MultiSet#Intersection(MultiSet#Intersection(a, b), b) == MultiSet#Intersection(a, b)); +axiom (forall a, b: MultiSet :: { MultiSet#Intersection(a, MultiSet#Intersection(a, b)) } + MultiSet#Intersection(a, MultiSet#Intersection(a, b)) == MultiSet#Intersection(a, b)); + +// multiset difference, a - b. clip() makes it positive. +function MultiSet#Difference(MultiSet, MultiSet): MultiSet; +axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Difference(a,b)[o] } + MultiSet#Difference(a,b)[o] == Math#clip(a[o] - b[o])); +axiom (forall a, b: MultiSet, y: Box :: { MultiSet#Difference(a, b), b[y], a[y] } + a[y] <= b[y] ==> MultiSet#Difference(a, b)[y] == 0 ); +axiom (forall a, b: MultiSet :: + { MultiSet#Card(MultiSet#Difference(a, b)) } + MultiSet#Card(MultiSet#Difference(a, b)) + MultiSet#Card(MultiSet#Difference(b, a)) + + 2 * MultiSet#Card(MultiSet#Intersection(a, b)) + == MultiSet#Card(MultiSet#Union(a, b)) && + MultiSet#Card(MultiSet#Difference(a, b)) == MultiSet#Card(a) - MultiSet#Card(MultiSet#Intersection(a, b))); + +// multiset subset means a must have at most as many of each element as b +function MultiSet#Subset(MultiSet, MultiSet): bool; +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Subset(a,b) } + MultiSet#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <= b[o])); + +function MultiSet#Equal(MultiSet, MultiSet): bool; +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Equal(a,b) } + MultiSet#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] == b[o])); +// extensionality axiom for multisets +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Equal(a,b) } + MultiSet#Equal(a,b) ==> a == b); + +function MultiSet#Disjoint(MultiSet, MultiSet): bool; +axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Disjoint(a,b) } + MultiSet#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] == 0 || b[o] == 0)); + +// conversion to a multiset. each element in the original set has duplicity 1. +function MultiSet#FromSet(Set): MultiSet; +axiom (forall s: Set, a: Box :: { MultiSet#FromSet(s)[a] } + (MultiSet#FromSet(s)[a] == 0 <==> !s[a]) && + (MultiSet#FromSet(s)[a] == 1 <==> s[a])); +axiom (forall s: Set :: { MultiSet#Card(MultiSet#FromSet(s)) } + MultiSet#Card(MultiSet#FromSet(s)) == Set#Card(s)); + +// conversion to a multiset, from a sequence. +function MultiSet#FromSeq(Seq): MultiSet uses { + axiom MultiSet#FromSeq(Seq#Empty(): Seq) == MultiSet#Empty(): MultiSet; +} + +// conversion produces a good map. +axiom (forall s: Seq :: { MultiSet#FromSeq(s) } $IsGoodMultiSet(MultiSet#FromSeq(s)) ); +// cardinality axiom +axiom (forall s: Seq :: + { MultiSet#Card(MultiSet#FromSeq(s)) } + MultiSet#Card(MultiSet#FromSeq(s)) == Seq#Length(s)); +// building axiom +axiom (forall s: Seq, v: Box :: + { MultiSet#FromSeq(Seq#Build(s, v)) } + MultiSet#FromSeq(Seq#Build(s, v)) == MultiSet#UnionOne(MultiSet#FromSeq(s), v) + ); + +// concatenation axiom +axiom (forall a: Seq, b: Seq :: + { MultiSet#FromSeq(Seq#Append(a, b)) } + MultiSet#FromSeq(Seq#Append(a, b)) == MultiSet#Union(MultiSet#FromSeq(a), MultiSet#FromSeq(b)) ); + +// update axiom +axiom (forall s: Seq, i: int, v: Box, x: Box :: + { MultiSet#FromSeq(Seq#Update(s, i, v))[x] } + 0 <= i && i < Seq#Length(s) ==> + MultiSet#FromSeq(Seq#Update(s, i, v))[x] == + MultiSet#Union(MultiSet#Difference(MultiSet#FromSeq(s), MultiSet#Singleton(Seq#Index(s,i))), MultiSet#Singleton(v))[x] ); + // i.e. MS(Update(s, i, v)) == MS(s) - {{s[i]}} + {{v}} +axiom (forall s: Seq, x: Box :: { MultiSet#FromSeq(s)[x] } + (exists i : int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && x == Seq#Index(s,i)) <==> 0 < MultiSet#FromSeq(s)[x] ); + +// --------------------------------------------------------------- +// -- Axiomatization of sequences -------------------------------- +// --------------------------------------------------------------- + +#include "PreludeSeq.bpl" + +// --------------------------------------------------------------- +// -- Axiomatization of Maps ------------------------------------- +// --------------------------------------------------------------- + +type Map; + +// A Map is defined by three functions, Map#Domain, Map#Elements, and #Map#Card. + +function Map#Domain(Map) : Set; + +function Map#Elements(Map) : [Box]Box; + +function Map#Card(Map) : int; + +axiom (forall m: Map :: { Map#Card(m) } 0 <= Map#Card(m)); + +axiom (forall m: Map :: + { Map#Card(m) } + Map#Card(m) == 0 <==> m == Map#Empty()); + +axiom (forall m: Map :: + { Map#Domain(m) } + m == Map#Empty() || (exists k: Box :: Map#Domain(m)[k])); +axiom (forall m: Map :: + { Map#Values(m) } + m == Map#Empty() || (exists v: Box :: Map#Values(m)[v])); +axiom (forall m: Map :: + { Map#Items(m) } + m == Map#Empty() || (exists k, v: Box :: Map#Items(m)[$Box(#_System._tuple#2._#Make2(k, v))])); + +axiom (forall m: Map :: + { Set#Card(Map#Domain(m)) } { Map#Card(m) } + Set#Card(Map#Domain(m)) == Map#Card(m)); +axiom (forall m: Map :: + { Set#Card(Map#Values(m)) } { Map#Card(m) } + Set#Card(Map#Values(m)) <= Map#Card(m)); +axiom (forall m: Map :: + { Set#Card(Map#Items(m)) } { Map#Card(m) } + Set#Card(Map#Items(m)) == Map#Card(m)); + +// The set of Values of a Map can be obtained by the function Map#Values, which is +// defined as follows. Remember, a Set is defined by membership (using Boogie's +// square brackets) and Map#Card, so we need to define what these mean for the Set +// returned by Map#Values. + +function Map#Values(Map) : Set; + +axiom (forall m: Map, v: Box :: { Map#Values(m)[v] } + Map#Values(m)[v] == + (exists u: Box :: { Map#Domain(m)[u] } { Map#Elements(m)[u] } + Map#Domain(m)[u] && + v == Map#Elements(m)[u])); + +// The set of Items--that is, (key,value) pairs--of a Map can be obtained by the +// function Map#Items. Again, we need to define membership of Set#Card for this +// set. Everywhere else in this axiomatization, Map is parameterized by types U V, +// even though Dafny only ever instantiates U V with Box Box. This makes the +// axiomatization more generic. Function Map#Items, however, returns a set of +// pairs, and the axiomatization of pairs is Dafny specific. Therefore, the +// definition of Map#Items here is to be considered Dafny specific. Also, note +// that it relies on the two destructors for 2-tuples. + +function Map#Items(Map) : Set; + +function #_System._tuple#2._#Make2(Box, Box) : DatatypeType; +function _System.Tuple2._0(DatatypeType) : Box; +function _System.Tuple2._1(DatatypeType) : Box; + +axiom (forall m: Map, item: Box :: { Map#Items(m)[item] } + Map#Items(m)[item] <==> + Map#Domain(m)[_System.Tuple2._0($Unbox(item))] && + Map#Elements(m)[_System.Tuple2._0($Unbox(item))] == _System.Tuple2._1($Unbox(item))); + +// Here are the operations that produce Map values. + +function Map#Empty(): Map; +axiom (forall u: Box :: + { Map#Domain(Map#Empty(): Map)[u] } + !Map#Domain(Map#Empty(): Map)[u]); + +function Map#Glue([Box]bool, [Box]Box, Ty): Map; +axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: + { Map#Domain(Map#Glue(a, b, t)) } + Map#Domain(Map#Glue(a, b, t)) == a); +axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: + { Map#Elements(Map#Glue(a, b, t)) } + Map#Elements(Map#Glue(a, b, t)) == b); +axiom (forall a: [Box]bool, b: [Box]Box, t0, t1: Ty :: + { Map#Glue(a, b, TMap(t0, t1)) } + // In the following line, no trigger needed, since the quantifier only gets used in negative contexts + (forall bx: Box :: a[bx] ==> $IsBox(bx, t0) && $IsBox(b[bx], t1)) + ==> + $Is(Map#Glue(a, b, TMap(t0, t1)), TMap(t0, t1))); + + +//Build is used in displays, and for map updates +function Map#Build(Map, Box, Box): Map; +/*axiom (forall m: Map, u: Box, v: Box :: + { Map#Domain(Map#Build(m, u, v))[u] } { Map#Elements(Map#Build(m, u, v))[u] } + Map#Domain(Map#Build(m, u, v))[u] && Map#Elements(Map#Build(m, u, v))[u] == v);*/ + +axiom (forall m: Map, u: Box, u': Box, v: Box :: + { Map#Domain(Map#Build(m, u, v))[u'] } { Map#Elements(Map#Build(m, u, v))[u'] } + (u' == u ==> Map#Domain(Map#Build(m, u, v))[u'] && + Map#Elements(Map#Build(m, u, v))[u'] == v) && + (u' != u ==> Map#Domain(Map#Build(m, u, v))[u'] == Map#Domain(m)[u'] && + Map#Elements(Map#Build(m, u, v))[u'] == Map#Elements(m)[u'])); +axiom (forall m: Map, u: Box, v: Box :: { Map#Card(Map#Build(m, u, v)) } + Map#Domain(m)[u] ==> Map#Card(Map#Build(m, u, v)) == Map#Card(m)); +axiom (forall m: Map, u: Box, v: Box :: { Map#Card(Map#Build(m, u, v)) } + !Map#Domain(m)[u] ==> Map#Card(Map#Build(m, u, v)) == Map#Card(m) + 1); + +// Map operations +function Map#Merge(Map, Map): Map; +axiom (forall m: Map, n: Map :: + { Map#Domain(Map#Merge(m, n)) } + Map#Domain(Map#Merge(m, n)) == Set#Union(Map#Domain(m), Map#Domain(n))); +axiom (forall m: Map, n: Map, u: Box :: + { Map#Elements(Map#Merge(m, n))[u] } + Map#Domain(Map#Merge(m, n))[u] ==> + (!Map#Domain(n)[u] ==> Map#Elements(Map#Merge(m, n))[u] == Map#Elements(m)[u]) && + (Map#Domain(n)[u] ==> Map#Elements(Map#Merge(m, n))[u] == Map#Elements(n)[u])); + +function Map#Subtract(Map, Set): Map; +axiom (forall m: Map, s: Set :: + { Map#Domain(Map#Subtract(m, s)) } + Map#Domain(Map#Subtract(m, s)) == Set#Difference(Map#Domain(m), s)); +axiom (forall m: Map, s: Set, u: Box :: + { Map#Elements(Map#Subtract(m, s))[u] } + Map#Domain(Map#Subtract(m, s))[u] ==> + Map#Elements(Map#Subtract(m, s))[u] == Map#Elements(m)[u]); + +//equality for maps +function Map#Equal(Map, Map): bool; +axiom (forall m: Map, m': Map:: + { Map#Equal(m, m') } + Map#Equal(m, m') <==> (forall u : Box :: Map#Domain(m)[u] == Map#Domain(m')[u]) && + (forall u : Box :: Map#Domain(m)[u] ==> Map#Elements(m)[u] == Map#Elements(m')[u])); +// extensionality +axiom (forall m: Map, m': Map:: + { Map#Equal(m, m') } + Map#Equal(m, m') ==> m == m'); + +function Map#Disjoint(Map, Map): bool; +axiom (forall m: Map, m': Map :: + { Map#Disjoint(m, m') } + Map#Disjoint(m, m') <==> (forall o: Box :: {Map#Domain(m)[o]} {Map#Domain(m')[o]} !Map#Domain(m)[o] || !Map#Domain(m')[o])); + +// --------------------------------------------------------------- +// -- Axiomatization of IMaps ------------------------------------ +// --------------------------------------------------------------- + +type IMap; + +// A IMap is defined by two functions, Map#Domain and Map#Elements. + +function IMap#Domain(IMap) : Set; + +function IMap#Elements(IMap) : [Box]Box; + +axiom (forall m: IMap :: + { IMap#Domain(m) } + m == IMap#Empty() || (exists k: Box :: IMap#Domain(m)[k])); +axiom (forall m: IMap :: + { IMap#Values(m) } + m == IMap#Empty() || (exists v: Box :: IMap#Values(m)[v])); +axiom (forall m: IMap :: + { IMap#Items(m) } + m == IMap#Empty() || (exists k, v: Box :: IMap#Items(m)[$Box(#_System._tuple#2._#Make2(k, v))])); + +axiom (forall m: IMap :: + { IMap#Domain(m) } + m == IMap#Empty() <==> IMap#Domain(m) == ISet#Empty()); +axiom (forall m: IMap :: + { IMap#Values(m) } + m == IMap#Empty() <==> IMap#Values(m) == ISet#Empty()); +axiom (forall m: IMap :: + { IMap#Items(m) } + m == IMap#Empty() <==> IMap#Items(m) == ISet#Empty()); + +// The set of Values of a IMap can be obtained by the function IMap#Values, which is +// defined as follows. Remember, a ISet is defined by membership (using Boogie's +// square brackets) so we need to define what these mean for the Set +// returned by Map#Values. + +function IMap#Values(IMap) : Set; + +axiom (forall m: IMap, v: Box :: { IMap#Values(m)[v] } + IMap#Values(m)[v] == + (exists u: Box :: { IMap#Domain(m)[u] } { IMap#Elements(m)[u] } + IMap#Domain(m)[u] && + v == IMap#Elements(m)[u])); + +// The set of Items--that is, (key,value) pairs--of a Map can be obtained by the +// function IMap#Items. +// Everywhere else in this axiomatization, IMap is parameterized by types U V, +// even though Dafny only ever instantiates U V with Box Box. This makes the +// axiomatization more generic. Function IMap#Items, however, returns a set of +// pairs, and the axiomatization of pairs is Dafny specific. Therefore, the +// definition of IMap#Items here is to be considered Dafny specific. Also, note +// that it relies on the two destructors for 2-tuples. + +function IMap#Items(IMap) : Set; + +axiom (forall m: IMap, item: Box :: { IMap#Items(m)[item] } + IMap#Items(m)[item] <==> + IMap#Domain(m)[_System.Tuple2._0($Unbox(item))] && + IMap#Elements(m)[_System.Tuple2._0($Unbox(item))] == _System.Tuple2._1($Unbox(item))); + +// Here are the operations that produce Map values. +function IMap#Empty(): IMap; +axiom (forall u: Box :: + { IMap#Domain(IMap#Empty(): IMap)[u] } + !IMap#Domain(IMap#Empty(): IMap)[u]); + +function IMap#Glue([Box] bool, [Box]Box, Ty): IMap; +axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: + { IMap#Domain(IMap#Glue(a, b, t)) } + IMap#Domain(IMap#Glue(a, b, t)) == a); +axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: + { IMap#Elements(IMap#Glue(a, b, t)) } + IMap#Elements(IMap#Glue(a, b, t)) == b); +axiom (forall a: [Box]bool, b: [Box]Box, t0, t1: Ty :: + { IMap#Glue(a, b, TIMap(t0, t1)) } + // In the following line, no trigger needed, since the quantifier only gets used in negative contexts + (forall bx: Box :: a[bx] ==> $IsBox(bx, t0) && $IsBox(b[bx], t1)) + ==> + $Is(Map#Glue(a, b, TIMap(t0, t1)), TIMap(t0, t1))); + +//Build is used in displays +function IMap#Build(IMap, Box, Box): IMap; +/*axiom (forall m: IMap, u: Box, v: Box :: + { IMap#Domain(IMap#Build(m, u, v))[u] } { IMap#Elements(IMap#Build(m, u, v))[u] } + IMap#Domain(IMap#Build(m, u, v))[u] && IMap#Elements(IMap#Build(m, u, v))[u] == v);*/ + +axiom (forall m: IMap, u: Box, u': Box, v: Box :: + { IMap#Domain(IMap#Build(m, u, v))[u'] } { IMap#Elements(IMap#Build(m, u, v))[u'] } + (u' == u ==> IMap#Domain(IMap#Build(m, u, v))[u'] && + IMap#Elements(IMap#Build(m, u, v))[u'] == v) && + (u' != u ==> IMap#Domain(IMap#Build(m, u, v))[u'] == IMap#Domain(m)[u'] && + IMap#Elements(IMap#Build(m, u, v))[u'] == IMap#Elements(m)[u'])); + +//equality for imaps +function IMap#Equal(IMap, IMap): bool; +axiom (forall m: IMap, m': IMap:: + { IMap#Equal(m, m') } + IMap#Equal(m, m') <==> (forall u : Box :: IMap#Domain(m)[u] == IMap#Domain(m')[u]) && + (forall u : Box :: IMap#Domain(m)[u] ==> IMap#Elements(m)[u] == IMap#Elements(m')[u])); +// extensionality +axiom (forall m: IMap, m': IMap:: + { IMap#Equal(m, m') } + IMap#Equal(m, m') ==> m == m'); + +// IMap operations +function IMap#Merge(IMap, IMap): IMap; +axiom (forall m: IMap, n: IMap :: + { IMap#Domain(IMap#Merge(m, n)) } + IMap#Domain(IMap#Merge(m, n)) == Set#Union(IMap#Domain(m), IMap#Domain(n))); +axiom (forall m: IMap, n: IMap, u: Box :: + { IMap#Elements(IMap#Merge(m, n))[u] } + IMap#Domain(IMap#Merge(m, n))[u] ==> + (!IMap#Domain(n)[u] ==> IMap#Elements(IMap#Merge(m, n))[u] == IMap#Elements(m)[u]) && + (IMap#Domain(n)[u] ==> IMap#Elements(IMap#Merge(m, n))[u] == IMap#Elements(n)[u])); + +function IMap#Subtract(IMap, Set): IMap; +axiom (forall m: IMap, s: Set :: + { IMap#Domain(IMap#Subtract(m, s)) } + IMap#Domain(IMap#Subtract(m, s)) == Set#Difference(IMap#Domain(m), s)); +axiom (forall m: IMap, s: Set, u: Box :: + { IMap#Elements(IMap#Subtract(m, s))[u] } + IMap#Domain(IMap#Subtract(m, s))[u] ==> + IMap#Elements(IMap#Subtract(m, s))[u] == IMap#Elements(m)[u]); + +// ------------------------------------------------------------------------- +// -- Provide arithmetic wrappers to improve triggering and non-linear math +// ------------------------------------------------------------------------- + +function INTERNAL_add_boogie(x:int, y:int) : int { x + y } +function INTERNAL_sub_boogie(x:int, y:int) : int { x - y } +function INTERNAL_mul_boogie(x:int, y:int) : int { x * y } +function INTERNAL_div_boogie(x:int, y:int) : int { x div y } +function INTERNAL_mod_boogie(x:int, y:int) : int { x mod y } +function {:never_pattern true} INTERNAL_lt_boogie(x:int, y:int) : bool { x < y } +function {:never_pattern true} INTERNAL_le_boogie(x:int, y:int) : bool { x <= y } +function {:never_pattern true} INTERNAL_gt_boogie(x:int, y:int) : bool { x > y } +function {:never_pattern true} INTERNAL_ge_boogie(x:int, y:int) : bool { x >= y } + +function Mul(x, y: int): int { x * y } +function Div(x, y: int): int { x div y } +function Mod(x, y: int): int { x mod y } +function Add(x, y: int): int { x + y } +function Sub(x, y: int): int { x - y } + +#if ARITH_DISTR +axiom (forall x, y, z: int :: + { Mul(Add(x, y), z) } + Mul(Add(x, y), z) == Add(Mul(x, z), Mul(y, z))); +axiom (forall x,y,z: int :: + { Mul(x, Add(y, z)) } + Mul(x, Add(y, z)) == Add(Mul(x, y), Mul(x, z))); +//axiom (forall x, y, z: int :: +// { Mul(Sub(x, y), z) } +// Mul(Sub(x, y), z) == Sub(Mul(x, z), Mul(y, z))); +#endif +#if ARITH_MUL_DIV_MOD +axiom (forall x, y: int :: + { Div(x, y), Mod(x, y) } + { Mul(Div(x, y), y) } + y != 0 ==> + Mul(Div(x, y), y) + Mod(x, y) == x); +#endif +#if ARITH_MUL_SIGN +axiom (forall x, y: int :: + { Mul(x, y) } + ((0 <= x && 0 <= y) || (x <= 0 && y <= 0) ==> 0 <= Mul(x, y))); +#endif +#if ARITH_MUL_COMM +axiom (forall x, y: int :: + { Mul(x, y) } + Mul(x, y) == Mul(y, x)); +#endif +#if ARITH_MUL_ASSOC +axiom (forall x, y, z: int :: + { Mul(x, Mul(y, z)) } + Mul(y, z) != z && Mul(y, z) != y ==> Mul(x, Mul(y, z)) == Mul(Mul(x, y), z)); +#endif + +// ------------------------------------------------------------------------- diff --git a/Source/DafnyCore/Prelude/Makefile b/Source/DafnyCore/Prelude/Makefile new file mode 100644 index 00000000000..1ddb294b5de --- /dev/null +++ b/Source/DafnyCore/Prelude/Makefile @@ -0,0 +1,16 @@ +DAFNY=../../../Scripts/dafny + +all: DafnyPrelude.bpl + +DafnyPrelude.bpl: PreludeSeq.bpl + # cpp is allergic to primes, so we have to do a song and dance around it + sed -e "s|'|PRIME|g" -i "" DafnyPreludeCore.bpl PreludeSeq.bpl + # also, we need to disable preprocessing of Boogie things + sed -e "s|^#if|//#if|" -i "" DafnyPreludeCore.bpl + sed -e "s|^#e|//#e|" -i "" DafnyPreludeCore.bpl + cpp -C -P DafnyPreludeCore.bpl DafnyPrelude.bpl + sed -e "s|^//#|#|" -i "" DafnyPreludeCore.bpl DafnyPrelude.bpl + sed -e "s|PRIME|'|g" -i "" DafnyPreludeCore.bpl PreludeSeq.bpl DafnyPrelude.bpl + +PreludeSeq.bpl: SequenceModel.dfy + $(DAFNY) verify SequenceModel.dfy --extract:PreludeSeq.bpl diff --git a/Source/DafnyCore/Prelude/SequenceModel.dfy b/Source/DafnyCore/Prelude/SequenceModel.dfy new file mode 100644 index 00000000000..f6627d3da20 --- /dev/null +++ b/Source/DafnyCore/Prelude/SequenceModel.dfy @@ -0,0 +1,981 @@ +module Lists { + export + reveals List, List.Length, List.At, List.Append, List.Take, List.Drop, List.Split + provides List.LengthAppend, List.AppendAt, List.AboutDrop + provides List.AppendTake, List.TakeFromAppend, List.AppendDrop, List.DropFromAppend + provides List.AppendTakeDrop, List.LengthTakeDrop + + datatype List = Nil | Cons(head: X, tail: List) + { + function Length(): nat { + if Nil? then 0 else 1 + tail.Length() + } + + function Append(xs: List): List { + match this + case Nil => xs + case Cons(x, tail) => Cons(x, tail.Append(xs)) + } + + lemma LengthAppend(xs: List) + ensures Append(xs).Length() == Length() + xs.Length() + { + } + + function At(i: nat): X + requires i < Length() + { + if i == 0 then head else tail.At(i - 1) + } + + lemma AppendAt(xs: List, i: nat) + requires i < Append(xs).Length() + ensures Append(xs).At(i) == + if i < Length() then At(i) else (LengthAppend(xs); xs.At(i - Length())) + { + } + + function Take(n: nat): List + requires n <= Length() + { + if n == 0 then Nil else Cons(head, tail.Take(n - 1)) + } + + function Drop(n: nat): List + requires n <= Length() + { + if n == 0 then this else tail.Drop(n - 1) + } + + function Split(n: nat): (split: (List, List)) + requires n <= Length() + ensures split.0.Append(split.1) == this + { + AppendTakeDrop(n); + (Take(n), Drop(n)) + } + + lemma AboutDrop(n: nat) + requires n < Length() + ensures Drop(n).Cons? + { + } + + lemma AppendTake(xs: List) + ensures (LengthAppend(xs); Append(xs).Take(Length()) == this) + { + match this + case Nil => + case Cons(x, tail) => + LengthAppend(xs); + } + + lemma TakeFromAppend(xs: List, n: nat) + requires n <= Length() + xs.Length() + ensures (LengthAppend(xs); + Append(xs).Take(n) == + if n <= Length() then Take(n) else Append(xs.Take(n - Length()))) + { + LengthAppend(xs); + } + + lemma AppendDrop(xs: List) + ensures (LengthAppend(xs); Append(xs).Drop(Length()) == xs) + { + match this + case Nil => + case Cons(x, tail) => + LengthAppend(xs); + } + + lemma DropFromAppend(xs: List, n: nat) + requires n <= Length() + xs.Length() + ensures (LengthAppend(xs); + Append(xs).Drop(n) == + if n <= Length() then Drop(n).Append(xs) else xs.Drop(n - Length())) + { + LengthAppend(xs); + } + + lemma AppendTakeDrop(i: nat) + requires i <= Length() + ensures Take(i).Append(Drop(i)) == this + { + } + + lemma LengthTakeDrop(i: nat) + requires i <= Length() + ensures Take(i).Length() == i && Drop(i).Length() == Length() - i + { + } + } +} + +module Boxes { + export + provides Box, arbitrary + + type Box(==,0) + + const arbitrary: Box +} + +// -------------------------------------------------------------------------------------------------------------------- + +module {:extract} Sequences { + export + provides Lists, Boxes + provides Seq, Length, AboutLength + provides Empty, LengthEmpty0, LengthEmpty1 + provides Index, Append, IndexAppend + provides Build, BuildInv0, BuildInv1, BuildInjective, LengthBuild + provides Update, IndexUpdate, LengthUpdate + provides Contains, SeqContainsItsElements, EmptyContainsNothing, AppendContains, BuildContains + provides Equal, AboutEqual, Extensionality + provides SameUntil, AboutSameUntil + provides Take, LengthTake, IndexTake + provides Drop, LengthDrop, IndexDrop0, IndexDrop1 + provides TakeContains, DropContains, AppendTakeDrop + provides DropNothing, TakeNothing, DropDrop + provides TakeUpdate0, TakeUpdate1, DropUpdate0, DropUpdate1, DropBuild + + import opened Lists + import opened Boxes + + // boogie: type Seq; + type {:extract_name "Seq"} Seq = List + + // boogie: function Seq#Length(Seq): int; + function {:extract_name "Seq#Length"} Length(s: Seq): int { + s.Length() + } + + // boogie: axiom (forall s: Seq :: { Seq#Length(s) } 0 <= Seq#Length(s)); + lemma {:extract_pattern Length(s)} AboutLength(s: Seq) + ensures 0 <= Length(s) + { + } + + // boogie: function Seq#Empty(): Seq; + function {:extract_name "Seq#Empty"} Empty(): Seq { + Nil + } + + // boogie: axiom Seq#Length(Seq#Empty()) == 0; + lemma {:extract_used_by Empty} LengthEmpty0() + ensures Length(Empty()) == 0 + { + } + + // boogie axiom (forall s: Seq :: { Seq#Length(s) } Seq#Length(s) == 0 ==> s == Seq#Empty()); + lemma {:extract_pattern Length(s)} LengthEmpty1(s: Seq) + requires Length(s) == 0 + ensures s == Empty() + { + } + + // boogie: function Seq#Index(Seq, int): Box; + function {:extract "Seq#Index"} Index(s: Seq, i: int): Box { + if 0 <= i < Length(s) then + s.At(i) + else + Boxes.arbitrary + } + + // boogie: function Seq#Append(Seq, Seq): Seq; + function {:extract_name "Seq#Append"} Append(s0: Seq, s1: Seq): Seq { + s0.Append(s1) + } + + // boogie: + // axiom (forall s0: Seq, s1: Seq :: { Seq#Length(Seq#Append(s0,s1)) } + // Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); + lemma {:extract_pattern Length(Append(s0, s1))} LengthAppend(s0: Seq, s1: Seq) + ensures Length(Append(s0, s1)) == Length(s0) + Length(s1) + { + s0.LengthAppend(s1); + } + + // boogie: + // axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#Index(Seq#Append(s0,s1), n) } + // (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && + // (Seq#Length(s0) <= n ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); + lemma {:extract_pattern Index(Append(s0, s1), n)} IndexAppend(s0: Seq, s1: Seq, n: int) + ensures n < Length(s0) ==> Index(Append(s0, s1), n) == Index(s0, n) + ensures Length(s0) <= n ==> Index(Append(s0, s1), n) == Index(s1, n - Length(s0)) + { + if + case n < 0 => + + case 0 <= n < Length(s0) => + if n == 0 { + calc { + Index(Append(s0, s1), 0); + Index(Cons(s0.head, Append(s0.tail, s1)), 0); + s0.head; + Index(Cons(s0.head, s0.tail), 0); + Index(s0, 0); + } + } else { + calc { + Index(Append(s0, s1), n); + // def. Append + Index(Cons(s0.head, Append(s0.tail, s1)), n); + // def. Index + Index(Append(s0.tail, s1), n - 1); + { IndexAppend(s0.tail, s1, n - 1); } + Index(s0.tail, n - 1); + // def. Index + Index(Cons(s0.head, s0.tail), n); + Index(s0, n); + } + } + + case Length(s0) <= n => + match s0 + case Nil => + calc { + Index(Append(s0, s1), n); + // def. Append + Index(s1, n); + // def. Length + Index(s1, n - Length(s0)); + } + case Cons(x, tail) => + calc { + Index(Append(s0, s1), n); + // def. Append + Index(Cons(s0.head, Append(s0.tail, s1)), n); + // def. Index + Index(Append(s0.tail, s1), n - 1); + { IndexAppend(s0.tail, s1, n - 1); } + Index(s1, n - 1 - Length(s0.tail)); + // def. Length + Index(s1, n - Length(s0)); + } + } + + // boogie: function Seq#Build(s: Seq, val: Box): Seq; + function {:extract_name "Seq#Build"} Build(s: Seq, val: Box): Seq { + s.Append(Cons(val, Nil)) + } + + // boogie: function Seq#Build_inv0(s: Seq) : Seq; + function {:extract_name "Seq#Build_inv0"} BuildInv0(s: Seq): Seq { + if s.Nil? then Nil else s.Take(s.Length() - 1) + } + // boogie: function Seq#Build_inv1(s: Seq) : Box; + function {:extract_name "Seq#Build_inv1"} BuildInv1(s: Seq): Box { + if s.Nil? then + Boxes.arbitrary + else + s.AboutDrop(s.Length() - 1); + s.Drop(s.Length() - 1).head + } + + // boogie: + // axiom (forall s: Seq, val: Box :: + // { Seq#Build(s, val) } + // Seq#Build_inv0(Seq#Build(s, val)) == s && + // Seq#Build_inv1(Seq#Build(s, val)) == val); + lemma {:extract_pattern Build(s, val)} BuildInjective(s: Seq, val: Box) + ensures BuildInv0(Build(s, val)) == s + ensures BuildInv1(Build(s, val)) == val + { + var b := Build(s, val); + var valList := Cons(val, Nil); + assert b == s.Append(valList) != Nil; + assert L: b.Length() == s.Length() + 1 by { + s.LengthAppend(valList); + } + + calc { + BuildInv0(b); + s.Append(valList).Take(b.Length() - 1); + { reveal L; } + s.Append(valList).Take(s.Length()); + { s.AppendTake(valList); } + s; + } + + calc { + BuildInv1(b); + { b.AboutDrop(b.Length() - 1); } + b.Drop(b.Length() - 1).head; + { reveal L; } + s.Append(valList).Drop(s.Length()).head; + { s.AppendDrop(valList); } + valList.head; + val; + } + } + + // boogie: + // axiom (forall s: Seq, v: Box :: + // { Seq#Build(s,v) } + // Seq#Length(Seq#Build(s,v)) == 1 + Seq#Length(s)); + lemma {:extract_pattern Build(s, v)} LengthBuild(s: Seq, v: Box) + ensures Length(Build(s, v)) == 1 + Length(s) + { + var valList := Cons(v, Nil); + assert valList.Length() == 1; + s.LengthAppend(valList); + } + + // boogie: + // axiom (forall s: Seq, i: int, v: Box :: { Seq#Index(Seq#Build(s,v), i) } + // (i == Seq#Length(s) ==> Seq#Index(Seq#Build(s,v), i) == v) && + // (i != Seq#Length(s) ==> Seq#Index(Seq#Build(s,v), i) == Seq#Index(s, i))); + lemma {:extract_pattern Index(Build(s, v), i)} IndexBuild(s: Seq, i: int, v: Box) + ensures i == Length(s) ==> Index(Build(s, v), i) == v + ensures i != Length(s) ==> Index(Build(s, v), i) == Index(s, i) + { + var b := Build(s, v); + var valList := Cons(v, Nil); + assert b == s.Append(valList) != Nil; + assert b.Length() == s.Length() + 1 by { + s.LengthAppend(valList); + } + + if 0 <= i < b.Length() { + calc { + Index(Build(s, v), i); + Index(s.Append(valList), i); + s.Append(valList).At(i); + { s.AppendAt(valList, i); } + if i < Length(s) then s.At(i) else valList.At(0); + } + } + } + + // boogie: function Seq#Update(Seq, int, Box): Seq; + function {:extract_name "Seq#Update"} Update(s: Seq, i: int, val: Box): Seq { + if !(0 <= i < s.Length()) then + s + else if i == 0 then + Cons(val, s.tail) + else + Cons(s.head, Update(s.tail, i - 1, val)) + } + + // boogie: + // axiom (forall s: Seq, i: int, v: Box :: { Seq#Length(Seq#Update(s,i,v)) } + // 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s,i,v)) == Seq#Length(s)); + lemma {:extract_pattern Length(Update(s, i, v))} LengthUpdate(s: Seq, i: int, v: Box) + requires 0 <= i < Length(s) + ensures Length(Update(s, i, v)) == Length(s) + { + } + + // boogie: + // axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Index(Seq#Update(s,i,v),n) } + // 0 <= n && n < Seq#Length(s) ==> + // (i == n ==> Seq#Index(Seq#Update(s,i,v),n) == v) && + // (i != n ==> Seq#Index(Seq#Update(s,i,v),n) == Seq#Index(s,n))); + lemma {:extract_pattern Index(Update(s, i, v), n)} IndexUpdate(s: Seq, i: int, v: Box, n: int) + requires 0 <= n < Length(s) + ensures i == n ==> Index(Update(s, i, v), n) == v + ensures i != n ==> Index(Update(s, i, v), n) == Index(s, n) + { + if 0 == i < s.Length() { + calc { + Index(Update(s, i, v), n); + { LengthUpdate(s, i, v); } + Update(s, i, v).At(n); + Cons(v, s.tail).At(n); + } + + } else if 0 < i < s.Length() { + LengthUpdate(s, i, v); + calc { + Index(Update(s, i, v), n); + Update(s, i, v).At(n); + Cons(s.head, Update(s.tail, i - 1, v)).At(n); + } + if n == 0 { + calc { + Cons(s.head, Update(s.tail, i - 1, v)).At(n); + s.head; + Cons(s.head, s.tail).At(n); + s.At(n); + Index(s, n); + } + } else { + calc { + Cons(s.head, Update(s.tail, i - 1, v)).At(n); + Update(s.tail, i - 1, v).At(n - 1); + { IndexUpdate(s.tail, i - 1, v, n - 1); } + if i == n then v else Index(s.tail, n - 1); + if i == n then v else Index(s, n); + } + } + } + } + + // boogie: function Seq#Contains(Seq, Box): bool; + predicate {:extract_name "Seq#Contains"} Contains(s: Seq, val: Box) { + exists i :: 0 <= i < Length(s) && Index(s, i) == val + } + + // boogie: + // axiom (forall s: Seq, x: Box :: { Seq#Contains(s,x) } + // Seq#Contains(s,x) <==> + // (exists i: int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && Seq#Index(s,i) == x)); + lemma {:extract_pattern Contains(s, x)} SeqContainsItsElements(s: Seq, x: Box) + ensures Contains(s, x) <==> + exists i: int {:extract_pattern Index(s, i)} :: 0 <= i < Length(s) && Index(s, i) == x + { + } + + // boogie: + // axiom (forall x: Box :: + // { Seq#Contains(Seq#Empty(), x) } + // !Seq#Contains(Seq#Empty(), x)); + lemma {:extract_pattern Contains(Empty(), x)} EmptyContainsNothing(x: Box) + ensures !Contains(Empty(), x) + { + } + + // boogie: + // axiom (forall s0: Seq, s1: Seq, x: Box :: + // { Seq#Contains(Seq#Append(s0, s1), x) } + // Seq#Contains(Seq#Append(s0, s1), x) <==> + // Seq#Contains(s0, x) || Seq#Contains(s1, x)); + lemma {:extract_pattern Contains(Append(s0, s1), x)} AppendContains(s0: Seq, s1: Seq, x: Box) + ensures Contains(Append(s0, s1), x) <==> Contains(s0, x) || Contains(s1, x) + { + var a := Append(s0, s1); + s0.LengthAppend(s1); + + if Contains(a, x) { + var i :| 0 <= i < Length(a) && Index(a, i) == x; + assert a.At(i) == if i < s0.Length() then s0.At(i) else s1.At(i - s0.Length()) by { + s0.AppendAt(s1, i); + } + + if i < s0.Length() { + assert Contains(s0, x) by { + assert 0 <= i < Length(s0) && Index(s0, i) == x; + } + } else { + var j := i - s0.Length(); + assert Contains(s1, x) by { + assert x == Index(s1, j); + } + } + } + + if Contains(s0, x) { + var i :| 0 <= i < Length(s0) && Index(s0, i) == x; + assert Contains(a, x) by { + assert Index(a, i) == x by { + s0.AppendAt(s1, i); + } + } + } + + if Contains(s1, x) { + var j :| 0 <= j < Length(s1) && Index(s1, j) == x; + var i := s0.Length() + j; + assert Contains(a, x) by { + assert Index(a, i) == x by { + s0.AppendAt(s1, i); + } + } + } + } + + // boogie: + // axiom (forall s: Seq, v: Box, x: Box :: // needed to prove things like '4 in [2,3,4]', see method TestSequences0 in SmallTests.dfy + // { Seq#Contains(Seq#Build(s, v), x) } + // Seq#Contains(Seq#Build(s, v), x) <==> (v == x || Seq#Contains(s, x))); + lemma {:extract_pattern Contains(Build(s, v), x)} BuildContains(s: Seq, v: Box, x: Box) + ensures Contains(Build(s, v), x) <==> v == x || Contains(s, x) + { + var valList := Cons(v, Nil); + var b := s.Append(valList); + assert b == Build(s, v); + + assert Ping: v == x ==> Contains(valList, x) by { + assert Index(valList, 0) == v; + } + assert Pong: Contains(valList, x) ==> v == x by { + if Contains(valList, x) { + var i :| 0 <= i < valList.Length() && Index(valList, i) == x; + assert valList.Length() == 1; + } + } + + calc { + Contains(b, x); + { AppendContains(s, valList, x); } + Contains(s, x) || Contains(valList, x); + { reveal Ping, Pong; } + v == x || Contains(s, x); + } + } + + // boogie: function Seq#Equal(Seq, Seq): bool; + predicate {:extract_name "Seq#Equal"} Equal(s0: Seq, s1: Seq) { + s0 == s1 + } + + // boogie: + // axiom (forall s0: Seq, s1: Seq :: { Seq#Equal(s0,s1) } + // Seq#Equal(s0,s1) <==> + // Seq#Length(s0) == Seq#Length(s1) && + // (forall j: int :: { Seq#Index(s0,j) } { Seq#Index(s1,j) } + // 0 <= j && j < Seq#Length(s0) ==> Seq#Index(s0,j) == Seq#Index(s1,j))); + lemma {:extract_pattern Equal(s0, s1)} AboutEqual(s0: Seq, s1: Seq) + ensures Equal(s0, s1) <==> + && Length(s0) == Length(s1) + && forall j: int {:extract_pattern Index(s0, j)} {:extract_pattern Index(s1, j)} :: + 0 <= j < Length(s0) ==> Index(s0, j) == Index(s1, j) + { + if Length(s0) == Length(s1) && forall j :: 0 <= j < Length(s0) ==> Index(s0, j) == Index(s1, j) { + assert forall s, i :: 0 <= i < Length(s) ==> Index(s, i) == s.At(i); + DatatypeEquality(s0, s1); + } + } + + lemma DatatypeEquality(s0: Seq, s1: Seq) + requires s0.Length() == s1.Length() + requires forall j :: 0 <= j < s0.Length() ==> s0.At(j) == s1.At(j) + ensures s0 == s1 + { + if s0.Length() != 0 { + assert s0.head == s1.head by { + assert s0.head == s0.At(0) && s1.head == s1.At(0); + } + var t0, t1 := s0.tail, s1.tail; + assert t0.Length() == t1.Length(); + assert forall i :: 0 <= i < t0.Length() ==> t0.At(i) == s0.At(i + 1); + assert forall i :: 0 <= i < t0.Length() ==> t0.At(i) == t1.At(i); + } + } + + // boogie: + // axiom (forall a: Seq, b: Seq :: { Seq#Equal(a,b) } // extensionality axiom for sequences + // Seq#Equal(a,b) ==> a == b); + lemma {:extract_pattern Equal(a, b)} Extensionality(a: Seq, b: Seq) + requires Equal(a, b) + ensures a == b + { + } + + // boogie: function Seq#SameUntil(Seq, Seq, int): bool; + predicate {:extract_name "Seq#SameUntil"} SameUntil(s0: Seq, s1: Seq, n: int) { + forall j :: 0 <= j < n ==> Index(s0, j) == Index(s1, j) + } + + // boogie: + // axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#SameUntil(s0,s1,n) } + // Seq#SameUntil(s0,s1,n) <==> + // (forall j: int :: { Seq#Index(s0,j) } { Seq#Index(s1,j) } + // 0 <= j && j < n ==> Seq#Index(s0,j) == Seq#Index(s1,j))); + lemma {:extract_pattern SameUntil(s0, s1, n)} AboutSameUntil(s0: Seq, s1: Seq, n: int) + ensures SameUntil(s0, s1, n) <==> + forall j: int {:extract_pattern Index(s0, j)} {:extract_pattern Index(s1, j)} :: + 0 <= j < n ==> Index(s0, j) == Index(s1, j) + { + } + + // boogie: function Seq#Take(s: Seq, howMany: int): Seq; + function {:extract_name "Seq#Take"} Take(s: Seq, howMany: int): Seq { + if howMany < 0 then + Empty() + else if howMany <= s.Length() then + s.Take(howMany) + else + s + } + + // boogie: + // axiom (forall s: Seq, n: int :: { Seq#Length(Seq#Take(s,n)) } + // 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Take(s,n)) == n); + lemma LengthTake(s: Seq, n: int) + requires 0 <= n <= Length(s) + ensures Length(Take(s, n)) == n + { + s.LengthTakeDrop(n); + } + + // boogie: + // axiom (forall s: Seq, n: int, j: int :: + // {:weight 25} + // { Seq#Index(Seq#Take(s,n), j) } + // { Seq#Index(s, j), Seq#Take(s,n) } + // 0 <= j && j < n && j < Seq#Length(s) ==> + // Seq#Index(Seq#Take(s,n), j) == Seq#Index(s, j)); + lemma {:extract_attribute "weight", 25} {:extract_pattern Index(Take(s, n), j)} {:extract_pattern Index(s, j), Take(s, n)} + IndexTake(s: Seq, n: int, j: int) + requires 0 <= j < n && j < Length(s) + ensures Index(Take(s, n), j) == Index(s, j) + { + if j != 0 && n <= s.Length() { + IndexTake(s.tail, n - 1, j - 1); + } + } + + // boogie: function Seq#Drop(s: Seq, howMany: int): Seq; + function {:extract_name "Seq#Drop"} Drop(s: Seq, howMany: int): Seq { + if 0 <= howMany <= s.Length() then + s.Drop(howMany) + else + Empty() + } + + // boogie: + // axiom (forall s: Seq, n: int :: { Seq#Length(Seq#Drop(s,n)) } + // 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s,n)) == Seq#Length(s) - n); + lemma {:extract_pattern Length(Drop(s, n))} LengthDrop(s: Seq, n: int) + requires 0 <= n <= Length(s) + ensures Length(Drop(s, n)) == Length(s) - n + { + s.LengthTakeDrop(n); + } + + // boogie: + // axiom (forall s: Seq, n: int, j: int :: + // {:weight 25} + // { Seq#Index(Seq#Drop(s,n), j) } + // 0 <= n && 0 <= j && j < Seq#Length(s)-n ==> + // Seq#Index(Seq#Drop(s,n), j) == Seq#Index(s, j+n)); + lemma {:extract_attribute "weight", 25} {:extract_pattern Index(Drop(s, n), j)} IndexDrop0(s: Seq, n: int, j: int) + requires 0 <= n && 0 <= j < Length(s) - n + ensures Index(Drop(s, n), j) == Index(s, j + n) + { + IndexDrop1(s, n, j + n); + } + + // boogie: + // axiom (forall s: Seq, n: int, k: int :: + // {:weight 25} + // { Seq#Index(s, k), Seq#Drop(s,n) } + // 0 <= n && n <= k && k < Seq#Length(s) ==> + // Seq#Index(Seq#Drop(s,n), k-n) == Seq#Index(s, k)); + lemma {:extract_attribute "weight", 25} {:extract_pattern Index(s, k), Drop(s, n)} IndexDrop1(s: Seq, n: int, k: int) + requires 0 <= n <= k < Length(s) + ensures Index(Drop(s, n), k - n) == Index(s, k) + { + if n != 0 { + IndexDrop1(s.tail, n - 1, k - 1); + } + } + + // boogie: + // axiom (forall s: Seq, n: int, x: Box :: + // { Seq#Contains(Seq#Take(s, n), x) } + // Seq#Contains(Seq#Take(s, n), x) <==> + // (exists i: int :: { Seq#Index(s, i) } + // 0 <= i && i < n && i < Seq#Length(s) && Seq#Index(s, i) == x)); + lemma {:extract_pattern Contains(Take(s, n), x)} TakeContains(s: Seq, n: int, x: Box) + ensures Contains(Take(s, n), x) <==> + exists i: int {:extract_pattern Index(s, i)} :: 0 <= i < n && i < Length(s) && Index(s, i) == x + { + if + case n < 0 => + calc { + Contains(Take(s, n), x); + { assert Take(s, n) == Empty(); } + Contains(Empty(), x); + false; + exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; + } + + case 0 <= n <= s.Length() => + var (prefix, suffix) := s.Split(n); + var t := s.Take(n); + assert t == prefix; + assert t.Length() == n by { + s.LengthTakeDrop(n); + } + assert L: forall i :: 0 <= i < Length(t) ==> Index(t, i) == Index(s, i) by { + forall i | 0 <= i < Length(t) + ensures Index(t, i) == Index(s, i) + { + prefix.AppendAt(suffix, i); + } + } + + calc { + Contains(t, x); + exists i :: 0 <= i < Length(t) && Index(t, i) == x; + { reveal L; } + exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; + } + + case s.Length() < n => + calc { + Contains(Take(s, n), x); + Contains(s, x); + exists i :: 0 <= i < Length(s) && Index(s, i) == x; + { assert Length(s) < n; } + exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; + } + } + + // boogie: + // axiom (forall s: Seq, n: int, x: Box :: + // { Seq#Contains(Seq#Drop(s, n), x) } + // Seq#Contains(Seq#Drop(s, n), x) <==> + // (exists i: int :: { Seq#Index(s, i) } + // 0 <= n && n <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); + lemma {:extract_pattern Contains(Drop(s, n), x)} DropContains(s: Seq, n: int, x: Box) + ensures Contains(Drop(s, n), x) <==> + exists i: int {:extract_pattern Index(s, i)} :: 0 <= n <= i < Length(s) && Index(s, i) == x + { + if 0 <= n <= s.Length() { + var (prefix, suffix) := s.Split(n); + var t := s.Take(n); + assert t == prefix; + assert t.Length() == n by { + s.LengthTakeDrop(n); + } + assert L: forall i :: 0 <= i < Length(suffix) ==> Index(suffix, i) == Index(s, n + i) by { + forall i | 0 <= i < Length(suffix) { + calc { + Index(s, n + i); + { prefix.LengthAppend(suffix); } + s.At(n + i); + { prefix.AppendAt(suffix, n + i); } + suffix.At(i); + Index(suffix, i); + } + } + } + + if Contains(Drop(s, n), x) { + var j :| 0 <= j < Length(suffix) && Index(suffix, j) == x; + var i := n + j; + assert 0 <= n <= i < Length(s) by { + prefix.LengthAppend(suffix); + } + assert Index(s, i) == x by { + reveal L; + } + } + + if i :| 0 <= n <= i < Length(s) && Index(s, i) == x { + var j := i - n; + assert 0 <= j < Length(suffix) by { + prefix.LengthAppend(suffix); + } + assert Index(suffix, j) == x by { + reveal L; + } + } + + } else { + calc { + Contains(Drop(s, n), x); + Contains(Empty(), x); + false; + exists i :: 0 <= n <= i < Length(s) && Index(s, i) == x; + } + } + } + + // boogie: + // axiom (forall s, t: Seq, n: int :: + // { Seq#Take(Seq#Append(s, t), n) } + // { Seq#Drop(Seq#Append(s, t), n) } + // n == Seq#Length(s) + // ==> + // Seq#Take(Seq#Append(s, t), n) == s && + // Seq#Drop(Seq#Append(s, t), n) == t); + lemma {:extract_pattern Take(Append(s, t), n)} {:extract_pattern Drop(Append(s, t), n)} AppendTakeDrop(s: Seq, t: Seq, n: int) + requires n == Length(s) + ensures Take(Append(s, t), n) == s + ensures Drop(Append(s, t), n) == t + { + var a := Append(s, t); + assert 0 <= n <= Length(a) by { + s.LengthAppend(t); + } + s.AppendTake(t); + s.AppendDrop(t); + } + + // Additional axioms about common things + + // boogie: + // axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } + // n == 0 ==> Seq#Drop(s, n) == s); + lemma {:extract_pattern Drop(s, n)} DropNothing(s: Seq, n: int) + requires n == 0 + ensures Drop(s, n) == s + { + } + + // boogie: + // axiom (forall s: Seq, n: int :: { Seq#Take(s, n) } + // n == 0 ==> Seq#Take(s, n) == Seq#Empty()); + lemma {: extract_pattern Take(s, n)} TakeNothing(s: Seq, n: int) + requires n == 0 + ensures Take(s, n) == Empty() + { + } + + // boogie: + // axiom (forall s: Seq, m, n: int :: { Seq#Drop(Seq#Drop(s, m), n) } + // 0 <= m && 0 <= n && m+n <= Seq#Length(s) ==> + // Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m+n)); + lemma {:extract_pattern Drop(Drop(s, m), n)} DropDrop(s: Seq, m: int, n: int) + requires 0 <= m && 0 <= n && m + n <= Length(s) + ensures Drop(Drop(s, m), n) == Drop(s, m + n) + { + if m != 0 { + DropDrop(s.tail, m - 1, n); + } + } + + // Commutability of Take and Drop with Update. + + // boogie: + // axiom (forall s: Seq, i: int, v: Box, n: int :: + // { Seq#Take(Seq#Update(s, i, v), n) } + // 0 <= i && i < n && n <= Seq#Length(s) ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v) ); + lemma {:extract_pattern Take(Update(s, i, v), n)} TakeUpdate0(s: Seq, i: int, v: Box, n: int) + requires 0 <= i < n <= Length(s) + ensures Take(Update(s, i, v), n) == Update(Take(s, n), i, v) + decreases i + { + if i != 0 { + TakeUpdate0(s.tail, i - 1, v, n - 1); + } + } + + // boogie: + // axiom (forall s: Seq, i: int, v: Box, n: int :: + // { Seq#Take(Seq#Update(s, i, v), n) } + // n <= i && i < Seq#Length(s) ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); + lemma {:extract_pattern Take(Update(s, i, v), n)} TakeUpdate1(s: Seq, i: int, v: Box, n: int) + requires n <= i < Length(s) + ensures Take(Update(s, i, v), n) == Take(s, n) + { + if 0 <= n && i != 0 { + TakeUpdate1(s.tail, i - 1, v, n - 1); + } + } + + // boogie: + // axiom (forall s: Seq, i: int, v: Box, n: int :: + // { Seq#Drop(Seq#Update(s, i, v), n) } + // 0 <= n && n <= i && i < Seq#Length(s) ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i-n, v) ); + lemma {:extract_pattern Drop(Update(s, i, v), n)} DropUpdate0(s: Seq, i: int, v: Box, n: int) + requires 0 <= n <= i < Length(s) + ensures Drop(Update(s, i, v), n) == Update(Drop(s, n), i - n, v) + { + if n != 0 { + DropUpdate0(s.tail, i - 1, v, n - 1); + } + } + + // boogie: + // axiom (forall s: Seq, i: int, v: Box, n: int :: + // { Seq#Drop(Seq#Update(s, i, v), n) } + // 0 <= i && i < n && n <= Seq#Length(s) ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); + lemma {:extract_pattern Drop(Update(s, i, v), n)} DropUpdate1(s: Seq, i: int, v: Box, n: int) + requires 0 <= i < n <= Length(s) + ensures Drop(Update(s, i, v), n) == Drop(s, n) + { + if i != 0 { + DropUpdate1(s.tail, i - 1, v, n - 1); + } + } + + // drop commutes with build. + + // boogie: + // axiom (forall s: Seq, v: Box, n: int :: + // { Seq#Drop(Seq#Build(s, v), n) } + // 0 <= n && n <= Seq#Length(s) ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v) ); + lemma {:extract_pattern Drop(Build(s, v), n)} DropBuild(s: Seq, v: Box, n: int) + requires 0 <= n <= Length(s) + ensures Drop(Build(s, v), n) == Build(Drop(s, n), v) + { + if n != 0 { + calc { + Drop(Build(s, v), n); + Drop(Cons(s.head, Build(s.tail, v)), n); + Drop(Build(s.tail, v), n - 1); + { DropBuild(s.tail, v, n - 1); } + Build(Drop(s.tail, n - 1), v); + Build(Drop(s, n), v); + } + } + } +} + + + +/* The following axioms also mention Seq#... symbols. They also mention $Is, the heap, and/or datatypes, so they + * are not part of the model above. + + +axiom (forall v: Seq, t0: Ty :: { $Is(v, TSeq(t0)) } + $Is(v, TSeq(t0)) <==> + (forall i : int :: { Seq#Index(v, i) } + 0 <= i && i < Seq#Length(v) ==> + $IsBox(Seq#Index(v, i), t0))); + +// Build preserves $Is +axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s,bx),TSeq(t)) } + $Is(s,TSeq(t)) && $IsBox(bx,t) ==> $Is(Seq#Build(s,bx),TSeq(t))); + +function Seq#Create(ty: Ty, heap: Heap, len: int, init: HandleType): Seq; +axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType :: + { Seq#Length(Seq#Create(ty, heap, len, init): Seq) } + $IsGoodHeap(heap) && 0 <= len ==> + Seq#Length(Seq#Create(ty, heap, len, init): Seq) == len); +axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: + { Seq#Index(Seq#Create(ty, heap, len, init), i) } + $IsGoodHeap(heap) && 0 <= i && i < len ==> + Seq#Index(Seq#Create(ty, heap, len, init), i) == Apply1(TInt, ty, heap, init, $Box(i))); + +function Seq#FromArray(h: Heap, a: ref): Seq; +axiom (forall h: Heap, a: ref :: + { Seq#Length(Seq#FromArray(h,a)) } + Seq#Length(Seq#FromArray(h, a)) == _System.array.Length(a)); +axiom (forall h: Heap, a: ref :: + { Seq#FromArray(h, a) } + (forall i: int :: + // it's important to include both triggers, so that assertions about the + // the relation between the array and the sequence can be proved in either + // direction + { read(h, a, IndexField(i)) } + { Seq#Index(Seq#FromArray(h, a): Seq, i) } + 0 <= i && + i < Seq#Length(Seq#FromArray(h, a)) // this will trigger the previous axiom to get a connection with _System.array.Length(a) + ==> + Seq#Index(Seq#FromArray(h, a), i) == read(h, a, IndexField(i)))); +axiom (forall h0, h1: Heap, a: ref :: + { Seq#FromArray(h1, a), $HeapSucc(h0, h1) } + $IsGoodHeap(h0) && $IsGoodHeap(h1) && $HeapSucc(h0, h1) && h0[a] == h1[a] + ==> + Seq#FromArray(h0, a) == Seq#FromArray(h1, a)); +axiom (forall h: Heap, i: int, v: Box, a: ref :: + { Seq#FromArray(update(h, a, IndexField(i), v), a) } + 0 <= i && i < _System.array.Length(a) ==> Seq#FromArray(update(h, a, IndexField(i), v), a) == Seq#Update(Seq#FromArray(h, a), i, v) ); + +// Extension axiom, triggers only on Takes from arrays. +axiom (forall h: Heap, a: ref, n0, n1: int :: + { Seq#Take(Seq#FromArray(h, a), n0), Seq#Take(Seq#FromArray(h, a), n1) } + n0 + 1 == n1 && 0 <= n0 && n1 <= _System.array.Length(a) ==> Seq#Take(Seq#FromArray(h, a), n1) == Seq#Build(Seq#Take(Seq#FromArray(h, a), n0), read(h, a, IndexField(n0): Field)) ); + +function Seq#Rank(Seq): int; +axiom (forall s: Seq, i: int :: + { DtRank($Unbox(Seq#Index(s, i)): DatatypeType) } + 0 <= i && i < Seq#Length(s) ==> DtRank($Unbox(Seq#Index(s, i)): DatatypeType) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int :: + { Seq#Rank(Seq#Drop(s, i)) } + 0 < i && i <= Seq#Length(s) ==> Seq#Rank(Seq#Drop(s, i)) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int :: + { Seq#Rank(Seq#Take(s, i)) } + 0 <= i && i < Seq#Length(s) ==> Seq#Rank(Seq#Take(s, i)) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int, j: int :: + { Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) } + 0 <= i && i < j && j <= Seq#Length(s) ==> Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) < Seq#Rank(s) ); + +*/ diff --git a/Source/DafnyCore/Prelude/Sequences.bpl b/Source/DafnyCore/Prelude/Sequences.bpl new file mode 100644 index 00000000000..f4cdeeb565e --- /dev/null +++ b/Source/DafnyCore/Prelude/Sequences.bpl @@ -0,0 +1,177 @@ + +type Seq; + +function Seq#Length(s: Seq) : int; + +axiom (forall s: Seq :: { Seq#Length(s) } 0 <= Seq#Length(s)); + +function Seq#Empty() : Seq +uses { +axiom Seq#Length(Seq#Empty()) == 0; +} + +axiom (forall s: Seq :: { Seq#Length(s) } Seq#Length(s) == 0 ==> s == Seq#Empty()); + +function Seq#Append(s0: Seq, s1: Seq) : Seq; + +axiom (forall s0: Seq, s1: Seq :: + { Seq#Length(Seq#Append(s0, s1)) } + Seq#Length(Seq#Append(s0, s1)) == Seq#Length(s0) + Seq#Length(s1)); + +axiom (forall s0: Seq, s1: Seq, n: int :: + { Index(Seq#Append(s0, s1), n) } + (n < Seq#Length(s0) ==> Index(Seq#Append(s0, s1), n) == Index(s0, n)) + && (Seq#Length(s0) <= n + ==> Index(Seq#Append(s0, s1), n) == Index(s1, n - Seq#Length(s0)))); + +function Seq#Build(s: Seq, val: Box) : Seq; + +function Seq#Build_inv0(s: Seq) : Seq; + +function Seq#Build_inv1(s: Seq) : Box; + +axiom (forall s: Seq, val: Box :: + { Seq#Build(s, val) } + Seq#Build_inv0(Seq#Build(s, val)) == s + && Seq#Build_inv1(Seq#Build(s, val)) == val); + +axiom (forall s: Seq, v: Box :: + { Seq#Build(s, v) } + Seq#Length(Seq#Build(s, v)) == 1 + Seq#Length(s)); + +axiom (forall s: Seq, i: int, v: Box :: + { Index(Seq#Build(s, v), i) } + (i == Seq#Length(s) ==> Index(Seq#Build(s, v), i) == v) + && (i != Seq#Length(s) ==> Index(Seq#Build(s, v), i) == Index(s, i))); + +function Seq#Update(s: Seq, i: int, val: Box) : Seq; + +axiom (forall s: Seq, i: int, v: Box :: + { Seq#Length(Seq#Update(s, i, v)) } + 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s, i, v)) == Seq#Length(s)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Index(Seq#Update(s, i, v), n) } + 0 <= n && n < Seq#Length(s) + ==> (i == n ==> Index(Seq#Update(s, i, v), n) == v) + && (i != n ==> Index(Seq#Update(s, i, v), n) == Index(s, n))); + +function Seq#Contains(s: Seq, val: Box) : bool; + +axiom (forall s: Seq, x: Box :: + { Seq#Contains(s, x) } + Seq#Contains(s, x) + <==> (forall i: int :: + { Index(s, i) } + 0 <= i && i < Seq#Length(s) && Index(s, i) == x)); + +axiom (forall x: Box :: + { Seq#Contains(Seq#Empty(), x) } + -Seq#Contains(Seq#Empty(), x)); + +axiom (forall s0: Seq, s1: Seq, x: Box :: + { Seq#Contains(Seq#Append(s0, s1), x) } + Seq#Contains(Seq#Append(s0, s1), x) + <==> Seq#Contains(s0, x) || Seq#Contains(s1, x)); + +axiom (forall s: Seq, v: Box, x: Box :: + { Seq#Contains(Seq#Build(s, v), x) } + Seq#Contains(Seq#Build(s, v), x) <==> v == x || Seq#Contains(s, x)); + +function Seq#Equal(s0: Seq, s1: Seq) : bool; + +axiom (forall s0: Seq, s1: Seq :: + { Seq#Equal(s0, s1) } + Seq#Equal(s0, s1) + <==> Seq#Length(s0) == Seq#Length(s1) + && (forall j: int :: + { Index(s1, j) } { Index(s0, j) } + 0 <= j && j < Seq#Length(s0) ==> Index(s0, j) == Index(s1, j))); + +axiom (forall a: Seq, b: Seq :: { Seq#Equal(a, b) } Seq#Equal(a, b) ==> a == b); + +function Seq#SameUntil(s0: Seq, s1: Seq, n: int) : bool; + +axiom (forall s0: Seq, s1: Seq, n: int :: + { Seq#SameUntil(s0, s1, n) } + Seq#SameUntil(s0, s1, n) + <==> (forall j: int :: + { Index(s1, j) } { Index(s0, j) } + 0 <= j && j < n ==> Index(s0, j) == Index(s1, j))); + +function Seq#Take(s: Seq, howMany: int) : Seq; + +axiom (forall s: Seq, n: int, j: int :: + {:weight 25} { Index(s, j), Seq#Take(s, n) } { Index(Seq#Take(s, n), j) } + 0 <= j && j < n && j < Seq#Length(s) ==> Index(Seq#Take(s, n), j) == Index(s, j)); + +function Seq#Drop(s: Seq, howMany: int) : Seq; + +axiom (forall s: Seq, n: int :: + { Seq#Length(Seq#Drop(s, n)) } + 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s, n)) == Seq#Length(s) - n); + +axiom (forall s: Seq, n: int, j: int :: + {:weight 25} { Index(Seq#Drop(s, n), j) } + 0 <= n && 0 <= j && j < Seq#Length(s) - n + ==> Index(Seq#Drop(s, n), j) == Index(s, j + n)); + +axiom (forall s: Seq, n: int, k: int :: + {:weight 25} { Index(s, k), Seq#Drop(s, n) } + 0 <= n && n <= k && k < Seq#Length(s) + ==> Index(Seq#Drop(s, n), k - n) == Index(s, k)); + +axiom (forall s: Seq, n: int, x: Box :: + { Seq#Contains(Seq#Take(s, n), x) } + Seq#Contains(Seq#Take(s, n), x) + <==> (forall i: int :: + { Index(s, i) } + 0 <= i && i < n && i < Seq#Length(s) && Index(s, i) == x)); + +axiom (forall s: Seq, n: int, x: Box :: + { Seq#Contains(Seq#Drop(s, n), x) } + Seq#Contains(Seq#Drop(s, n), x) + <==> (forall i: int :: + { Index(s, i) } + 0 <= n && n <= i && i < Seq#Length(s) && Index(s, i) == x)); + +axiom (forall s: Seq, t: Seq, n: int :: + { Seq#Drop(Seq#Append(s, t), n) } { Seq#Take(Seq#Append(s, t), n) } + n == Seq#Length(s) + ==> Seq#Take(Seq#Append(s, t), n) == s && Seq#Drop(Seq#Append(s, t), n) == t); + +axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } n == 0 ==> Seq#Drop(s, n) == s); + +axiom (forall s: Seq, n: int :: + { Seq#Take(s, n) } + n == 0 ==> Seq#Take(s, n) == Seq#Empty()); + +axiom (forall s: Seq, m: int, n: int :: + { Seq#Drop(Seq#Drop(s, m), n) } + 0 <= m && 0 <= n && m + n <= Seq#Length(s) + ==> Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m + n)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Take(Seq#Update(s, i, v), n) } + 0 <= i && i < n && n <= Seq#Length(s) + ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Take(Seq#Update(s, i, v), n) } + n <= i && i < Seq#Length(s) + ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Drop(Seq#Update(s, i, v), n) } + 0 <= n && n <= i && i < Seq#Length(s) + ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i - n, v)); + +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Drop(Seq#Update(s, i, v), n) } + 0 <= i && i < n && n <= Seq#Length(s) + ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); + +axiom (forall s: Seq, v: Box, n: int :: + { Seq#Drop(Seq#Build(s, v), n) } + 0 <= n && n <= Seq#Length(s) + ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v)); From 727c9cd83d12c58df25e0b87f9fe38009e5dd056 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 12:43:22 -0700 Subject: [PATCH 11/52] Ignore files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 34f20c8c80f..c14b2cfbd45 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,5 @@ Source/IntegrationTests/TestFiles/LitTests/LitTest/**/*.deps.json Source/IntegrationTests/TestFiles/LitTests/LitTest/**/*.csproj /Source/IntegrationTests/TestFiles/LitTests/LitTest/pythonmodule/multimodule/PythonModule2 /Source/IntegrationTests/TestFiles/LitTests/LitTest/pythonmodule/singlemodule/dafnysource/PythonModule1 +/Source/DafnyCore/Prelude/DafnyPrelude.bpl +/Source/DafnyCore/Prelude/PreludeSeq.bpl From 01eeec59adc038ebd4260a4a472f5a337279684a Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 12:53:42 -0700 Subject: [PATCH 12/52] chore: Mostly whitespace changes --- Source/DafnyCore/DafnyPrelude.bpl | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index d82cd33cc1c..ab6a29f31bb 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -958,30 +958,35 @@ axiom (forall s: Seq, x: Box :: { MultiSet#FromSeq(s)[x] } // -- Axiomatization of sequences -------------------------------- // --------------------------------------------------------------- + + type Seq; -function Seq#Length(Seq): int; +function Seq#Length(s: Seq) : int; + axiom (forall s: Seq :: { Seq#Length(s) } 0 <= Seq#Length(s)); -function Seq#Empty(): Seq uses { - axiom (Seq#Length(Seq#Empty(): Seq) == 0); +function Seq#Empty() : Seq +uses { +axiom Seq#Length(Seq#Empty()) == 0; } axiom (forall s: Seq :: { Seq#Length(s) } (Seq#Length(s) == 0 ==> s == Seq#Empty()) + ); // The following would be a nice fact to include, because it would enable verifying the // GenericPick.SeqPick* methods in Test/dafny0/SmallTests.dfy. However, it substantially // slows down performance on some other tests, including running seemingly forever on // some. -// && (Seq#Length(s) != 0 ==> (exists x: Box :: Seq#Contains(s, x))) - ); +// axiom (forall s: Seq :: { Seq#Length(s) } +// (Seq#Length(s) != 0 ==> (exists x: Box :: Seq#Contains(s, x))) +// ); + // The empty sequence $Is any type //axiom (forall t: Ty :: {$Is(Seq#Empty(): Seq, TSeq(t))} $Is(Seq#Empty(): Seq, TSeq(t))); -function Seq#Singleton(Box): Seq; -axiom (forall t: Box :: { Seq#Length(Seq#Singleton(t)) } Seq#Length(Seq#Singleton(t)) == 1); +function Seq#Build(s: Seq, val: Box) : Seq; -function Seq#Build(s: Seq, val: Box): Seq; function Seq#Build_inv0(s: Seq) : Seq; function Seq#Build_inv1(s: Seq) : Box; axiom (forall s: Seq, val: Box :: From 827bba1ffad1e0cb2f1888f855d8673b7d283526 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 12:58:16 -0700 Subject: [PATCH 13/52] Rename files to have better names --- .gitignore | 2 +- Source/DafnyCore/Prelude/Makefile | 18 +++++++++--------- .../{DafnyPreludeCore.bpl => PreludeCore.bpl} | 2 +- .../{SequenceModel.dfy => Sequences.dfy} | 0 4 files changed, 11 insertions(+), 11 deletions(-) rename Source/DafnyCore/Prelude/{DafnyPreludeCore.bpl => PreludeCore.bpl} (99%) rename Source/DafnyCore/Prelude/{SequenceModel.dfy => Sequences.dfy} (100%) diff --git a/.gitignore b/.gitignore index c14b2cfbd45..beb07d34a9c 100644 --- a/.gitignore +++ b/.gitignore @@ -89,4 +89,4 @@ Source/IntegrationTests/TestFiles/LitTests/LitTest/**/*.csproj /Source/IntegrationTests/TestFiles/LitTests/LitTest/pythonmodule/multimodule/PythonModule2 /Source/IntegrationTests/TestFiles/LitTests/LitTest/pythonmodule/singlemodule/dafnysource/PythonModule1 /Source/DafnyCore/Prelude/DafnyPrelude.bpl -/Source/DafnyCore/Prelude/PreludeSeq.bpl +/Source/DafnyCore/Prelude/Sequences.bpl diff --git a/Source/DafnyCore/Prelude/Makefile b/Source/DafnyCore/Prelude/Makefile index 1ddb294b5de..4dedea4f27c 100644 --- a/Source/DafnyCore/Prelude/Makefile +++ b/Source/DafnyCore/Prelude/Makefile @@ -2,15 +2,15 @@ DAFNY=../../../Scripts/dafny all: DafnyPrelude.bpl -DafnyPrelude.bpl: PreludeSeq.bpl +DafnyPrelude.bpl: Sequences.bpl # cpp is allergic to primes, so we have to do a song and dance around it - sed -e "s|'|PRIME|g" -i "" DafnyPreludeCore.bpl PreludeSeq.bpl + sed -e "s|'|PRIME|g" -i "" PreludeCore.bpl Sequences.bpl # also, we need to disable preprocessing of Boogie things - sed -e "s|^#if|//#if|" -i "" DafnyPreludeCore.bpl - sed -e "s|^#e|//#e|" -i "" DafnyPreludeCore.bpl - cpp -C -P DafnyPreludeCore.bpl DafnyPrelude.bpl - sed -e "s|^//#|#|" -i "" DafnyPreludeCore.bpl DafnyPrelude.bpl - sed -e "s|PRIME|'|g" -i "" DafnyPreludeCore.bpl PreludeSeq.bpl DafnyPrelude.bpl + sed -e "s|^#if|//#if|" -i "" PreludeCore.bpl + sed -e "s|^#e|//#e|" -i "" PreludeCore.bpl + cpp -C -P PreludeCore.bpl DafnyPrelude.bpl + sed -e "s|^//#|#|" -i "" PreludeCore.bpl DafnyPrelude.bpl + sed -e "s|PRIME|'|g" -i "" PreludeCore.bpl Sequences.bpl DafnyPrelude.bpl -PreludeSeq.bpl: SequenceModel.dfy - $(DAFNY) verify SequenceModel.dfy --extract:PreludeSeq.bpl +Sequences.bpl: Sequences.dfy + $(DAFNY) verify Sequences.dfy --extract:Sequences.bpl diff --git a/Source/DafnyCore/Prelude/DafnyPreludeCore.bpl b/Source/DafnyCore/Prelude/PreludeCore.bpl similarity index 99% rename from Source/DafnyCore/Prelude/DafnyPreludeCore.bpl rename to Source/DafnyCore/Prelude/PreludeCore.bpl index 0c43c370b3d..8b673301bbc 100644 --- a/Source/DafnyCore/Prelude/DafnyPreludeCore.bpl +++ b/Source/DafnyCore/Prelude/PreludeCore.bpl @@ -958,7 +958,7 @@ axiom (forall s: Seq, x: Box :: { MultiSet#FromSeq(s)[x] } // -- Axiomatization of sequences -------------------------------- // --------------------------------------------------------------- -#include "PreludeSeq.bpl" +#include "Sequences.bpl" // --------------------------------------------------------------- // -- Axiomatization of Maps ------------------------------------- diff --git a/Source/DafnyCore/Prelude/SequenceModel.dfy b/Source/DafnyCore/Prelude/Sequences.dfy similarity index 100% rename from Source/DafnyCore/Prelude/SequenceModel.dfy rename to Source/DafnyCore/Prelude/Sequences.dfy From 8969d7a30e0f00c125fc19646490ac750cf4d433 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:05:41 -0700 Subject: [PATCH 14/52] =?UTF-8?q?Don=E2=80=99t=20include=20this=20generate?= =?UTF-8?q?d=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/Prelude/DafnyPrelude.bpl | 1469 --------------------- 1 file changed, 1469 deletions(-) delete mode 100644 Source/DafnyCore/Prelude/DafnyPrelude.bpl diff --git a/Source/DafnyCore/Prelude/DafnyPrelude.bpl b/Source/DafnyCore/Prelude/DafnyPrelude.bpl deleted file mode 100644 index 0b17328b65d..00000000000 --- a/Source/DafnyCore/Prelude/DafnyPrelude.bpl +++ /dev/null @@ -1,1469 +0,0 @@ -// Dafny prelude -// Created 9 February 2008 by Rustan Leino. -// Converted to Boogie 2 on 28 June 2008. -// Edited sequence axioms 20 October 2009 by Alex Summers. -// Modified 2014 by Dan Rosen. -// Copyright (c) 2008-2014, Microsoft. -// Copyright by the contributors to the Dafny Project -// SPDX-License-Identifier: MIT - -const $$Language$Dafny: bool uses { // To be recognizable to the ModelViewer as - axiom $$Language$Dafny; // coming from a Dafny program. -} - -// --------------------------------------------------------------- -// -- Types ------------------------------------------------------ -// --------------------------------------------------------------- - -type Ty; -type Bv0 = int; - -const unique TBool : Ty uses { - axiom Tag(TBool) == TagBool; -} -const unique TChar : Ty uses { - axiom Tag(TChar) == TagChar; -} -const unique TInt : Ty uses { - axiom Tag(TInt) == TagInt; -} -const unique TReal : Ty uses { - axiom Tag(TReal) == TagReal; -} -const unique TORDINAL : Ty uses { - axiom Tag(TORDINAL) == TagORDINAL; -} -// See for which axioms we can make use of the trigger to determine the connection. -function TBitvector(int) : Ty; -axiom (forall w: int :: { TBitvector(w) } Inv0_TBitvector(TBitvector(w)) == w); - -function TSet(Ty) : Ty; -axiom (forall t: Ty :: { TSet(t) } Inv0_TSet(TSet(t)) == t); -axiom (forall t: Ty :: { TSet(t) } Tag(TSet(t)) == TagSet); - -function TISet(Ty) : Ty; -axiom (forall t: Ty :: { TISet(t) } Inv0_TISet(TISet(t)) == t); -axiom (forall t: Ty :: { TISet(t) } Tag(TISet(t)) == TagISet); - -function TMultiSet(Ty) : Ty; -axiom (forall t: Ty :: { TMultiSet(t) } Inv0_TMultiSet(TMultiSet(t)) == t); -axiom (forall t: Ty :: { TMultiSet(t) } Tag(TMultiSet(t)) == TagMultiSet); - -function TSeq(Ty) : Ty; -axiom (forall t: Ty :: { TSeq(t) } Inv0_TSeq(TSeq(t)) == t); -axiom (forall t: Ty :: { TSeq(t) } Tag(TSeq(t)) == TagSeq); - -function TMap(Ty, Ty) : Ty; -axiom (forall t, u: Ty :: { TMap(t,u) } Inv0_TMap(TMap(t,u)) == t); -axiom (forall t, u: Ty :: { TMap(t,u) } Inv1_TMap(TMap(t,u)) == u); -axiom (forall t, u: Ty :: { TMap(t,u) } Tag(TMap(t,u)) == TagMap); - -function TIMap(Ty, Ty) : Ty; -axiom (forall t, u: Ty :: { TIMap(t,u) } Inv0_TIMap(TIMap(t,u)) == t); -axiom (forall t, u: Ty :: { TIMap(t,u) } Inv1_TIMap(TIMap(t,u)) == u); -axiom (forall t, u: Ty :: { TIMap(t,u) } Tag(TIMap(t,u)) == TagIMap); - - -function Inv0_TBitvector(Ty) : int; -function Inv0_TSet(Ty) : Ty; -function Inv0_TISet(Ty) : Ty; -function Inv0_TSeq(Ty) : Ty; -function Inv0_TMultiSet(Ty) : Ty; -function Inv0_TMap(Ty) : Ty; -function Inv1_TMap(Ty) : Ty; -function Inv0_TIMap(Ty) : Ty; -function Inv1_TIMap(Ty) : Ty; - -// -- Classes and Datatypes -- - -// -- Type Tags -- -type TyTag; -function Tag(Ty) : TyTag; - -const unique TagBool : TyTag; -const unique TagChar : TyTag; -const unique TagInt : TyTag; -const unique TagReal : TyTag; -const unique TagORDINAL : TyTag; -const unique TagSet : TyTag; -const unique TagISet : TyTag; -const unique TagMultiSet : TyTag; -const unique TagSeq : TyTag; -const unique TagMap : TyTag; -const unique TagIMap : TyTag; -const unique TagClass : TyTag; - -type TyTagFamily; -function TagFamily(Ty): TyTagFamily; - -// --------------------------------------------------------------- -// -- Literals --------------------------------------------------- -// --------------------------------------------------------------- -function {:identity} Lit(x: T): T { x } -axiom (forall x: T :: { $Box(Lit(x)) } $Box(Lit(x)) == Lit($Box(x)) ); - -// Specialize Lit to concrete types. -// These aren't logically required, but on some examples improve -// verification speed -function {:identity} LitInt(x: int): int { x } -axiom (forall x: int :: { $Box(LitInt(x)) } $Box(LitInt(x)) == Lit($Box(x)) ); - -function {:identity} LitReal(x: real): real { x } -axiom (forall x: real :: { $Box(LitReal(x)) } $Box(LitReal(x)) == Lit($Box(x)) ); - -// --------------------------------------------------------------- -// -- Characters ------------------------------------------------- -// --------------------------------------------------------------- - -#if UNICODE_CHAR -function {:inline} char#IsChar(n: int): bool { - (0 <= n && n < 55296 /* 0xD800 */) || - (57344 /* 0xE000 */ <= n && n < 1114112 /* 0x11_0000 */ ) -} -#else -function {:inline} char#IsChar(n: int): bool { - 0 <= n && n < 65536 -} -#endif - -type char; -function char#FromInt(int): char; -axiom (forall n: int :: - { char#FromInt(n) } - char#IsChar(n) ==> char#ToInt(char#FromInt(n)) == n); - -function char#ToInt(char): int; -axiom (forall ch: char :: - { char#ToInt(ch) } - char#FromInt(char#ToInt(ch)) == ch && - char#IsChar(char#ToInt(ch))); - -function char#Plus(char, char): char; -axiom (forall a: char, b: char :: - { char#Plus(a, b) } - char#Plus(a, b) == char#FromInt(char#ToInt(a) + char#ToInt(b))); - -function char#Minus(char, char): char; -axiom (forall a: char, b: char :: - { char#Minus(a, b) } - char#Minus(a, b) == char#FromInt(char#ToInt(a) - char#ToInt(b))); - -// --------------------------------------------------------------- -// -- References ------------------------------------------------- -// --------------------------------------------------------------- - -type ref; -const null: ref; - -// --------------------------------------------------------------- -// -- Boxing and unboxing ---------------------------------------- -// --------------------------------------------------------------- - -type Box; -const $ArbitraryBoxValue: Box; - -function $Box(T): Box; -function $Unbox(Box): T; -axiom (forall x : T :: { $Box(x) } $Unbox($Box(x)) == x); -axiom (forall x : Box :: { $Unbox(x): T} $Box($Unbox(x): T) == x); - - -// Corresponding entries for boxes... -// This could probably be solved by having Box also inhabit Ty -function $IsBox(Box,Ty): bool; -function $IsAllocBox(Box,Ty,Heap): bool; - -axiom (forall bx : Box :: - { $IsBox(bx, TInt) } - ( $IsBox(bx, TInt) ==> $Box($Unbox(bx) : int) == bx && $Is($Unbox(bx) : int, TInt))); -axiom (forall bx : Box :: - { $IsBox(bx, TReal) } - ( $IsBox(bx, TReal) ==> $Box($Unbox(bx) : real) == bx && $Is($Unbox(bx) : real, TReal))); -axiom (forall bx : Box :: - { $IsBox(bx, TBool) } - ( $IsBox(bx, TBool) ==> $Box($Unbox(bx) : bool) == bx && $Is($Unbox(bx) : bool, TBool))); -axiom (forall bx : Box :: - { $IsBox(bx, TChar) } - ( $IsBox(bx, TChar) ==> $Box($Unbox(bx) : char) == bx && $Is($Unbox(bx) : char, TChar))); - -// Since each bitvector type is a separate type in Boogie, the Box/Unbox axioms for bitvectors are -// generated programmatically. Except, Bv0 is given here. -axiom (forall bx : Box :: - { $IsBox(bx, TBitvector(0)) } - ( $IsBox(bx, TBitvector(0)) ==> $Box($Unbox(bx) : Bv0) == bx && $Is($Unbox(bx) : Bv0, TBitvector(0)))); - -axiom (forall bx : Box, t : Ty :: - { $IsBox(bx, TSet(t)) } - ( $IsBox(bx, TSet(t)) ==> $Box($Unbox(bx) : Set) == bx && $Is($Unbox(bx) : Set, TSet(t)))); -axiom (forall bx : Box, t : Ty :: - { $IsBox(bx, TISet(t)) } - ( $IsBox(bx, TISet(t)) ==> $Box($Unbox(bx) : ISet) == bx && $Is($Unbox(bx) : ISet, TISet(t)))); -axiom (forall bx : Box, t : Ty :: - { $IsBox(bx, TMultiSet(t)) } - ( $IsBox(bx, TMultiSet(t)) ==> $Box($Unbox(bx) : MultiSet) == bx && $Is($Unbox(bx) : MultiSet, TMultiSet(t)))); -axiom (forall bx : Box, t : Ty :: - { $IsBox(bx, TSeq(t)) } - ( $IsBox(bx, TSeq(t)) ==> $Box($Unbox(bx) : Seq) == bx && $Is($Unbox(bx) : Seq, TSeq(t)))); -axiom (forall bx : Box, s : Ty, t : Ty :: - { $IsBox(bx, TMap(s, t)) } - ( $IsBox(bx, TMap(s, t)) ==> $Box($Unbox(bx) : Map) == bx && $Is($Unbox(bx) : Map, TMap(s, t)))); -axiom (forall bx : Box, s : Ty, t : Ty :: - { $IsBox(bx, TIMap(s, t)) } - ( $IsBox(bx, TIMap(s, t)) ==> $Box($Unbox(bx) : IMap) == bx && $Is($Unbox(bx) : IMap, TIMap(s, t)))); - -axiom (forall v : T, t : Ty :: - { $IsBox($Box(v), t) } - ( $IsBox($Box(v), t) <==> $Is(v,t) )); -axiom (forall v : T, t : Ty, h : Heap :: - { $IsAllocBox($Box(v), t, h) } - ( $IsAllocBox($Box(v), t, h) <==> $IsAlloc(v,t,h) )); - -// --------------------------------------------------------------- -// -- Is and IsAlloc --------------------------------------------- -// --------------------------------------------------------------- - -// Type-argument to $Is is the /representation type/, -// the second value argument to $Is is the actual type. -function $Is(T,Ty): bool; // no heap for now -axiom(forall v : int :: { $Is(v,TInt) } $Is(v,TInt)); -axiom(forall v : real :: { $Is(v,TReal) } $Is(v,TReal)); -axiom(forall v : bool :: { $Is(v,TBool) } $Is(v,TBool)); -axiom(forall v : char :: { $Is(v,TChar) } $Is(v,TChar)); -axiom(forall v : ORDINAL :: { $Is(v,TORDINAL) } $Is(v,TORDINAL)); - -// Since every bitvector type is a separate type in Boogie, the $Is/$IsAlloc axioms -// for bitvectors are generated programatically. Except, TBitvector(0) is given here. -axiom (forall v: Bv0 :: { $Is(v, TBitvector(0)) } $Is(v, TBitvector(0))); - -axiom (forall v: Set, t0: Ty :: { $Is(v, TSet(t0)) } - $Is(v, TSet(t0)) <==> - (forall bx: Box :: { v[bx] } - v[bx] ==> $IsBox(bx, t0))); -axiom (forall v: ISet, t0: Ty :: { $Is(v, TISet(t0)) } - $Is(v, TISet(t0)) <==> - (forall bx: Box :: { v[bx] } - v[bx] ==> $IsBox(bx, t0))); -axiom (forall v: MultiSet, t0: Ty :: { $Is(v, TMultiSet(t0)) } - $Is(v, TMultiSet(t0)) <==> - (forall bx: Box :: { v[bx] } - 0 < v[bx] ==> $IsBox(bx, t0))); -axiom (forall v: MultiSet, t0: Ty :: { $Is(v, TMultiSet(t0)) } - $Is(v, TMultiSet(t0)) ==> $IsGoodMultiSet(v)); -axiom (forall v: Seq, t0: Ty :: { $Is(v, TSeq(t0)) } - $Is(v, TSeq(t0)) <==> - (forall i : int :: { Seq#Index(v, i) } - 0 <= i && i < Seq#Length(v) ==> - $IsBox(Seq#Index(v, i), t0))); - -axiom (forall v: Map, t0: Ty, t1: Ty :: - { $Is(v, TMap(t0, t1)) } - $Is(v, TMap(t0, t1)) - <==> (forall bx: Box :: - { Map#Elements(v)[bx] } { Map#Domain(v)[bx] } - Map#Domain(v)[bx] ==> - $IsBox(Map#Elements(v)[bx], t1) && - $IsBox(bx, t0))); - -axiom (forall v: Map, t0: Ty, t1: Ty :: - { $Is(v, TMap(t0, t1)) } - $Is(v, TMap(t0, t1)) ==> - $Is(Map#Domain(v), TSet(t0)) && - $Is(Map#Values(v), TSet(t1)) && - $Is(Map#Items(v), TSet(Tclass._System.Tuple2(t0, t1)))); -axiom (forall v: IMap, t0: Ty, t1: Ty :: - { $Is(v, TIMap(t0, t1)) } - $Is(v, TIMap(t0, t1)) - <==> (forall bx: Box :: - { IMap#Elements(v)[bx] } { IMap#Domain(v)[bx] } - IMap#Domain(v)[bx] ==> - $IsBox(IMap#Elements(v)[bx], t1) && - $IsBox(bx, t0))); -axiom (forall v: IMap, t0: Ty, t1: Ty :: - { $Is(v, TIMap(t0, t1)) } - $Is(v, TIMap(t0, t1)) ==> - $Is(IMap#Domain(v), TISet(t0)) && - $Is(IMap#Values(v), TISet(t1)) && - $Is(IMap#Items(v), TISet(Tclass._System.Tuple2(t0, t1)))); - -function $IsAlloc(T,Ty,Heap): bool; -axiom(forall h : Heap, v : int :: { $IsAlloc(v,TInt,h) } $IsAlloc(v,TInt,h)); -axiom(forall h : Heap, v : real :: { $IsAlloc(v,TReal,h) } $IsAlloc(v,TReal,h)); -axiom(forall h : Heap, v : bool :: { $IsAlloc(v,TBool,h) } $IsAlloc(v,TBool,h)); -axiom(forall h : Heap, v : char :: { $IsAlloc(v,TChar,h) } $IsAlloc(v,TChar,h)); -axiom(forall h : Heap, v : ORDINAL :: { $IsAlloc(v,TORDINAL,h) } $IsAlloc(v,TORDINAL,h)); - -axiom (forall v: Bv0, h: Heap :: { $IsAlloc(v, TBitvector(0), h) } $IsAlloc(v, TBitvector(0), h)); - -axiom (forall v: Set, t0: Ty, h: Heap :: { $IsAlloc(v, TSet(t0), h) } - $IsAlloc(v, TSet(t0), h) <==> - (forall bx: Box :: { v[bx] } - v[bx] ==> $IsAllocBox(bx, t0, h))); -axiom (forall v: ISet, t0: Ty, h: Heap :: { $IsAlloc(v, TISet(t0), h) } - $IsAlloc(v, TISet(t0), h) <==> - (forall bx: Box :: { v[bx] } - v[bx] ==> $IsAllocBox(bx, t0, h))); -axiom (forall v: MultiSet, t0: Ty, h: Heap :: { $IsAlloc(v, TMultiSet(t0), h) } - $IsAlloc(v, TMultiSet(t0), h) <==> - (forall bx: Box :: { v[bx] } - 0 < v[bx] ==> $IsAllocBox(bx, t0, h))); -axiom (forall v: Seq, t0: Ty, h: Heap :: { $IsAlloc(v, TSeq(t0), h) } - $IsAlloc(v, TSeq(t0), h) <==> - (forall i : int :: { Seq#Index(v, i) } - 0 <= i && i < Seq#Length(v) ==> - $IsAllocBox(Seq#Index(v, i), t0, h))); - -axiom (forall v: Map, t0: Ty, t1: Ty, h: Heap :: - { $IsAlloc(v, TMap(t0, t1), h) } - $IsAlloc(v, TMap(t0, t1), h) - <==> (forall bx: Box :: - { Map#Elements(v)[bx] } { Map#Domain(v)[bx] } - Map#Domain(v)[bx] ==> - $IsAllocBox(Map#Elements(v)[bx], t1, h) && - $IsAllocBox(bx, t0, h))); - -axiom (forall v: IMap, t0: Ty, t1: Ty, h: Heap :: - { $IsAlloc(v, TIMap(t0, t1), h) } - $IsAlloc(v, TIMap(t0, t1), h) - <==> (forall bx: Box :: - { IMap#Elements(v)[bx] } { IMap#Domain(v)[bx] } - IMap#Domain(v)[bx] ==> - $IsAllocBox(IMap#Elements(v)[bx], t1, h) && - $IsAllocBox(bx, t0, h))); - - -function $AlwaysAllocated(Ty): bool; - axiom (forall ty: Ty :: { $AlwaysAllocated(ty) } - $AlwaysAllocated(ty) ==> - (forall h: Heap, v: Box :: { $IsAllocBox(v, ty, h) } $IsBox(v, ty) ==> $IsAllocBox(v, ty, h))); - -function $OlderTag(Heap): bool; - -// --------------------------------------------------------------- -// -- Encoding of type names ------------------------------------- -// --------------------------------------------------------------- - -type ClassName; -const unique class._System.int: ClassName; -const unique class._System.bool: ClassName; -const unique class._System.set: ClassName; -const unique class._System.seq: ClassName; -const unique class._System.multiset: ClassName; - -function Tclass._System.object?(): Ty; -function Tclass._System.Tuple2(Ty, Ty): Ty; - -function /*{:never_pattern true}*/ dtype(ref): Ty; // changed from ClassName to Ty - -function TypeTuple(a: ClassName, b: ClassName): ClassName; -function TypeTupleCar(ClassName): ClassName; -function TypeTupleCdr(ClassName): ClassName; -// TypeTuple is injective in both arguments: -axiom (forall a: ClassName, b: ClassName :: { TypeTuple(a,b) } - TypeTupleCar(TypeTuple(a,b)) == a && - TypeTupleCdr(TypeTuple(a,b)) == b); - -// -- Function handles ------------------------------------------- - -type HandleType; - -function SetRef_to_SetBox(s: [ref]bool): Set; -axiom (forall s: [ref]bool, bx: Box :: { SetRef_to_SetBox(s)[bx] } - SetRef_to_SetBox(s)[bx] == s[$Unbox(bx): ref]); -axiom (forall s: [ref]bool :: { SetRef_to_SetBox(s) } - $Is(SetRef_to_SetBox(s), TSet(Tclass._System.object?()))); - -// Functions ApplyN, RequiresN, and ReadsN are generated on demand by the translator, -// but Apply1 is referred to in the prelude, so its definition is hardcoded here. -function Apply1(Ty, Ty, Heap, HandleType, Box): Box; - -// --------------------------------------------------------------- -// -- Datatypes -------------------------------------------------- -// --------------------------------------------------------------- - -type DatatypeType; - -type DtCtorId; -function DatatypeCtorId(DatatypeType): DtCtorId; - -function DtRank(DatatypeType): int; -function BoxRank(Box): int; - -axiom (forall d: DatatypeType :: {BoxRank($Box(d))} BoxRank($Box(d)) == DtRank(d)); - -// --------------------------------------------------------------- -// -- Big Ordinals ----------------------------------------------- -// --------------------------------------------------------------- - -type ORDINAL = Box; // :| There are more big ordinals than boxes - -// The following two functions give an abstracton over all ordinals. -// Function ORD#IsNat returns true when the ordinal is one of the natural -// numbers. Function ORD#Offset gives how many successors (that is, -// +1 operations) an ordinal is above the nearest lower limit ordinal. -// That is, if the ordinal is \lambda+n, then ORD#Offset returns n. -function ORD#IsNat(ORDINAL): bool; -function ORD#Offset(ORDINAL): int; -axiom (forall o:ORDINAL :: { ORD#Offset(o) } 0 <= ORD#Offset(o)); - -function {:inline} ORD#IsLimit(o: ORDINAL): bool { ORD#Offset(o) == 0 } -function {:inline} ORD#IsSucc(o: ORDINAL): bool { 0 < ORD#Offset(o) } - -function ORD#FromNat(int): ORDINAL; -axiom (forall n:int :: { ORD#FromNat(n) } - 0 <= n ==> ORD#IsNat(ORD#FromNat(n)) && ORD#Offset(ORD#FromNat(n)) == n); -axiom (forall o:ORDINAL :: { ORD#Offset(o) } { ORD#IsNat(o) } - ORD#IsNat(o) ==> o == ORD#FromNat(ORD#Offset(o))); - -function ORD#Less(ORDINAL, ORDINAL): bool; -axiom (forall o,p: ORDINAL :: { ORD#Less(o,p) } - (ORD#Less(o,p) ==> o != p) && // irreflexivity - (ORD#IsNat(o) && !ORD#IsNat(p) ==> ORD#Less(o,p)) && - (ORD#IsNat(o) && ORD#IsNat(p) ==> ORD#Less(o,p) == (ORD#Offset(o) < ORD#Offset(p))) && - (ORD#Less(o,p) && ORD#IsNat(p) ==> ORD#IsNat(o))); -// ORD#Less is trichotomous: -axiom (forall o,p: ORDINAL :: { ORD#Less(o,p), ORD#Less(p,o) } - ORD#Less(o,p) || o == p || ORD#Less(p,o)); -// ORD#Less is transitive: -axiom (forall o,p,r: ORDINAL :: - { ORD#Less(o,p), ORD#Less(p,r) } - { ORD#Less(o,p), ORD#Less(o,r) } - ORD#Less(o,p) && ORD#Less(p,r) ==> ORD#Less(o,r)); - -// ORD#LessThanLimit is a synonym of ORD#Less, introduced for more selected triggering -function ORD#LessThanLimit(ORDINAL, ORDINAL): bool; -axiom (forall o,p: ORDINAL :: { ORD#LessThanLimit(o, p) } - ORD#LessThanLimit(o, p) == ORD#Less(o, p)); - -function ORD#Plus(ORDINAL, ORDINAL): ORDINAL; -axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } - (ORD#IsNat(ORD#Plus(o,p)) ==> ORD#IsNat(o) && ORD#IsNat(p)) && - (ORD#IsNat(p) ==> - ORD#IsNat(ORD#Plus(o,p)) == ORD#IsNat(o) && - ORD#Offset(ORD#Plus(o,p)) == ORD#Offset(o) + ORD#Offset(p))); -axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } - (o == ORD#Plus(o, p) || ORD#Less(o, ORD#Plus(o, p))) && - (p == ORD#Plus(o, p) || ORD#Less(p, ORD#Plus(o, p)))); -axiom (forall o,p: ORDINAL :: { ORD#Plus(o,p) } - (o == ORD#FromNat(0) ==> ORD#Plus(o, p) == p) && - (p == ORD#FromNat(0) ==> ORD#Plus(o, p) == o)); - -function ORD#Minus(ORDINAL, ORDINAL): ORDINAL; -axiom (forall o,p: ORDINAL :: { ORD#Minus(o,p) } - ORD#IsNat(p) && ORD#Offset(p) <= ORD#Offset(o) ==> - ORD#IsNat(ORD#Minus(o,p)) == ORD#IsNat(o) && - ORD#Offset(ORD#Minus(o,p)) == ORD#Offset(o) - ORD#Offset(p)); -axiom (forall o,p: ORDINAL :: { ORD#Minus(o,p) } - ORD#IsNat(p) && ORD#Offset(p) <= ORD#Offset(o) ==> - (p == ORD#FromNat(0) && ORD#Minus(o, p) == o) || - (p != ORD#FromNat(0) && ORD#Less(ORD#Minus(o, p), o))); - -// o+m+n == o+(m+n) -axiom (forall o: ORDINAL, m,n: int :: - { ORD#Plus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) } - 0 <= m && 0 <= n ==> - ORD#Plus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(m+n))); -// o-m-n == o+(m+n) -axiom (forall o: ORDINAL, m,n: int :: - { ORD#Minus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) } - 0 <= m && 0 <= n && m+n <= ORD#Offset(o) ==> - ORD#Minus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(m+n))); -// o+m-n == EITHER o+(m-n) OR o-(n-m) -axiom (forall o: ORDINAL, m,n: int :: - { ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) } - 0 <= m && 0 <= n && n <= ORD#Offset(o) + m ==> - (0 <= m - n ==> ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(m-n))) && - (m - n <= 0 ==> ORD#Minus(ORD#Plus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(n-m)))); -// o-m+n == EITHER o-(m-n) OR o+(n-m) -axiom (forall o: ORDINAL, m,n: int :: - { ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) } - 0 <= m && 0 <= n && n <= ORD#Offset(o) + m ==> - (0 <= m - n ==> ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Minus(o, ORD#FromNat(m-n))) && - (m - n <= 0 ==> ORD#Plus(ORD#Minus(o, ORD#FromNat(m)), ORD#FromNat(n)) == ORD#Plus(o, ORD#FromNat(n-m)))); - -// --------------------------------------------------------------- -// -- Axiom contexts --------------------------------------------- -// --------------------------------------------------------------- - -// used to make sure function axioms are not used while their consistency is being checked -const $ModuleContextHeight: int; -const $FunctionContextHeight: int; - -// --------------------------------------------------------------- -// -- Layers of function encodings ------------------------------- -// --------------------------------------------------------------- - -type LayerType; -const $LZ: LayerType; -function $LS(LayerType): LayerType; -function AsFuelBottom(LayerType) : LayerType; - -function AtLayer([LayerType]A, LayerType): A; -axiom (forall f : [LayerType]A, ly : LayerType :: { AtLayer(f,ly) } AtLayer(f,ly) == f[ly]); -axiom (forall f : [LayerType]A, ly : LayerType :: { AtLayer(f,$LS(ly)) } AtLayer(f,$LS(ly)) == AtLayer(f,ly)); - -// --------------------------------------------------------------- -// -- Fields ----------------------------------------------------- -// --------------------------------------------------------------- - -type Field; - -function FDim(Field): int uses { - axiom FDim(alloc) == 0; -} - -function IndexField(int): Field; -axiom (forall i: int :: { IndexField(i) } FDim(IndexField(i)) == 1); -function IndexField_Inverse(Field): int; -axiom (forall i: int :: { IndexField(i) } IndexField_Inverse(IndexField(i)) == i); - -function MultiIndexField(Field, int): Field; -axiom (forall f: Field, i: int :: { MultiIndexField(f,i) } FDim(MultiIndexField(f,i)) == FDim(f) + 1); -function MultiIndexField_Inverse0(Field): Field; -function MultiIndexField_Inverse1(Field): int; -axiom (forall f: Field, i: int :: { MultiIndexField(f,i) } - MultiIndexField_Inverse0(MultiIndexField(f,i)) == f && - MultiIndexField_Inverse1(MultiIndexField(f,i)) == i); - -function DeclType(Field): ClassName; - -type NameFamily; -function DeclName(Field): NameFamily uses { - axiom DeclName(alloc) == allocName; -} -function FieldOfDecl(ClassName, NameFamily): Field; -axiom (forall cl : ClassName, nm: NameFamily :: - {FieldOfDecl(cl, nm): Field} - DeclType(FieldOfDecl(cl, nm): Field) == cl && DeclName(FieldOfDecl(cl, nm): Field) == nm); - -function $IsGhostField(Field): bool uses { - axiom $IsGhostField(alloc); // treat as ghost field, since it is allowed to be changed by ghost code -} -axiom (forall h: Heap, k: Heap :: { $HeapSuccGhost(h,k) } - $HeapSuccGhost(h,k) ==> - $HeapSucc(h,k) && - (forall o: ref, f: Field :: { read(k, o, f) } - !$IsGhostField(f) ==> read(h, o, f) == read(k, o, f))); - -// --------------------------------------------------------------- -// -- Allocatedness and Heap Succession -------------------------- -// --------------------------------------------------------------- - - -// $IsAlloc and $IsAllocBox are monotonic - -axiom (forall h, k : Heap, v : T, t : Ty :: - { $HeapSucc(h, k), $IsAlloc(v, t, h) } - $HeapSucc(h, k) ==> $IsAlloc(v, t, h) ==> $IsAlloc(v, t, k)); -axiom (forall h, k : Heap, bx : Box, t : Ty :: - { $HeapSucc(h, k), $IsAllocBox(bx, t, h) } - $HeapSucc(h, k) ==> $IsAllocBox(bx, t, h) ==> $IsAllocBox(bx, t, k)); - -// No axioms for $Is and $IsBox since they don't talk about the heap. - -const unique alloc: Field; -const unique allocName: NameFamily; - -// --------------------------------------------------------------- -// -- Arrays ----------------------------------------------------- -// --------------------------------------------------------------- - -function _System.array.Length(a: ref): int; -axiom (forall o: ref :: {_System.array.Length(o)} 0 <= _System.array.Length(o)); - - -// --------------------------------------------------------------- -// -- Reals ------------------------------------------------------ -// --------------------------------------------------------------- - -function Int(x: real): int { int(x) } -function Real(x: int): real { real(x) } -axiom (forall i: int :: { Int(Real(i)) } Int(Real(i)) == i); - -function {:inline} _System.real.Floor(x: real): int { Int(x) } - -// --------------------------------------------------------------- -// -- The heap --------------------------------------------------- -// --------------------------------------------------------------- -type Heap = [ref][Field]Box; -function {:inline} read(H: Heap, r: ref, f: Field) : Box { H[r][f] } -function {:inline} update(H:Heap, r:ref, f: Field, v: Box) : Heap { H[r := H[r][f := v]] } - -function $IsGoodHeap(Heap): bool; -function $IsHeapAnchor(Heap): bool; -var $Heap: Heap where $IsGoodHeap($Heap) && $IsHeapAnchor($Heap); - -// The following is used as a reference heap in places where the translation needs a heap -// but the expression generated is really one that is (at least in a correct program) -// independent of the heap. -const $OneHeap: Heap uses { - axiom $IsGoodHeap($OneHeap); -} - -function $HeapSucc(Heap, Heap): bool; -axiom (forall h: Heap, r: ref, f: Field, x: Box :: { update(h, r, f, x) } - $IsGoodHeap(update(h, r, f, x)) ==> - $HeapSucc(h, update(h, r, f, x))); -axiom (forall a,b,c: Heap :: { $HeapSucc(a,b), $HeapSucc(b,c) } - a != c ==> $HeapSucc(a,b) && $HeapSucc(b,c) ==> $HeapSucc(a,c)); -axiom (forall h: Heap, k: Heap :: { $HeapSucc(h,k) } - $HeapSucc(h,k) ==> (forall o: ref :: { read(k, o, alloc) } $Unbox(read(h, o, alloc)) ==> $Unbox(read(k, o, alloc)))); - -function $HeapSuccGhost(Heap, Heap): bool; - -// --------------------------------------------------------------- -// -- Useful macros ---------------------------------------------- -// --------------------------------------------------------------- - -// havoc everything in $Heap, except {this}+rds+nw -procedure $YieldHavoc(this: ref, rds: Set, nw: Set); - modifies $Heap; - ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } - $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> - $o == this || rds[$Box($o)] || nw[$Box($o)] ==> - read($Heap, $o, $f) == read(old($Heap), $o, $f)); - ensures $HeapSucc(old($Heap), $Heap); - -// havoc everything in $Heap, except rds-modi-{this} -procedure $IterHavoc0(this: ref, rds: Set, modi: Set); - modifies $Heap; - ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } - $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> - rds[$Box($o)] && !modi[$Box($o)] && $o != this ==> - read($Heap, $o, $f) == read(old($Heap), $o, $f)); - ensures $HeapSucc(old($Heap), $Heap); - -// havoc $Heap at {this}+modi+nw -procedure $IterHavoc1(this: ref, modi: Set, nw: Set); - modifies $Heap; - ensures (forall $o: ref, $f: Field :: { read($Heap, $o, $f) } - $o != null && $Unbox(read(old($Heap), $o, alloc)) ==> - read($Heap, $o, $f) == read(old($Heap), $o, $f) || - $o == this || modi[$Box($o)] || nw[$Box($o)]); - ensures $HeapSucc(old($Heap), $Heap); - -procedure $IterCollectNewObjects(prevHeap: Heap, newHeap: Heap, this: ref, NW: Field) - returns (s: Set); - ensures (forall bx: Box :: { s[bx] } s[bx] <==> - ($Unbox(read(newHeap, this, NW)) : Set)[bx] || - ($Unbox(bx) != null && !$Unbox(read(prevHeap, $Unbox(bx):ref, alloc)) && $Unbox(read(newHeap, $Unbox(bx):ref, alloc)))); - -// --------------------------------------------------------------- -// -- Axiomatizations -------------------------------------------- -// --------------------------------------------------------------- - -// --------------------------------------------------------------- -// -- Axiomatization of sets ------------------------------------- -// --------------------------------------------------------------- - -type Set = [Box]bool; - -function Set#Card(Set): int; -axiom (forall s: Set :: { Set#Card(s) } 0 <= Set#Card(s)); - -function Set#Empty(): Set; -axiom (forall o: Box :: { Set#Empty()[o] } !Set#Empty()[o]); -axiom (forall s: Set :: { Set#Card(s) } - (Set#Card(s) == 0 <==> s == Set#Empty()) && - (Set#Card(s) != 0 ==> (exists x: Box :: s[x]))); - -// the empty set could be of anything -//axiom (forall t: Ty :: { $Is(Set#Empty() : [Box]bool, TSet(t)) } $Is(Set#Empty() : [Box]bool, TSet(t))); - -function Set#Singleton(Box): Set; -axiom (forall r: Box :: { Set#Singleton(r) } Set#Singleton(r)[r]); -axiom (forall r: Box, o: Box :: { Set#Singleton(r)[o] } Set#Singleton(r)[o] <==> r == o); -axiom (forall r: Box :: { Set#Card(Set#Singleton(r)) } Set#Card(Set#Singleton(r)) == 1); - -function Set#UnionOne(Set, Box): Set; -axiom (forall a: Set, x: Box, o: Box :: { Set#UnionOne(a,x)[o] } - Set#UnionOne(a,x)[o] <==> o == x || a[o]); -axiom (forall a: Set, x: Box :: { Set#UnionOne(a, x) } - Set#UnionOne(a, x)[x]); -axiom (forall a: Set, x: Box, y: Box :: { Set#UnionOne(a, x), a[y] } - a[y] ==> Set#UnionOne(a, x)[y]); -axiom (forall a: Set, x: Box :: { Set#Card(Set#UnionOne(a, x)) } - a[x] ==> Set#Card(Set#UnionOne(a, x)) == Set#Card(a)); -axiom (forall a: Set, x: Box :: { Set#Card(Set#UnionOne(a, x)) } - !a[x] ==> Set#Card(Set#UnionOne(a, x)) == Set#Card(a) + 1); - -function Set#Union(Set, Set): Set; -axiom (forall a: Set, b: Set, o: Box :: { Set#Union(a,b)[o] } - Set#Union(a,b)[o] <==> a[o] || b[o]); -axiom (forall a, b: Set, y: Box :: { Set#Union(a, b), a[y] } - a[y] ==> Set#Union(a, b)[y]); -axiom (forall a, b: Set, y: Box :: { Set#Union(a, b), b[y] } - b[y] ==> Set#Union(a, b)[y]); -axiom (forall a, b: Set :: { Set#Union(a, b) } - Set#Disjoint(a, b) ==> - Set#Difference(Set#Union(a, b), a) == b && - Set#Difference(Set#Union(a, b), b) == a); -// Follows from the general union axiom, but might be still worth including, because disjoint union is a common case: -// axiom (forall a, b: Set :: { Set#Card(Set#Union(a, b)) } -// Set#Disjoint(a, b) ==> -// Set#Card(Set#Union(a, b)) == Set#Card(a) + Set#Card(b)); - -function Set#Intersection(Set, Set): Set; -axiom (forall a: Set, b: Set, o: Box :: { Set#Intersection(a,b)[o] } - Set#Intersection(a,b)[o] <==> a[o] && b[o]); - -axiom (forall a, b: Set :: { Set#Union(Set#Union(a, b), b) } - Set#Union(Set#Union(a, b), b) == Set#Union(a, b)); -axiom (forall a, b: Set :: { Set#Union(a, Set#Union(a, b)) } - Set#Union(a, Set#Union(a, b)) == Set#Union(a, b)); -axiom (forall a, b: Set :: { Set#Intersection(Set#Intersection(a, b), b) } - Set#Intersection(Set#Intersection(a, b), b) == Set#Intersection(a, b)); -axiom (forall a, b: Set :: { Set#Intersection(a, Set#Intersection(a, b)) } - Set#Intersection(a, Set#Intersection(a, b)) == Set#Intersection(a, b)); -axiom (forall a, b: Set :: { Set#Card(Set#Union(a, b)) }{ Set#Card(Set#Intersection(a, b)) } - Set#Card(Set#Union(a, b)) + Set#Card(Set#Intersection(a, b)) == Set#Card(a) + Set#Card(b)); - -function Set#Difference(Set, Set): Set; -axiom (forall a: Set, b: Set, o: Box :: { Set#Difference(a,b)[o] } - Set#Difference(a,b)[o] <==> a[o] && !b[o]); -axiom (forall a, b: Set, y: Box :: { Set#Difference(a, b), b[y] } - b[y] ==> !Set#Difference(a, b)[y] ); -axiom (forall a, b: Set :: - { Set#Card(Set#Difference(a, b)) } - Set#Card(Set#Difference(a, b)) + Set#Card(Set#Difference(b, a)) - + Set#Card(Set#Intersection(a, b)) - == Set#Card(Set#Union(a, b)) && - Set#Card(Set#Difference(a, b)) == Set#Card(a) - Set#Card(Set#Intersection(a, b))); - -function Set#Subset(Set, Set): bool; -axiom (forall a: Set, b: Set :: { Set#Subset(a,b) } - Set#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] ==> b[o])); -// axiom(forall a: Set, b: Set :: -// { Set#Subset(a,b), Set#Card(a), Set#Card(b) } // very restrictive trigger -// Set#Subset(a,b) ==> Set#Card(a) <= Set#Card(b)); - - -function Set#Equal(Set, Set): bool; -axiom (forall a: Set, b: Set :: { Set#Equal(a,b) } - Set#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <==> b[o])); -axiom (forall a: Set, b: Set :: { Set#Equal(a,b) } // extensionality axiom for sets - Set#Equal(a,b) ==> a == b); - -function Set#Disjoint(Set, Set): bool; -axiom (forall a: Set, b: Set :: { Set#Disjoint(a,b) } - Set#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} !a[o] || !b[o])); - -// --------------------------------------------------------------- -// -- Axiomatization of isets ------------------------------------- -// --------------------------------------------------------------- - -type ISet = [Box]bool; - -function ISet#Empty(): Set; -axiom (forall o: Box :: { ISet#Empty()[o] } !ISet#Empty()[o]); - -// the empty set could be of anything -//axiom (forall t: Ty :: { $Is(ISet#Empty() : [Box]bool, TISet(t)) } $Is(ISet#Empty() : [Box]bool, TISet(t))); - - -function ISet#UnionOne(ISet, Box): ISet; -axiom (forall a: ISet, x: Box, o: Box :: { ISet#UnionOne(a,x)[o] } - ISet#UnionOne(a,x)[o] <==> o == x || a[o]); -axiom (forall a: ISet, x: Box :: { ISet#UnionOne(a, x) } - ISet#UnionOne(a, x)[x]); -axiom (forall a: ISet, x: Box, y: Box :: { ISet#UnionOne(a, x), a[y] } - a[y] ==> ISet#UnionOne(a, x)[y]); - -function ISet#Union(ISet, ISet): ISet; -axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Union(a,b)[o] } - ISet#Union(a,b)[o] <==> a[o] || b[o]); -axiom (forall a, b: ISet, y: Box :: { ISet#Union(a, b), a[y] } - a[y] ==> ISet#Union(a, b)[y]); -axiom (forall a, b: Set, y: Box :: { ISet#Union(a, b), b[y] } - b[y] ==> ISet#Union(a, b)[y]); -axiom (forall a, b: ISet :: { ISet#Union(a, b) } - ISet#Disjoint(a, b) ==> - ISet#Difference(ISet#Union(a, b), a) == b && - ISet#Difference(ISet#Union(a, b), b) == a); - -function ISet#Intersection(ISet, ISet): ISet; -axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Intersection(a,b)[o] } - ISet#Intersection(a,b)[o] <==> a[o] && b[o]); - -axiom (forall a, b: ISet :: { ISet#Union(ISet#Union(a, b), b) } - ISet#Union(ISet#Union(a, b), b) == ISet#Union(a, b)); -axiom (forall a, b: Set :: { ISet#Union(a, ISet#Union(a, b)) } - ISet#Union(a, ISet#Union(a, b)) == ISet#Union(a, b)); -axiom (forall a, b: ISet :: { ISet#Intersection(ISet#Intersection(a, b), b) } - ISet#Intersection(ISet#Intersection(a, b), b) == ISet#Intersection(a, b)); -axiom (forall a, b: ISet :: { ISet#Intersection(a, ISet#Intersection(a, b)) } - ISet#Intersection(a, ISet#Intersection(a, b)) == ISet#Intersection(a, b)); - - -function ISet#Difference(ISet, ISet): ISet; -axiom (forall a: ISet, b: ISet, o: Box :: { ISet#Difference(a,b)[o] } - ISet#Difference(a,b)[o] <==> a[o] && !b[o]); -axiom (forall a, b: ISet, y: Box :: { ISet#Difference(a, b), b[y] } - b[y] ==> !ISet#Difference(a, b)[y] ); - -function ISet#Subset(ISet, ISet): bool; -axiom (forall a: ISet, b: ISet :: { ISet#Subset(a,b) } - ISet#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] ==> b[o])); - -function ISet#Equal(ISet, ISet): bool; -axiom (forall a: ISet, b: ISet :: { ISet#Equal(a,b) } - ISet#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <==> b[o])); -axiom (forall a: ISet, b: ISet :: { ISet#Equal(a,b) } // extensionality axiom for sets - ISet#Equal(a,b) ==> a == b); - -function ISet#Disjoint(ISet, ISet): bool; -axiom (forall a: ISet, b: ISet :: { ISet#Disjoint(a,b) } - ISet#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} !a[o] || !b[o])); - -// --------------------------------------------------------------- -// -- Axiomatization of multisets -------------------------------- -// --------------------------------------------------------------- - -function Math#min(a: int, b: int): int; -axiom (forall a: int, b: int :: { Math#min(a, b) } a <= b <==> Math#min(a, b) == a); -axiom (forall a: int, b: int :: { Math#min(a, b) } b <= a <==> Math#min(a, b) == b); -axiom (forall a: int, b: int :: { Math#min(a, b) } Math#min(a, b) == a || Math#min(a, b) == b); - -function Math#clip(a: int): int; -axiom (forall a: int :: { Math#clip(a) } 0 <= a ==> Math#clip(a) == a); -axiom (forall a: int :: { Math#clip(a) } a < 0 ==> Math#clip(a) == 0); - -type MultiSet = [Box]int; - -function $IsGoodMultiSet(ms: MultiSet): bool; -// ints are non-negative, used after havocing, and for conversion from sequences to multisets. -axiom (forall ms: MultiSet :: { $IsGoodMultiSet(ms) } - $IsGoodMultiSet(ms) <==> - (forall bx: Box :: { ms[bx] } 0 <= ms[bx] && ms[bx] <= MultiSet#Card(ms))); - -function MultiSet#Card(MultiSet): int; -axiom (forall s: MultiSet :: { MultiSet#Card(s) } 0 <= MultiSet#Card(s)); -axiom (forall s: MultiSet, x: Box, n: int :: { MultiSet#Card(s[x := n]) } - 0 <= n ==> MultiSet#Card(s[x := n]) == MultiSet#Card(s) - s[x] + n); - -function MultiSet#Empty(): MultiSet; -axiom (forall o: Box :: { MultiSet#Empty()[o] } MultiSet#Empty()[o] == 0); -axiom (forall s: MultiSet :: { MultiSet#Card(s) } - (MultiSet#Card(s) == 0 <==> s == MultiSet#Empty()) && - (MultiSet#Card(s) != 0 ==> (exists x: Box :: 0 < s[x]))); - -function MultiSet#Singleton(Box): MultiSet; -axiom (forall r: Box, o: Box :: { MultiSet#Singleton(r)[o] } (MultiSet#Singleton(r)[o] == 1 <==> r == o) && - (MultiSet#Singleton(r)[o] == 0 <==> r != o)); -axiom (forall r: Box :: { MultiSet#Singleton(r) } MultiSet#Singleton(r) == MultiSet#UnionOne(MultiSet#Empty(), r)); - -function MultiSet#UnionOne(MultiSet, Box): MultiSet; -// pure containment axiom (in the original multiset or is the added element) -axiom (forall a: MultiSet, x: Box, o: Box :: { MultiSet#UnionOne(a,x)[o] } - 0 < MultiSet#UnionOne(a,x)[o] <==> o == x || 0 < a[o]); -// union-ing increases count by one -axiom (forall a: MultiSet, x: Box :: { MultiSet#UnionOne(a, x) } - MultiSet#UnionOne(a, x)[x] == a[x] + 1); -// non-decreasing -axiom (forall a: MultiSet, x: Box, y: Box :: { MultiSet#UnionOne(a, x), a[y] } - 0 < a[y] ==> 0 < MultiSet#UnionOne(a, x)[y]); -// other elements unchanged -axiom (forall a: MultiSet, x: Box, y: Box :: { MultiSet#UnionOne(a, x), a[y] } - x != y ==> a[y] == MultiSet#UnionOne(a, x)[y]); -axiom (forall a: MultiSet, x: Box :: { MultiSet#Card(MultiSet#UnionOne(a, x)) } - MultiSet#Card(MultiSet#UnionOne(a, x)) == MultiSet#Card(a) + 1); - - -function MultiSet#Union(MultiSet, MultiSet): MultiSet; -// union-ing is the sum of the contents -axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Union(a,b)[o] } - MultiSet#Union(a,b)[o] == a[o] + b[o]); -axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Card(MultiSet#Union(a,b)) } - MultiSet#Card(MultiSet#Union(a,b)) == MultiSet#Card(a) + MultiSet#Card(b)); - -function MultiSet#Intersection(MultiSet, MultiSet): MultiSet; -axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Intersection(a,b)[o] } - MultiSet#Intersection(a,b)[o] == Math#min(a[o], b[o])); - -// left and right pseudo-idempotence -axiom (forall a, b: MultiSet :: { MultiSet#Intersection(MultiSet#Intersection(a, b), b) } - MultiSet#Intersection(MultiSet#Intersection(a, b), b) == MultiSet#Intersection(a, b)); -axiom (forall a, b: MultiSet :: { MultiSet#Intersection(a, MultiSet#Intersection(a, b)) } - MultiSet#Intersection(a, MultiSet#Intersection(a, b)) == MultiSet#Intersection(a, b)); - -// multiset difference, a - b. clip() makes it positive. -function MultiSet#Difference(MultiSet, MultiSet): MultiSet; -axiom (forall a: MultiSet, b: MultiSet, o: Box :: { MultiSet#Difference(a,b)[o] } - MultiSet#Difference(a,b)[o] == Math#clip(a[o] - b[o])); -axiom (forall a, b: MultiSet, y: Box :: { MultiSet#Difference(a, b), b[y], a[y] } - a[y] <= b[y] ==> MultiSet#Difference(a, b)[y] == 0 ); -axiom (forall a, b: MultiSet :: - { MultiSet#Card(MultiSet#Difference(a, b)) } - MultiSet#Card(MultiSet#Difference(a, b)) + MultiSet#Card(MultiSet#Difference(b, a)) - + 2 * MultiSet#Card(MultiSet#Intersection(a, b)) - == MultiSet#Card(MultiSet#Union(a, b)) && - MultiSet#Card(MultiSet#Difference(a, b)) == MultiSet#Card(a) - MultiSet#Card(MultiSet#Intersection(a, b))); - -// multiset subset means a must have at most as many of each element as b -function MultiSet#Subset(MultiSet, MultiSet): bool; -axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Subset(a,b) } - MultiSet#Subset(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] <= b[o])); - -function MultiSet#Equal(MultiSet, MultiSet): bool; -axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Equal(a,b) } - MultiSet#Equal(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] == b[o])); -// extensionality axiom for multisets -axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Equal(a,b) } - MultiSet#Equal(a,b) ==> a == b); - -function MultiSet#Disjoint(MultiSet, MultiSet): bool; -axiom (forall a: MultiSet, b: MultiSet :: { MultiSet#Disjoint(a,b) } - MultiSet#Disjoint(a,b) <==> (forall o: Box :: {a[o]} {b[o]} a[o] == 0 || b[o] == 0)); - -// conversion to a multiset. each element in the original set has duplicity 1. -function MultiSet#FromSet(Set): MultiSet; -axiom (forall s: Set, a: Box :: { MultiSet#FromSet(s)[a] } - (MultiSet#FromSet(s)[a] == 0 <==> !s[a]) && - (MultiSet#FromSet(s)[a] == 1 <==> s[a])); -axiom (forall s: Set :: { MultiSet#Card(MultiSet#FromSet(s)) } - MultiSet#Card(MultiSet#FromSet(s)) == Set#Card(s)); - -// conversion to a multiset, from a sequence. -function MultiSet#FromSeq(Seq): MultiSet uses { - axiom MultiSet#FromSeq(Seq#Empty(): Seq) == MultiSet#Empty(): MultiSet; -} - -// conversion produces a good map. -axiom (forall s: Seq :: { MultiSet#FromSeq(s) } $IsGoodMultiSet(MultiSet#FromSeq(s)) ); -// cardinality axiom -axiom (forall s: Seq :: - { MultiSet#Card(MultiSet#FromSeq(s)) } - MultiSet#Card(MultiSet#FromSeq(s)) == Seq#Length(s)); -// building axiom -axiom (forall s: Seq, v: Box :: - { MultiSet#FromSeq(Seq#Build(s, v)) } - MultiSet#FromSeq(Seq#Build(s, v)) == MultiSet#UnionOne(MultiSet#FromSeq(s), v) - ); - -// concatenation axiom -axiom (forall a: Seq, b: Seq :: - { MultiSet#FromSeq(Seq#Append(a, b)) } - MultiSet#FromSeq(Seq#Append(a, b)) == MultiSet#Union(MultiSet#FromSeq(a), MultiSet#FromSeq(b)) ); - -// update axiom -axiom (forall s: Seq, i: int, v: Box, x: Box :: - { MultiSet#FromSeq(Seq#Update(s, i, v))[x] } - 0 <= i && i < Seq#Length(s) ==> - MultiSet#FromSeq(Seq#Update(s, i, v))[x] == - MultiSet#Union(MultiSet#Difference(MultiSet#FromSeq(s), MultiSet#Singleton(Seq#Index(s,i))), MultiSet#Singleton(v))[x] ); - // i.e. MS(Update(s, i, v)) == MS(s) - {{s[i]}} + {{v}} -axiom (forall s: Seq, x: Box :: { MultiSet#FromSeq(s)[x] } - (exists i : int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && x == Seq#Index(s,i)) <==> 0 < MultiSet#FromSeq(s)[x] ); - -// --------------------------------------------------------------- -// -- Axiomatization of sequences -------------------------------- -// --------------------------------------------------------------- - - - -type Seq; - -function Seq#Length(s: Seq) : int; - -axiom (forall s: Seq :: { Seq#Length(s) } 0 <= Seq#Length(s)); - -function Seq#Empty() : Seq -uses { -axiom Seq#Length(Seq#Empty()) == 0; -} - -axiom (forall s: Seq :: { Seq#Length(s) } Seq#Length(s) == 0 ==> s == Seq#Empty()); - -function Seq#Append(s0: Seq, s1: Seq) : Seq; - -axiom (forall s0: Seq, s1: Seq :: - { Seq#Length(Seq#Append(s0, s1)) } - Seq#Length(Seq#Append(s0, s1)) == Seq#Length(s0) + Seq#Length(s1)); - -axiom (forall s0: Seq, s1: Seq, n: int :: - { Index(Seq#Append(s0, s1), n) } - (n < Seq#Length(s0) ==> Index(Seq#Append(s0, s1), n) == Index(s0, n)) - && (Seq#Length(s0) <= n - ==> Index(Seq#Append(s0, s1), n) == Index(s1, n - Seq#Length(s0)))); - -function Seq#Build(s: Seq, val: Box) : Seq; - -function Seq#Build_inv0(s: Seq) : Seq; - -function Seq#Build_inv1(s: Seq) : Box; - -axiom (forall s: Seq, val: Box :: - { Seq#Build(s, val) } - Seq#Build_inv0(Seq#Build(s, val)) == s - && Seq#Build_inv1(Seq#Build(s, val)) == val); - -axiom (forall s: Seq, v: Box :: - { Seq#Build(s, v) } - Seq#Length(Seq#Build(s, v)) == 1 + Seq#Length(s)); - -axiom (forall s: Seq, i: int, v: Box :: - { Index(Seq#Build(s, v), i) } - (i == Seq#Length(s) ==> Index(Seq#Build(s, v), i) == v) - && (i != Seq#Length(s) ==> Index(Seq#Build(s, v), i) == Index(s, i))); - -function Seq#Update(s: Seq, i: int, val: Box) : Seq; - -axiom (forall s: Seq, i: int, v: Box :: - { Seq#Length(Seq#Update(s, i, v)) } - 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s, i, v)) == Seq#Length(s)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Index(Seq#Update(s, i, v), n) } - 0 <= n && n < Seq#Length(s) - ==> (i == n ==> Index(Seq#Update(s, i, v), n) == v) - && (i != n ==> Index(Seq#Update(s, i, v), n) == Index(s, n))); - -function Seq#Contains(s: Seq, val: Box) : bool; - -axiom (forall s: Seq, x: Box :: - { Seq#Contains(s, x) } - Seq#Contains(s, x) - <==> (forall i: int :: - { Index(s, i) } - 0 <= i && i < Seq#Length(s) && Index(s, i) == x)); - -axiom (forall x: Box :: - { Seq#Contains(Seq#Empty(), x) } - -Seq#Contains(Seq#Empty(), x)); - -axiom (forall s0: Seq, s1: Seq, x: Box :: - { Seq#Contains(Seq#Append(s0, s1), x) } - Seq#Contains(Seq#Append(s0, s1), x) - <==> Seq#Contains(s0, x) || Seq#Contains(s1, x)); - -axiom (forall s: Seq, v: Box, x: Box :: - { Seq#Contains(Seq#Build(s, v), x) } - Seq#Contains(Seq#Build(s, v), x) <==> v == x || Seq#Contains(s, x)); - -function Seq#Equal(s0: Seq, s1: Seq) : bool; - -axiom (forall s0: Seq, s1: Seq :: - { Seq#Equal(s0, s1) } - Seq#Equal(s0, s1) - <==> Seq#Length(s0) == Seq#Length(s1) - && (forall j: int :: - { Index(s1, j) } { Index(s0, j) } - 0 <= j && j < Seq#Length(s0) ==> Index(s0, j) == Index(s1, j))); - -axiom (forall a: Seq, b: Seq :: { Seq#Equal(a, b) } Seq#Equal(a, b) ==> a == b); - -function Seq#SameUntil(s0: Seq, s1: Seq, n: int) : bool; - -axiom (forall s0: Seq, s1: Seq, n: int :: - { Seq#SameUntil(s0, s1, n) } - Seq#SameUntil(s0, s1, n) - <==> (forall j: int :: - { Index(s1, j) } { Index(s0, j) } - 0 <= j && j < n ==> Index(s0, j) == Index(s1, j))); - -function Seq#Take(s: Seq, howMany: int) : Seq; - -axiom (forall s: Seq, n: int, j: int :: - {:weight 25} { Index(s, j), Seq#Take(s, n) } { Index(Seq#Take(s, n), j) } - 0 <= j && j < n && j < Seq#Length(s) ==> Index(Seq#Take(s, n), j) == Index(s, j)); - -function Seq#Drop(s: Seq, howMany: int) : Seq; - -axiom (forall s: Seq, n: int :: - { Seq#Length(Seq#Drop(s, n)) } - 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s, n)) == Seq#Length(s) - n); - -axiom (forall s: Seq, n: int, j: int :: - {:weight 25} { Index(Seq#Drop(s, n), j) } - 0 <= n && 0 <= j && j < Seq#Length(s) - n - ==> Index(Seq#Drop(s, n), j) == Index(s, j + n)); - -axiom (forall s: Seq, n: int, k: int :: - {:weight 25} { Index(s, k), Seq#Drop(s, n) } - 0 <= n && n <= k && k < Seq#Length(s) - ==> Index(Seq#Drop(s, n), k - n) == Index(s, k)); - -axiom (forall s: Seq, n: int, x: Box :: - { Seq#Contains(Seq#Take(s, n), x) } - Seq#Contains(Seq#Take(s, n), x) - <==> (forall i: int :: - { Index(s, i) } - 0 <= i && i < n && i < Seq#Length(s) && Index(s, i) == x)); - -axiom (forall s: Seq, n: int, x: Box :: - { Seq#Contains(Seq#Drop(s, n), x) } - Seq#Contains(Seq#Drop(s, n), x) - <==> (forall i: int :: - { Index(s, i) } - 0 <= n && n <= i && i < Seq#Length(s) && Index(s, i) == x)); - -axiom (forall s: Seq, t: Seq, n: int :: - { Seq#Drop(Seq#Append(s, t), n) } { Seq#Take(Seq#Append(s, t), n) } - n == Seq#Length(s) - ==> Seq#Take(Seq#Append(s, t), n) == s && Seq#Drop(Seq#Append(s, t), n) == t); - -axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } n == 0 ==> Seq#Drop(s, n) == s); - -axiom (forall s: Seq, n: int :: - { Seq#Take(s, n) } - n == 0 ==> Seq#Take(s, n) == Seq#Empty()); - -axiom (forall s: Seq, m: int, n: int :: - { Seq#Drop(Seq#Drop(s, m), n) } - 0 <= m && 0 <= n && m + n <= Seq#Length(s) - ==> Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m + n)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Take(Seq#Update(s, i, v), n) } - 0 <= i && i < n && n <= Seq#Length(s) - ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Take(Seq#Update(s, i, v), n) } - n <= i && i < Seq#Length(s) - ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Drop(Seq#Update(s, i, v), n) } - 0 <= n && n <= i && i < Seq#Length(s) - ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i - n, v)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Drop(Seq#Update(s, i, v), n) } - 0 <= i && i < n && n <= Seq#Length(s) - ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); - -axiom (forall s: Seq, v: Box, n: int :: - { Seq#Drop(Seq#Build(s, v), n) } - 0 <= n && n <= Seq#Length(s) - ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v)); - - -// --------------------------------------------------------------- -// -- Axiomatization of Maps ------------------------------------- -// --------------------------------------------------------------- - -type Map; - -// A Map is defined by three functions, Map#Domain, Map#Elements, and #Map#Card. - -function Map#Domain(Map) : Set; - -function Map#Elements(Map) : [Box]Box; - -function Map#Card(Map) : int; - -axiom (forall m: Map :: { Map#Card(m) } 0 <= Map#Card(m)); - -axiom (forall m: Map :: - { Map#Card(m) } - Map#Card(m) == 0 <==> m == Map#Empty()); - -axiom (forall m: Map :: - { Map#Domain(m) } - m == Map#Empty() || (exists k: Box :: Map#Domain(m)[k])); -axiom (forall m: Map :: - { Map#Values(m) } - m == Map#Empty() || (exists v: Box :: Map#Values(m)[v])); -axiom (forall m: Map :: - { Map#Items(m) } - m == Map#Empty() || (exists k, v: Box :: Map#Items(m)[$Box(#_System._tuple#2._#Make2(k, v))])); - -axiom (forall m: Map :: - { Set#Card(Map#Domain(m)) } { Map#Card(m) } - Set#Card(Map#Domain(m)) == Map#Card(m)); -axiom (forall m: Map :: - { Set#Card(Map#Values(m)) } { Map#Card(m) } - Set#Card(Map#Values(m)) <= Map#Card(m)); -axiom (forall m: Map :: - { Set#Card(Map#Items(m)) } { Map#Card(m) } - Set#Card(Map#Items(m)) == Map#Card(m)); - -// The set of Values of a Map can be obtained by the function Map#Values, which is -// defined as follows. Remember, a Set is defined by membership (using Boogie's -// square brackets) and Map#Card, so we need to define what these mean for the Set -// returned by Map#Values. - -function Map#Values(Map) : Set; - -axiom (forall m: Map, v: Box :: { Map#Values(m)[v] } - Map#Values(m)[v] == - (exists u: Box :: { Map#Domain(m)[u] } { Map#Elements(m)[u] } - Map#Domain(m)[u] && - v == Map#Elements(m)[u])); - -// The set of Items--that is, (key,value) pairs--of a Map can be obtained by the -// function Map#Items. Again, we need to define membership of Set#Card for this -// set. Everywhere else in this axiomatization, Map is parameterized by types U V, -// even though Dafny only ever instantiates U V with Box Box. This makes the -// axiomatization more generic. Function Map#Items, however, returns a set of -// pairs, and the axiomatization of pairs is Dafny specific. Therefore, the -// definition of Map#Items here is to be considered Dafny specific. Also, note -// that it relies on the two destructors for 2-tuples. - -function Map#Items(Map) : Set; - -function #_System._tuple#2._#Make2(Box, Box) : DatatypeType; -function _System.Tuple2._0(DatatypeType) : Box; -function _System.Tuple2._1(DatatypeType) : Box; - -axiom (forall m: Map, item: Box :: { Map#Items(m)[item] } - Map#Items(m)[item] <==> - Map#Domain(m)[_System.Tuple2._0($Unbox(item))] && - Map#Elements(m)[_System.Tuple2._0($Unbox(item))] == _System.Tuple2._1($Unbox(item))); - -// Here are the operations that produce Map values. - -function Map#Empty(): Map; -axiom (forall u: Box :: - { Map#Domain(Map#Empty(): Map)[u] } - !Map#Domain(Map#Empty(): Map)[u]); - -function Map#Glue([Box]bool, [Box]Box, Ty): Map; -axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: - { Map#Domain(Map#Glue(a, b, t)) } - Map#Domain(Map#Glue(a, b, t)) == a); -axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: - { Map#Elements(Map#Glue(a, b, t)) } - Map#Elements(Map#Glue(a, b, t)) == b); -axiom (forall a: [Box]bool, b: [Box]Box, t0, t1: Ty :: - { Map#Glue(a, b, TMap(t0, t1)) } - // In the following line, no trigger needed, since the quantifier only gets used in negative contexts - (forall bx: Box :: a[bx] ==> $IsBox(bx, t0) && $IsBox(b[bx], t1)) - ==> - $Is(Map#Glue(a, b, TMap(t0, t1)), TMap(t0, t1))); - - -//Build is used in displays, and for map updates -function Map#Build(Map, Box, Box): Map; -/*axiom (forall m: Map, u: Box, v: Box :: - { Map#Domain(Map#Build(m, u, v))[u] } { Map#Elements(Map#Build(m, u, v))[u] } - Map#Domain(Map#Build(m, u, v))[u] && Map#Elements(Map#Build(m, u, v))[u] == v);*/ - -axiom (forall m: Map, u: Box, u': Box, v: Box :: - { Map#Domain(Map#Build(m, u, v))[u'] } { Map#Elements(Map#Build(m, u, v))[u'] } - (u' == u ==> Map#Domain(Map#Build(m, u, v))[u'] && - Map#Elements(Map#Build(m, u, v))[u'] == v) && - (u' != u ==> Map#Domain(Map#Build(m, u, v))[u'] == Map#Domain(m)[u'] && - Map#Elements(Map#Build(m, u, v))[u'] == Map#Elements(m)[u'])); -axiom (forall m: Map, u: Box, v: Box :: { Map#Card(Map#Build(m, u, v)) } - Map#Domain(m)[u] ==> Map#Card(Map#Build(m, u, v)) == Map#Card(m)); -axiom (forall m: Map, u: Box, v: Box :: { Map#Card(Map#Build(m, u, v)) } - !Map#Domain(m)[u] ==> Map#Card(Map#Build(m, u, v)) == Map#Card(m) + 1); - -// Map operations -function Map#Merge(Map, Map): Map; -axiom (forall m: Map, n: Map :: - { Map#Domain(Map#Merge(m, n)) } - Map#Domain(Map#Merge(m, n)) == Set#Union(Map#Domain(m), Map#Domain(n))); -axiom (forall m: Map, n: Map, u: Box :: - { Map#Elements(Map#Merge(m, n))[u] } - Map#Domain(Map#Merge(m, n))[u] ==> - (!Map#Domain(n)[u] ==> Map#Elements(Map#Merge(m, n))[u] == Map#Elements(m)[u]) && - (Map#Domain(n)[u] ==> Map#Elements(Map#Merge(m, n))[u] == Map#Elements(n)[u])); - -function Map#Subtract(Map, Set): Map; -axiom (forall m: Map, s: Set :: - { Map#Domain(Map#Subtract(m, s)) } - Map#Domain(Map#Subtract(m, s)) == Set#Difference(Map#Domain(m), s)); -axiom (forall m: Map, s: Set, u: Box :: - { Map#Elements(Map#Subtract(m, s))[u] } - Map#Domain(Map#Subtract(m, s))[u] ==> - Map#Elements(Map#Subtract(m, s))[u] == Map#Elements(m)[u]); - -//equality for maps -function Map#Equal(Map, Map): bool; -axiom (forall m: Map, m': Map:: - { Map#Equal(m, m') } - Map#Equal(m, m') <==> (forall u : Box :: Map#Domain(m)[u] == Map#Domain(m')[u]) && - (forall u : Box :: Map#Domain(m)[u] ==> Map#Elements(m)[u] == Map#Elements(m')[u])); -// extensionality -axiom (forall m: Map, m': Map:: - { Map#Equal(m, m') } - Map#Equal(m, m') ==> m == m'); - -function Map#Disjoint(Map, Map): bool; -axiom (forall m: Map, m': Map :: - { Map#Disjoint(m, m') } - Map#Disjoint(m, m') <==> (forall o: Box :: {Map#Domain(m)[o]} {Map#Domain(m')[o]} !Map#Domain(m)[o] || !Map#Domain(m')[o])); - -// --------------------------------------------------------------- -// -- Axiomatization of IMaps ------------------------------------ -// --------------------------------------------------------------- - -type IMap; - -// A IMap is defined by two functions, Map#Domain and Map#Elements. - -function IMap#Domain(IMap) : Set; - -function IMap#Elements(IMap) : [Box]Box; - -axiom (forall m: IMap :: - { IMap#Domain(m) } - m == IMap#Empty() || (exists k: Box :: IMap#Domain(m)[k])); -axiom (forall m: IMap :: - { IMap#Values(m) } - m == IMap#Empty() || (exists v: Box :: IMap#Values(m)[v])); -axiom (forall m: IMap :: - { IMap#Items(m) } - m == IMap#Empty() || (exists k, v: Box :: IMap#Items(m)[$Box(#_System._tuple#2._#Make2(k, v))])); - -axiom (forall m: IMap :: - { IMap#Domain(m) } - m == IMap#Empty() <==> IMap#Domain(m) == ISet#Empty()); -axiom (forall m: IMap :: - { IMap#Values(m) } - m == IMap#Empty() <==> IMap#Values(m) == ISet#Empty()); -axiom (forall m: IMap :: - { IMap#Items(m) } - m == IMap#Empty() <==> IMap#Items(m) == ISet#Empty()); - -// The set of Values of a IMap can be obtained by the function IMap#Values, which is -// defined as follows. Remember, a ISet is defined by membership (using Boogie's -// square brackets) so we need to define what these mean for the Set -// returned by Map#Values. - -function IMap#Values(IMap) : Set; - -axiom (forall m: IMap, v: Box :: { IMap#Values(m)[v] } - IMap#Values(m)[v] == - (exists u: Box :: { IMap#Domain(m)[u] } { IMap#Elements(m)[u] } - IMap#Domain(m)[u] && - v == IMap#Elements(m)[u])); - -// The set of Items--that is, (key,value) pairs--of a Map can be obtained by the -// function IMap#Items. -// Everywhere else in this axiomatization, IMap is parameterized by types U V, -// even though Dafny only ever instantiates U V with Box Box. This makes the -// axiomatization more generic. Function IMap#Items, however, returns a set of -// pairs, and the axiomatization of pairs is Dafny specific. Therefore, the -// definition of IMap#Items here is to be considered Dafny specific. Also, note -// that it relies on the two destructors for 2-tuples. - -function IMap#Items(IMap) : Set; - -axiom (forall m: IMap, item: Box :: { IMap#Items(m)[item] } - IMap#Items(m)[item] <==> - IMap#Domain(m)[_System.Tuple2._0($Unbox(item))] && - IMap#Elements(m)[_System.Tuple2._0($Unbox(item))] == _System.Tuple2._1($Unbox(item))); - -// Here are the operations that produce Map values. -function IMap#Empty(): IMap; -axiom (forall u: Box :: - { IMap#Domain(IMap#Empty(): IMap)[u] } - !IMap#Domain(IMap#Empty(): IMap)[u]); - -function IMap#Glue([Box] bool, [Box]Box, Ty): IMap; -axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: - { IMap#Domain(IMap#Glue(a, b, t)) } - IMap#Domain(IMap#Glue(a, b, t)) == a); -axiom (forall a: [Box]bool, b: [Box]Box, t: Ty :: - { IMap#Elements(IMap#Glue(a, b, t)) } - IMap#Elements(IMap#Glue(a, b, t)) == b); -axiom (forall a: [Box]bool, b: [Box]Box, t0, t1: Ty :: - { IMap#Glue(a, b, TIMap(t0, t1)) } - // In the following line, no trigger needed, since the quantifier only gets used in negative contexts - (forall bx: Box :: a[bx] ==> $IsBox(bx, t0) && $IsBox(b[bx], t1)) - ==> - $Is(Map#Glue(a, b, TIMap(t0, t1)), TIMap(t0, t1))); - -//Build is used in displays -function IMap#Build(IMap, Box, Box): IMap; -/*axiom (forall m: IMap, u: Box, v: Box :: - { IMap#Domain(IMap#Build(m, u, v))[u] } { IMap#Elements(IMap#Build(m, u, v))[u] } - IMap#Domain(IMap#Build(m, u, v))[u] && IMap#Elements(IMap#Build(m, u, v))[u] == v);*/ - -axiom (forall m: IMap, u: Box, u': Box, v: Box :: - { IMap#Domain(IMap#Build(m, u, v))[u'] } { IMap#Elements(IMap#Build(m, u, v))[u'] } - (u' == u ==> IMap#Domain(IMap#Build(m, u, v))[u'] && - IMap#Elements(IMap#Build(m, u, v))[u'] == v) && - (u' != u ==> IMap#Domain(IMap#Build(m, u, v))[u'] == IMap#Domain(m)[u'] && - IMap#Elements(IMap#Build(m, u, v))[u'] == IMap#Elements(m)[u'])); - -//equality for imaps -function IMap#Equal(IMap, IMap): bool; -axiom (forall m: IMap, m': IMap:: - { IMap#Equal(m, m') } - IMap#Equal(m, m') <==> (forall u : Box :: IMap#Domain(m)[u] == IMap#Domain(m')[u]) && - (forall u : Box :: IMap#Domain(m)[u] ==> IMap#Elements(m)[u] == IMap#Elements(m')[u])); -// extensionality -axiom (forall m: IMap, m': IMap:: - { IMap#Equal(m, m') } - IMap#Equal(m, m') ==> m == m'); - -// IMap operations -function IMap#Merge(IMap, IMap): IMap; -axiom (forall m: IMap, n: IMap :: - { IMap#Domain(IMap#Merge(m, n)) } - IMap#Domain(IMap#Merge(m, n)) == Set#Union(IMap#Domain(m), IMap#Domain(n))); -axiom (forall m: IMap, n: IMap, u: Box :: - { IMap#Elements(IMap#Merge(m, n))[u] } - IMap#Domain(IMap#Merge(m, n))[u] ==> - (!IMap#Domain(n)[u] ==> IMap#Elements(IMap#Merge(m, n))[u] == IMap#Elements(m)[u]) && - (IMap#Domain(n)[u] ==> IMap#Elements(IMap#Merge(m, n))[u] == IMap#Elements(n)[u])); - -function IMap#Subtract(IMap, Set): IMap; -axiom (forall m: IMap, s: Set :: - { IMap#Domain(IMap#Subtract(m, s)) } - IMap#Domain(IMap#Subtract(m, s)) == Set#Difference(IMap#Domain(m), s)); -axiom (forall m: IMap, s: Set, u: Box :: - { IMap#Elements(IMap#Subtract(m, s))[u] } - IMap#Domain(IMap#Subtract(m, s))[u] ==> - IMap#Elements(IMap#Subtract(m, s))[u] == IMap#Elements(m)[u]); - -// ------------------------------------------------------------------------- -// -- Provide arithmetic wrappers to improve triggering and non-linear math -// ------------------------------------------------------------------------- - -function INTERNAL_add_boogie(x:int, y:int) : int { x + y } -function INTERNAL_sub_boogie(x:int, y:int) : int { x - y } -function INTERNAL_mul_boogie(x:int, y:int) : int { x * y } -function INTERNAL_div_boogie(x:int, y:int) : int { x div y } -function INTERNAL_mod_boogie(x:int, y:int) : int { x mod y } -function {:never_pattern true} INTERNAL_lt_boogie(x:int, y:int) : bool { x < y } -function {:never_pattern true} INTERNAL_le_boogie(x:int, y:int) : bool { x <= y } -function {:never_pattern true} INTERNAL_gt_boogie(x:int, y:int) : bool { x > y } -function {:never_pattern true} INTERNAL_ge_boogie(x:int, y:int) : bool { x >= y } - -function Mul(x, y: int): int { x * y } -function Div(x, y: int): int { x div y } -function Mod(x, y: int): int { x mod y } -function Add(x, y: int): int { x + y } -function Sub(x, y: int): int { x - y } - -#if ARITH_DISTR -axiom (forall x, y, z: int :: - { Mul(Add(x, y), z) } - Mul(Add(x, y), z) == Add(Mul(x, z), Mul(y, z))); -axiom (forall x,y,z: int :: - { Mul(x, Add(y, z)) } - Mul(x, Add(y, z)) == Add(Mul(x, y), Mul(x, z))); -//axiom (forall x, y, z: int :: -// { Mul(Sub(x, y), z) } -// Mul(Sub(x, y), z) == Sub(Mul(x, z), Mul(y, z))); -#endif -#if ARITH_MUL_DIV_MOD -axiom (forall x, y: int :: - { Div(x, y), Mod(x, y) } - { Mul(Div(x, y), y) } - y != 0 ==> - Mul(Div(x, y), y) + Mod(x, y) == x); -#endif -#if ARITH_MUL_SIGN -axiom (forall x, y: int :: - { Mul(x, y) } - ((0 <= x && 0 <= y) || (x <= 0 && y <= 0) ==> 0 <= Mul(x, y))); -#endif -#if ARITH_MUL_COMM -axiom (forall x, y: int :: - { Mul(x, y) } - Mul(x, y) == Mul(y, x)); -#endif -#if ARITH_MUL_ASSOC -axiom (forall x, y, z: int :: - { Mul(x, Mul(y, z)) } - Mul(y, z) != z && Mul(y, z) != y ==> Mul(x, Mul(y, z)) == Mul(Mul(x, y), z)); -#endif - -// ------------------------------------------------------------------------- - From 083e0d96d18bf8f32a198620cb361878d1f5b4a3 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:06:28 -0700 Subject: [PATCH 15/52] chore: Change formatting to match generated --- Source/DafnyCore/DafnyPrelude.bpl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index ab6a29f31bb..5cdb3f684fd 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -970,9 +970,8 @@ function Seq#Empty() : Seq uses { axiom Seq#Length(Seq#Empty()) == 0; } -axiom (forall s: Seq :: { Seq#Length(s) } - (Seq#Length(s) == 0 ==> s == Seq#Empty()) - ); + +axiom (forall s: Seq :: { Seq#Length(s) } Seq#Length(s) == 0 ==> s == Seq#Empty()); // The following would be a nice fact to include, because it would enable verifying the // GenericPick.SeqPick* methods in Test/dafny0/SmallTests.dfy. However, it substantially // slows down performance on some other tests, including running seemingly forever on @@ -988,18 +987,21 @@ axiom (forall s: Seq :: { Seq#Length(s) } function Seq#Build(s: Seq, val: Box) : Seq; function Seq#Build_inv0(s: Seq) : Seq; + function Seq#Build_inv1(s: Seq) : Box; + axiom (forall s: Seq, val: Box :: { Seq#Build(s, val) } - Seq#Build_inv0(Seq#Build(s, val)) == s && - Seq#Build_inv1(Seq#Build(s, val)) == val); + Seq#Build_inv0(Seq#Build(s, val)) == s + && Seq#Build_inv1(Seq#Build(s, val)) == val); axiom (forall s: Seq, v: Box :: { Seq#Build(s,v) } Seq#Length(Seq#Build(s,v)) == 1 + Seq#Length(s)); + axiom (forall s: Seq, i: int, v: Box :: { Seq#Index(Seq#Build(s,v), i) } - (i == Seq#Length(s) ==> Seq#Index(Seq#Build(s,v), i) == v) && - (i != Seq#Length(s) ==> Seq#Index(Seq#Build(s,v), i) == Seq#Index(s, i))); + (i == Seq#Length(s) ==> Seq#Index(Seq#Build(s,v), i) == v) + && (i != Seq#Length(s) ==> Seq#Index(Seq#Build(s,v), i) == Seq#Index(s, i))); // Build preserves $Is axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s,bx),TSeq(t)) } From 5bd75cec79c3684be4a509df4baf0fc16232ef3e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:07:07 -0700 Subject: [PATCH 16/52] Move non-generated seq decls into Core file --- Source/DafnyCore/Prelude/PreludeCore.bpl | 64 +++++++++++++++++++++ Source/DafnyCore/Prelude/Sequences.dfy | 72 ------------------------ 2 files changed, 64 insertions(+), 72 deletions(-) diff --git a/Source/DafnyCore/Prelude/PreludeCore.bpl b/Source/DafnyCore/Prelude/PreludeCore.bpl index 8b673301bbc..f37403a4097 100644 --- a/Source/DafnyCore/Prelude/PreludeCore.bpl +++ b/Source/DafnyCore/Prelude/PreludeCore.bpl @@ -960,6 +960,70 @@ axiom (forall s: Seq, x: Box :: { MultiSet#FromSeq(s)[x] } #include "Sequences.bpl" +axiom (forall v: Seq, t0: Ty :: { $Is(v, TSeq(t0)) } + $Is(v, TSeq(t0)) <==> + (forall i : int :: { Seq#Index(v, i) } + 0 <= i && i < Seq#Length(v) ==> + $IsBox(Seq#Index(v, i), t0))); + +// Build preserves $Is +axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s,bx),TSeq(t)) } + $Is(s,TSeq(t)) && $IsBox(bx,t) ==> $Is(Seq#Build(s,bx),TSeq(t))); + +function Seq#Create(ty: Ty, heap: Heap, len: int, init: HandleType): Seq; +axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType :: + { Seq#Length(Seq#Create(ty, heap, len, init): Seq) } + $IsGoodHeap(heap) && 0 <= len ==> + Seq#Length(Seq#Create(ty, heap, len, init): Seq) == len); +axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: + { Seq#Index(Seq#Create(ty, heap, len, init), i) } + $IsGoodHeap(heap) && 0 <= i && i < len ==> + Seq#Index(Seq#Create(ty, heap, len, init), i) == Apply1(TInt, ty, heap, init, $Box(i))); + +function Seq#FromArray(h: Heap, a: ref): Seq; +axiom (forall h: Heap, a: ref :: + { Seq#Length(Seq#FromArray(h,a)) } + Seq#Length(Seq#FromArray(h, a)) == _System.array.Length(a)); +axiom (forall h: Heap, a: ref :: + { Seq#FromArray(h, a) } + (forall i: int :: + // it's important to include both triggers, so that assertions about the + // the relation between the array and the sequence can be proved in either + // direction + { read(h, a, IndexField(i)) } + { Seq#Index(Seq#FromArray(h, a): Seq, i) } + 0 <= i && + i < Seq#Length(Seq#FromArray(h, a)) // this will trigger the previous axiom to get a connection with _System.array.Length(a) + ==> + Seq#Index(Seq#FromArray(h, a), i) == read(h, a, IndexField(i)))); +axiom (forall h0, h1: Heap, a: ref :: + { Seq#FromArray(h1, a), $HeapSucc(h0, h1) } + $IsGoodHeap(h0) && $IsGoodHeap(h1) && $HeapSucc(h0, h1) && h0[a] == h1[a] + ==> + Seq#FromArray(h0, a) == Seq#FromArray(h1, a)); +axiom (forall h: Heap, i: int, v: Box, a: ref :: + { Seq#FromArray(update(h, a, IndexField(i), v), a) } + 0 <= i && i < _System.array.Length(a) ==> Seq#FromArray(update(h, a, IndexField(i), v), a) == Seq#Update(Seq#FromArray(h, a), i, v) ); + +// Extension axiom, triggers only on Takes from arrays. +axiom (forall h: Heap, a: ref, n0, n1: int :: + { Seq#Take(Seq#FromArray(h, a), n0), Seq#Take(Seq#FromArray(h, a), n1) } + n0 + 1 == n1 && 0 <= n0 && n1 <= _System.array.Length(a) ==> Seq#Take(Seq#FromArray(h, a), n1) == Seq#Build(Seq#Take(Seq#FromArray(h, a), n0), read(h, a, IndexField(n0): Field)) ); + +function Seq#Rank(Seq): int; +axiom (forall s: Seq, i: int :: + { DtRank($Unbox(Seq#Index(s, i)): DatatypeType) } + 0 <= i && i < Seq#Length(s) ==> DtRank($Unbox(Seq#Index(s, i)): DatatypeType) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int :: + { Seq#Rank(Seq#Drop(s, i)) } + 0 < i && i <= Seq#Length(s) ==> Seq#Rank(Seq#Drop(s, i)) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int :: + { Seq#Rank(Seq#Take(s, i)) } + 0 <= i && i < Seq#Length(s) ==> Seq#Rank(Seq#Take(s, i)) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int, j: int :: + { Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) } + 0 <= i && i < j && j <= Seq#Length(s) ==> Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) < Seq#Rank(s) ); + // --------------------------------------------------------------- // -- Axiomatization of Maps ------------------------------------- // --------------------------------------------------------------- diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index f6627d3da20..678a50c477d 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -907,75 +907,3 @@ module {:extract} Sequences { } } } - - - -/* The following axioms also mention Seq#... symbols. They also mention $Is, the heap, and/or datatypes, so they - * are not part of the model above. - - -axiom (forall v: Seq, t0: Ty :: { $Is(v, TSeq(t0)) } - $Is(v, TSeq(t0)) <==> - (forall i : int :: { Seq#Index(v, i) } - 0 <= i && i < Seq#Length(v) ==> - $IsBox(Seq#Index(v, i), t0))); - -// Build preserves $Is -axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s,bx),TSeq(t)) } - $Is(s,TSeq(t)) && $IsBox(bx,t) ==> $Is(Seq#Build(s,bx),TSeq(t))); - -function Seq#Create(ty: Ty, heap: Heap, len: int, init: HandleType): Seq; -axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType :: - { Seq#Length(Seq#Create(ty, heap, len, init): Seq) } - $IsGoodHeap(heap) && 0 <= len ==> - Seq#Length(Seq#Create(ty, heap, len, init): Seq) == len); -axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: - { Seq#Index(Seq#Create(ty, heap, len, init), i) } - $IsGoodHeap(heap) && 0 <= i && i < len ==> - Seq#Index(Seq#Create(ty, heap, len, init), i) == Apply1(TInt, ty, heap, init, $Box(i))); - -function Seq#FromArray(h: Heap, a: ref): Seq; -axiom (forall h: Heap, a: ref :: - { Seq#Length(Seq#FromArray(h,a)) } - Seq#Length(Seq#FromArray(h, a)) == _System.array.Length(a)); -axiom (forall h: Heap, a: ref :: - { Seq#FromArray(h, a) } - (forall i: int :: - // it's important to include both triggers, so that assertions about the - // the relation between the array and the sequence can be proved in either - // direction - { read(h, a, IndexField(i)) } - { Seq#Index(Seq#FromArray(h, a): Seq, i) } - 0 <= i && - i < Seq#Length(Seq#FromArray(h, a)) // this will trigger the previous axiom to get a connection with _System.array.Length(a) - ==> - Seq#Index(Seq#FromArray(h, a), i) == read(h, a, IndexField(i)))); -axiom (forall h0, h1: Heap, a: ref :: - { Seq#FromArray(h1, a), $HeapSucc(h0, h1) } - $IsGoodHeap(h0) && $IsGoodHeap(h1) && $HeapSucc(h0, h1) && h0[a] == h1[a] - ==> - Seq#FromArray(h0, a) == Seq#FromArray(h1, a)); -axiom (forall h: Heap, i: int, v: Box, a: ref :: - { Seq#FromArray(update(h, a, IndexField(i), v), a) } - 0 <= i && i < _System.array.Length(a) ==> Seq#FromArray(update(h, a, IndexField(i), v), a) == Seq#Update(Seq#FromArray(h, a), i, v) ); - -// Extension axiom, triggers only on Takes from arrays. -axiom (forall h: Heap, a: ref, n0, n1: int :: - { Seq#Take(Seq#FromArray(h, a), n0), Seq#Take(Seq#FromArray(h, a), n1) } - n0 + 1 == n1 && 0 <= n0 && n1 <= _System.array.Length(a) ==> Seq#Take(Seq#FromArray(h, a), n1) == Seq#Build(Seq#Take(Seq#FromArray(h, a), n0), read(h, a, IndexField(n0): Field)) ); - -function Seq#Rank(Seq): int; -axiom (forall s: Seq, i: int :: - { DtRank($Unbox(Seq#Index(s, i)): DatatypeType) } - 0 <= i && i < Seq#Length(s) ==> DtRank($Unbox(Seq#Index(s, i)): DatatypeType) < Seq#Rank(s) ); -axiom (forall s: Seq, i: int :: - { Seq#Rank(Seq#Drop(s, i)) } - 0 < i && i <= Seq#Length(s) ==> Seq#Rank(Seq#Drop(s, i)) < Seq#Rank(s) ); -axiom (forall s: Seq, i: int :: - { Seq#Rank(Seq#Take(s, i)) } - 0 <= i && i < Seq#Length(s) ==> Seq#Rank(Seq#Take(s, i)) < Seq#Rank(s) ); -axiom (forall s: Seq, i: int, j: int :: - { Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) } - 0 <= i && i < j && j <= Seq#Length(s) ==> Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) < Seq#Rank(s) ); - -*/ From 2e6bf6da9fac446ac1cbbb7bb5d1f969a16c567e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:07:28 -0700 Subject: [PATCH 17/52] =?UTF-8?q?Don=E2=80=99t=20include=20this=20generate?= =?UTF-8?q?d=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/Prelude/Sequences.bpl | 177 ------------------------- 1 file changed, 177 deletions(-) delete mode 100644 Source/DafnyCore/Prelude/Sequences.bpl diff --git a/Source/DafnyCore/Prelude/Sequences.bpl b/Source/DafnyCore/Prelude/Sequences.bpl deleted file mode 100644 index f4cdeeb565e..00000000000 --- a/Source/DafnyCore/Prelude/Sequences.bpl +++ /dev/null @@ -1,177 +0,0 @@ - -type Seq; - -function Seq#Length(s: Seq) : int; - -axiom (forall s: Seq :: { Seq#Length(s) } 0 <= Seq#Length(s)); - -function Seq#Empty() : Seq -uses { -axiom Seq#Length(Seq#Empty()) == 0; -} - -axiom (forall s: Seq :: { Seq#Length(s) } Seq#Length(s) == 0 ==> s == Seq#Empty()); - -function Seq#Append(s0: Seq, s1: Seq) : Seq; - -axiom (forall s0: Seq, s1: Seq :: - { Seq#Length(Seq#Append(s0, s1)) } - Seq#Length(Seq#Append(s0, s1)) == Seq#Length(s0) + Seq#Length(s1)); - -axiom (forall s0: Seq, s1: Seq, n: int :: - { Index(Seq#Append(s0, s1), n) } - (n < Seq#Length(s0) ==> Index(Seq#Append(s0, s1), n) == Index(s0, n)) - && (Seq#Length(s0) <= n - ==> Index(Seq#Append(s0, s1), n) == Index(s1, n - Seq#Length(s0)))); - -function Seq#Build(s: Seq, val: Box) : Seq; - -function Seq#Build_inv0(s: Seq) : Seq; - -function Seq#Build_inv1(s: Seq) : Box; - -axiom (forall s: Seq, val: Box :: - { Seq#Build(s, val) } - Seq#Build_inv0(Seq#Build(s, val)) == s - && Seq#Build_inv1(Seq#Build(s, val)) == val); - -axiom (forall s: Seq, v: Box :: - { Seq#Build(s, v) } - Seq#Length(Seq#Build(s, v)) == 1 + Seq#Length(s)); - -axiom (forall s: Seq, i: int, v: Box :: - { Index(Seq#Build(s, v), i) } - (i == Seq#Length(s) ==> Index(Seq#Build(s, v), i) == v) - && (i != Seq#Length(s) ==> Index(Seq#Build(s, v), i) == Index(s, i))); - -function Seq#Update(s: Seq, i: int, val: Box) : Seq; - -axiom (forall s: Seq, i: int, v: Box :: - { Seq#Length(Seq#Update(s, i, v)) } - 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s, i, v)) == Seq#Length(s)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Index(Seq#Update(s, i, v), n) } - 0 <= n && n < Seq#Length(s) - ==> (i == n ==> Index(Seq#Update(s, i, v), n) == v) - && (i != n ==> Index(Seq#Update(s, i, v), n) == Index(s, n))); - -function Seq#Contains(s: Seq, val: Box) : bool; - -axiom (forall s: Seq, x: Box :: - { Seq#Contains(s, x) } - Seq#Contains(s, x) - <==> (forall i: int :: - { Index(s, i) } - 0 <= i && i < Seq#Length(s) && Index(s, i) == x)); - -axiom (forall x: Box :: - { Seq#Contains(Seq#Empty(), x) } - -Seq#Contains(Seq#Empty(), x)); - -axiom (forall s0: Seq, s1: Seq, x: Box :: - { Seq#Contains(Seq#Append(s0, s1), x) } - Seq#Contains(Seq#Append(s0, s1), x) - <==> Seq#Contains(s0, x) || Seq#Contains(s1, x)); - -axiom (forall s: Seq, v: Box, x: Box :: - { Seq#Contains(Seq#Build(s, v), x) } - Seq#Contains(Seq#Build(s, v), x) <==> v == x || Seq#Contains(s, x)); - -function Seq#Equal(s0: Seq, s1: Seq) : bool; - -axiom (forall s0: Seq, s1: Seq :: - { Seq#Equal(s0, s1) } - Seq#Equal(s0, s1) - <==> Seq#Length(s0) == Seq#Length(s1) - && (forall j: int :: - { Index(s1, j) } { Index(s0, j) } - 0 <= j && j < Seq#Length(s0) ==> Index(s0, j) == Index(s1, j))); - -axiom (forall a: Seq, b: Seq :: { Seq#Equal(a, b) } Seq#Equal(a, b) ==> a == b); - -function Seq#SameUntil(s0: Seq, s1: Seq, n: int) : bool; - -axiom (forall s0: Seq, s1: Seq, n: int :: - { Seq#SameUntil(s0, s1, n) } - Seq#SameUntil(s0, s1, n) - <==> (forall j: int :: - { Index(s1, j) } { Index(s0, j) } - 0 <= j && j < n ==> Index(s0, j) == Index(s1, j))); - -function Seq#Take(s: Seq, howMany: int) : Seq; - -axiom (forall s: Seq, n: int, j: int :: - {:weight 25} { Index(s, j), Seq#Take(s, n) } { Index(Seq#Take(s, n), j) } - 0 <= j && j < n && j < Seq#Length(s) ==> Index(Seq#Take(s, n), j) == Index(s, j)); - -function Seq#Drop(s: Seq, howMany: int) : Seq; - -axiom (forall s: Seq, n: int :: - { Seq#Length(Seq#Drop(s, n)) } - 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s, n)) == Seq#Length(s) - n); - -axiom (forall s: Seq, n: int, j: int :: - {:weight 25} { Index(Seq#Drop(s, n), j) } - 0 <= n && 0 <= j && j < Seq#Length(s) - n - ==> Index(Seq#Drop(s, n), j) == Index(s, j + n)); - -axiom (forall s: Seq, n: int, k: int :: - {:weight 25} { Index(s, k), Seq#Drop(s, n) } - 0 <= n && n <= k && k < Seq#Length(s) - ==> Index(Seq#Drop(s, n), k - n) == Index(s, k)); - -axiom (forall s: Seq, n: int, x: Box :: - { Seq#Contains(Seq#Take(s, n), x) } - Seq#Contains(Seq#Take(s, n), x) - <==> (forall i: int :: - { Index(s, i) } - 0 <= i && i < n && i < Seq#Length(s) && Index(s, i) == x)); - -axiom (forall s: Seq, n: int, x: Box :: - { Seq#Contains(Seq#Drop(s, n), x) } - Seq#Contains(Seq#Drop(s, n), x) - <==> (forall i: int :: - { Index(s, i) } - 0 <= n && n <= i && i < Seq#Length(s) && Index(s, i) == x)); - -axiom (forall s: Seq, t: Seq, n: int :: - { Seq#Drop(Seq#Append(s, t), n) } { Seq#Take(Seq#Append(s, t), n) } - n == Seq#Length(s) - ==> Seq#Take(Seq#Append(s, t), n) == s && Seq#Drop(Seq#Append(s, t), n) == t); - -axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } n == 0 ==> Seq#Drop(s, n) == s); - -axiom (forall s: Seq, n: int :: - { Seq#Take(s, n) } - n == 0 ==> Seq#Take(s, n) == Seq#Empty()); - -axiom (forall s: Seq, m: int, n: int :: - { Seq#Drop(Seq#Drop(s, m), n) } - 0 <= m && 0 <= n && m + n <= Seq#Length(s) - ==> Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m + n)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Take(Seq#Update(s, i, v), n) } - 0 <= i && i < n && n <= Seq#Length(s) - ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Take(Seq#Update(s, i, v), n) } - n <= i && i < Seq#Length(s) - ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Drop(Seq#Update(s, i, v), n) } - 0 <= n && n <= i && i < Seq#Length(s) - ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i - n, v)); - -axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Drop(Seq#Update(s, i, v), n) } - 0 <= i && i < n && n <= Seq#Length(s) - ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); - -axiom (forall s: Seq, v: Box, n: int :: - { Seq#Drop(Seq#Build(s, v), n) } - 0 <= n && n <= Seq#Length(s) - ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v)); From cbc6e8706cb46743d978c9e722539f1d0ccde78e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:07:50 -0700 Subject: [PATCH 18/52] Change order of declarations --- Source/DafnyCore/Prelude/Sequences.dfy | 162 ++++++++++++------------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index 678a50c477d..2cd22aac102 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -174,87 +174,6 @@ module {:extract} Sequences { { } - // boogie: function Seq#Index(Seq, int): Box; - function {:extract "Seq#Index"} Index(s: Seq, i: int): Box { - if 0 <= i < Length(s) then - s.At(i) - else - Boxes.arbitrary - } - - // boogie: function Seq#Append(Seq, Seq): Seq; - function {:extract_name "Seq#Append"} Append(s0: Seq, s1: Seq): Seq { - s0.Append(s1) - } - - // boogie: - // axiom (forall s0: Seq, s1: Seq :: { Seq#Length(Seq#Append(s0,s1)) } - // Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); - lemma {:extract_pattern Length(Append(s0, s1))} LengthAppend(s0: Seq, s1: Seq) - ensures Length(Append(s0, s1)) == Length(s0) + Length(s1) - { - s0.LengthAppend(s1); - } - - // boogie: - // axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#Index(Seq#Append(s0,s1), n) } - // (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && - // (Seq#Length(s0) <= n ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); - lemma {:extract_pattern Index(Append(s0, s1), n)} IndexAppend(s0: Seq, s1: Seq, n: int) - ensures n < Length(s0) ==> Index(Append(s0, s1), n) == Index(s0, n) - ensures Length(s0) <= n ==> Index(Append(s0, s1), n) == Index(s1, n - Length(s0)) - { - if - case n < 0 => - - case 0 <= n < Length(s0) => - if n == 0 { - calc { - Index(Append(s0, s1), 0); - Index(Cons(s0.head, Append(s0.tail, s1)), 0); - s0.head; - Index(Cons(s0.head, s0.tail), 0); - Index(s0, 0); - } - } else { - calc { - Index(Append(s0, s1), n); - // def. Append - Index(Cons(s0.head, Append(s0.tail, s1)), n); - // def. Index - Index(Append(s0.tail, s1), n - 1); - { IndexAppend(s0.tail, s1, n - 1); } - Index(s0.tail, n - 1); - // def. Index - Index(Cons(s0.head, s0.tail), n); - Index(s0, n); - } - } - - case Length(s0) <= n => - match s0 - case Nil => - calc { - Index(Append(s0, s1), n); - // def. Append - Index(s1, n); - // def. Length - Index(s1, n - Length(s0)); - } - case Cons(x, tail) => - calc { - Index(Append(s0, s1), n); - // def. Append - Index(Cons(s0.head, Append(s0.tail, s1)), n); - // def. Index - Index(Append(s0.tail, s1), n - 1); - { IndexAppend(s0.tail, s1, n - 1); } - Index(s1, n - 1 - Length(s0.tail)); - // def. Length - Index(s1, n - Length(s0)); - } - } - // boogie: function Seq#Build(s: Seq, val: Box): Seq; function {:extract_name "Seq#Build"} Build(s: Seq, val: Box): Seq { s.Append(Cons(val, Nil)) @@ -348,6 +267,87 @@ module {:extract} Sequences { } } + // boogie: function Seq#Index(Seq, int): Box; + function {:extract "Seq#Index"} Index(s: Seq, i: int): Box { + if 0 <= i < Length(s) then + s.At(i) + else + Boxes.arbitrary + } + + // boogie: function Seq#Append(Seq, Seq): Seq; + function {:extract_name "Seq#Append"} Append(s0: Seq, s1: Seq): Seq { + s0.Append(s1) + } + + // boogie: + // axiom (forall s0: Seq, s1: Seq :: { Seq#Length(Seq#Append(s0,s1)) } + // Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); + lemma {:extract_pattern Length(Append(s0, s1))} LengthAppend(s0: Seq, s1: Seq) + ensures Length(Append(s0, s1)) == Length(s0) + Length(s1) + { + s0.LengthAppend(s1); + } + + // boogie: + // axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#Index(Seq#Append(s0,s1), n) } + // (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && + // (Seq#Length(s0) <= n ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); + lemma {:extract_pattern Index(Append(s0, s1), n)} IndexAppend(s0: Seq, s1: Seq, n: int) + ensures n < Length(s0) ==> Index(Append(s0, s1), n) == Index(s0, n) + ensures Length(s0) <= n ==> Index(Append(s0, s1), n) == Index(s1, n - Length(s0)) + { + if + case n < 0 => + + case 0 <= n < Length(s0) => + if n == 0 { + calc { + Index(Append(s0, s1), 0); + Index(Cons(s0.head, Append(s0.tail, s1)), 0); + s0.head; + Index(Cons(s0.head, s0.tail), 0); + Index(s0, 0); + } + } else { + calc { + Index(Append(s0, s1), n); + // def. Append + Index(Cons(s0.head, Append(s0.tail, s1)), n); + // def. Index + Index(Append(s0.tail, s1), n - 1); + { IndexAppend(s0.tail, s1, n - 1); } + Index(s0.tail, n - 1); + // def. Index + Index(Cons(s0.head, s0.tail), n); + Index(s0, n); + } + } + + case Length(s0) <= n => + match s0 + case Nil => + calc { + Index(Append(s0, s1), n); + // def. Append + Index(s1, n); + // def. Length + Index(s1, n - Length(s0)); + } + case Cons(x, tail) => + calc { + Index(Append(s0, s1), n); + // def. Append + Index(Cons(s0.head, Append(s0.tail, s1)), n); + // def. Index + Index(Append(s0.tail, s1), n - 1); + { IndexAppend(s0.tail, s1, n - 1); } + Index(s1, n - 1 - Length(s0.tail)); + // def. Length + Index(s1, n - Length(s0)); + } + } + // boogie: function Seq#Update(Seq, int, Box): Seq; function {:extract_name "Seq#Update"} Update(s: Seq, i: int, val: Box): Seq { if !(0 <= i < s.Length()) then From 0fdae0d89ddfa5ba05aa9af3d92fd96ffe063f6c Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:22:52 -0700 Subject: [PATCH 19/52] chore: Change formatting --- Source/DafnyCore/DafnyPrelude.bpl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 5cdb3f684fd..d2584beb0be 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -996,12 +996,13 @@ axiom (forall s: Seq, val: Box :: && Seq#Build_inv1(Seq#Build(s, val)) == val); axiom (forall s: Seq, v: Box :: - { Seq#Build(s,v) } - Seq#Length(Seq#Build(s,v)) == 1 + Seq#Length(s)); + { Seq#Build(s, v) } + Seq#Length(Seq#Build(s, v)) == 1 + Seq#Length(s)); -axiom (forall s: Seq, i: int, v: Box :: { Seq#Index(Seq#Build(s,v), i) } - (i == Seq#Length(s) ==> Seq#Index(Seq#Build(s,v), i) == v) - && (i != Seq#Length(s) ==> Seq#Index(Seq#Build(s,v), i) == Seq#Index(s, i))); +axiom (forall s: Seq, i: int, v: Box :: + { Seq#Index(Seq#Build(s, v), i) } + (i == Seq#Length(s) ==> Seq#Index(Seq#Build(s, v), i) == v) + && (i != Seq#Length(s) ==> Seq#Index(Seq#Build(s, v), i) == Seq#Index(s, i))); // Build preserves $Is axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s,bx),TSeq(t)) } @@ -1017,7 +1018,6 @@ axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: $IsGoodHeap(heap) && 0 <= i && i < len ==> Seq#Index(Seq#Create(ty, heap, len, init), i) == Apply1(TInt, ty, heap, init, $Box(i))); -function Seq#Append(Seq, Seq): Seq; axiom (forall s0: Seq, s1: Seq :: { Seq#Length(Seq#Append(s0,s1)) } Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); @@ -1035,7 +1035,10 @@ axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Index(Seq#Update(s,i,v),n) (i == n ==> Seq#Index(Seq#Update(s,i,v),n) == v) && (i != n ==> Seq#Index(Seq#Update(s,i,v),n) == Seq#Index(s,n))); -function Seq#Contains(Seq, Box): bool; +function Seq#Append(s0: Seq, s1: Seq) : Seq; + +function Seq#Contains(s: Seq, val: Box) : bool; + axiom (forall s: Seq, x: Box :: { Seq#Contains(s,x) } Seq#Contains(s,x) <==> (exists i: int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && Seq#Index(s,i) == x)); From 204fa36e1f4bd2a10c875eda628c1880b9c8485e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:27:24 -0700 Subject: [PATCH 20/52] chore: Name parameters --- Source/DafnyCore/DafnyPrelude.bpl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index d2584beb0be..51ffdc8af89 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1021,13 +1021,15 @@ axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: axiom (forall s0: Seq, s1: Seq :: { Seq#Length(Seq#Append(s0,s1)) } Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); -function Seq#Index(Seq, int): Box; +function Seq#Index(s: Seq, i: int) : Box; + axiom (forall t: Box :: { Seq#Index(Seq#Singleton(t), 0) } Seq#Index(Seq#Singleton(t), 0) == t); axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#Index(Seq#Append(s0,s1), n) } (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && (Seq#Length(s0) <= n ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); -function Seq#Update(Seq, int, Box): Seq; +function Seq#Update(s: Seq, i: int, val: Box) : Seq; + axiom (forall s: Seq, i: int, v: Box :: { Seq#Length(Seq#Update(s,i,v)) } 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s,i,v)) == Seq#Length(s)); axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Index(Seq#Update(s,i,v),n) } @@ -1066,7 +1068,8 @@ axiom (forall s: Seq, n: int, x: Box :: (exists i: int :: { Seq#Index(s, i) } 0 <= n && n <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); -function Seq#Equal(Seq, Seq): bool; +function Seq#Equal(s0: Seq, s1: Seq) : bool; + axiom (forall s0: Seq, s1: Seq :: { Seq#Equal(s0,s1) } Seq#Equal(s0,s1) <==> Seq#Length(s0) == Seq#Length(s1) && @@ -1075,13 +1078,15 @@ axiom (forall s0: Seq, s1: Seq :: { Seq#Equal(s0,s1) } axiom (forall a: Seq, b: Seq :: { Seq#Equal(a,b) } // extensionality axiom for sequences Seq#Equal(a,b) ==> a == b); -function Seq#SameUntil(Seq, Seq, int): bool; +function Seq#SameUntil(s0: Seq, s1: Seq, n: int) : bool; + axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#SameUntil(s0,s1,n) } Seq#SameUntil(s0,s1,n) <==> (forall j: int :: { Seq#Index(s0,j) } { Seq#Index(s1,j) } 0 <= j && j < n ==> Seq#Index(s0,j) == Seq#Index(s1,j))); -function Seq#Take(s: Seq, howMany: int): Seq; +function Seq#Take(s: Seq, howMany: int) : Seq; + axiom (forall s: Seq, n: int :: { Seq#Length(Seq#Take(s,n)) } 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Take(s,n)) == n); axiom (forall s: Seq, n: int, j: int :: @@ -1091,7 +1096,8 @@ axiom (forall s: Seq, n: int, j: int :: 0 <= j && j < n && j < Seq#Length(s) ==> Seq#Index(Seq#Take(s,n), j) == Seq#Index(s, j)); -function Seq#Drop(s: Seq, howMany: int): Seq; +function Seq#Drop(s: Seq, howMany: int) : Seq; + axiom (forall s: Seq, n: int :: { Seq#Length(Seq#Drop(s,n)) } 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s,n)) == Seq#Length(s) - n); axiom (forall s: Seq, n: int, j: int :: From 88f563a9910c795df736b733c3bcc9c8ebcf4bff Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:30:59 -0700 Subject: [PATCH 21/52] Fix forall/exists distinction --- Source/DafnyCore/Backends/Extractor.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index 51944973b97..f292e32fd95 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -263,7 +263,12 @@ private Boogie.Expr ExtractExpr(Expression expr) { var kv = GetKeyValues(tok, quantifierExpr.Attributes); var body = ExtractExpr(quantifierExpr.LogicalBody()); - return new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); + if (quantifierExpr is ExistsExpr) { + return new Boogie.ExistsExpr(tok, new List(), boundVars, kv, triggers, body); + } else { + Contract.Assert(quantifierExpr is ForallExpr); + return new Boogie.ForallExpr(tok, new List(), boundVars, kv, triggers, body); + } } default: From 589c6ac865016b1c1441f55ab5ca3d9a45775d46 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:35:24 -0700 Subject: [PATCH 22/52] chore: Adjust formatting --- Source/DafnyCore/DafnyPrelude.bpl | 55 ++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 51ffdc8af89..db88158ab47 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1018,21 +1018,28 @@ axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: $IsGoodHeap(heap) && 0 <= i && i < len ==> Seq#Index(Seq#Create(ty, heap, len, init), i) == Apply1(TInt, ty, heap, init, $Box(i))); -axiom (forall s0: Seq, s1: Seq :: { Seq#Length(Seq#Append(s0,s1)) } +axiom (forall s0: Seq, s1: Seq :: + { Seq#Length(Seq#Append(s0,s1)) } Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); function Seq#Index(s: Seq, i: int) : Box; -axiom (forall t: Box :: { Seq#Index(Seq#Singleton(t), 0) } Seq#Index(Seq#Singleton(t), 0) == t); -axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#Index(Seq#Append(s0,s1), n) } +axiom (forall t: Box :: + { Seq#Index(Seq#Singleton(t), 0) } + Seq#Index(Seq#Singleton(t), 0) == t); + +axiom (forall s0: Seq, s1: Seq, n: int :: + { Seq#Index(Seq#Append(s0,s1), n) } (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && (Seq#Length(s0) <= n ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); function Seq#Update(s: Seq, i: int, val: Box) : Seq; -axiom (forall s: Seq, i: int, v: Box :: { Seq#Length(Seq#Update(s,i,v)) } +axiom (forall s: Seq, i: int, v: Box :: + { Seq#Length(Seq#Update(s,i,v)) } 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s,i,v)) == Seq#Length(s)); -axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Index(Seq#Update(s,i,v),n) } +axiom (forall s: Seq, i: int, v: Box, n: int :: + { Seq#Index(Seq#Update(s,i,v),n) } 0 <= n && n < Seq#Length(s) ==> (i == n ==> Seq#Index(Seq#Update(s,i,v),n) == v) && (i != n ==> Seq#Index(Seq#Update(s,i,v),n) == Seq#Index(s,n))); @@ -1041,9 +1048,13 @@ function Seq#Append(s0: Seq, s1: Seq) : Seq; function Seq#Contains(s: Seq, val: Box) : bool; -axiom (forall s: Seq, x: Box :: { Seq#Contains(s,x) } +axiom (forall s: Seq, x: Box :: + { Seq#Contains(s,x) } Seq#Contains(s,x) <==> - (exists i: int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && Seq#Index(s,i) == x)); + (exists i: int :: + { Seq#Index(s,i) } + 0 <= i && i < Seq#Length(s) && Seq#Index(s,i) == x)); + axiom (forall x: Box :: { Seq#Contains(Seq#Empty(), x) } !Seq#Contains(Seq#Empty(), x)); @@ -1060,35 +1071,44 @@ axiom (forall s: Seq, v: Box, x: Box :: // needed to prove things like '4 in [2 axiom (forall s: Seq, n: int, x: Box :: { Seq#Contains(Seq#Take(s, n), x) } Seq#Contains(Seq#Take(s, n), x) <==> - (exists i: int :: { Seq#Index(s, i) } + (exists i: int :: + { Seq#Index(s, i) } 0 <= i && i < n && i < Seq#Length(s) && Seq#Index(s, i) == x)); axiom (forall s: Seq, n: int, x: Box :: { Seq#Contains(Seq#Drop(s, n), x) } Seq#Contains(Seq#Drop(s, n), x) <==> - (exists i: int :: { Seq#Index(s, i) } + (exists i: int :: + { Seq#Index(s, i) } 0 <= n && n <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); function Seq#Equal(s0: Seq, s1: Seq) : bool; -axiom (forall s0: Seq, s1: Seq :: { Seq#Equal(s0,s1) } +axiom (forall s0: Seq, s1: Seq :: + { Seq#Equal(s0,s1) } Seq#Equal(s0,s1) <==> Seq#Length(s0) == Seq#Length(s1) && - (forall j: int :: { Seq#Index(s0,j) } { Seq#Index(s1,j) } + (forall j: int :: + { Seq#Index(s0,j) } + { Seq#Index(s1,j) } 0 <= j && j < Seq#Length(s0) ==> Seq#Index(s0,j) == Seq#Index(s1,j))); -axiom (forall a: Seq, b: Seq :: { Seq#Equal(a,b) } // extensionality axiom for sequences - Seq#Equal(a,b) ==> a == b); + +// extensionality axiom for sequences +axiom (forall a: Seq, b: Seq :: { Seq#Equal(a,b) } Seq#Equal(a,b) ==> a == b); function Seq#SameUntil(s0: Seq, s1: Seq, n: int) : bool; -axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#SameUntil(s0,s1,n) } +axiom (forall s0: Seq, s1: Seq, n: int :: + { Seq#SameUntil(s0,s1,n) } Seq#SameUntil(s0,s1,n) <==> (forall j: int :: { Seq#Index(s0,j) } { Seq#Index(s1,j) } 0 <= j && j < n ==> Seq#Index(s0,j) == Seq#Index(s1,j))); function Seq#Take(s: Seq, howMany: int) : Seq; -axiom (forall s: Seq, n: int :: { Seq#Length(Seq#Take(s,n)) } +axiom (forall s: Seq, n: int :: + { Seq#Length(Seq#Take(s,n)) } 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Take(s,n)) == n); + axiom (forall s: Seq, n: int, j: int :: {:weight 25} { Seq#Index(Seq#Take(s,n), j) } @@ -1098,13 +1118,16 @@ axiom (forall s: Seq, n: int, j: int :: function Seq#Drop(s: Seq, howMany: int) : Seq; -axiom (forall s: Seq, n: int :: { Seq#Length(Seq#Drop(s,n)) } +axiom (forall s: Seq, n: int :: + { Seq#Length(Seq#Drop(s,n)) } 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s,n)) == Seq#Length(s) - n); + axiom (forall s: Seq, n: int, j: int :: {:weight 25} { Seq#Index(Seq#Drop(s,n), j) } 0 <= n && 0 <= j && j < Seq#Length(s)-n ==> Seq#Index(Seq#Drop(s,n), j) == Seq#Index(s, j+n)); + axiom (forall s: Seq, n: int, k: int :: {:weight 25} { Seq#Index(s, k), Seq#Drop(s,n) } From 005109e9eb8e549213970ce94e1f31d399a8dd5e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:45:11 -0700 Subject: [PATCH 23/52] Remove the used Seq#Singleton --- Source/DafnyCore/DafnyPrelude.bpl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index db88158ab47..17e154138e9 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1024,10 +1024,6 @@ axiom (forall s0: Seq, s1: Seq :: function Seq#Index(s: Seq, i: int) : Box; -axiom (forall t: Box :: - { Seq#Index(Seq#Singleton(t), 0) } - Seq#Index(Seq#Singleton(t), 0) == t); - axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#Index(Seq#Append(s0,s1), n) } (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && From 3a8a234fda498cdad5b54e431163f46ab1cf996b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:37:14 -0700 Subject: [PATCH 24/52] chore: Add spaces after commas --- Source/DafnyCore/DafnyPrelude.bpl | 74 +++++++++++++++---------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 17e154138e9..3420c8bb3ba 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1005,8 +1005,8 @@ axiom (forall s: Seq, i: int, v: Box :: && (i != Seq#Length(s) ==> Seq#Index(Seq#Build(s, v), i) == Seq#Index(s, i))); // Build preserves $Is -axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s,bx),TSeq(t)) } - $Is(s,TSeq(t)) && $IsBox(bx,t) ==> $Is(Seq#Build(s,bx),TSeq(t))); +axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s, bx), TSeq(t)) } + $Is(s, TSeq(t)) && $IsBox(bx, t) ==> $Is(Seq#Build(s, bx), TSeq(t))); function Seq#Create(ty: Ty, heap: Heap, len: int, init: HandleType): Seq; axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType :: @@ -1019,37 +1019,37 @@ axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: Seq#Index(Seq#Create(ty, heap, len, init), i) == Apply1(TInt, ty, heap, init, $Box(i))); axiom (forall s0: Seq, s1: Seq :: - { Seq#Length(Seq#Append(s0,s1)) } - Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); + { Seq#Length(Seq#Append(s0, s1)) } + Seq#Length(Seq#Append(s0, s1)) == Seq#Length(s0) + Seq#Length(s1)); function Seq#Index(s: Seq, i: int) : Box; axiom (forall s0: Seq, s1: Seq, n: int :: - { Seq#Index(Seq#Append(s0,s1), n) } - (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && - (Seq#Length(s0) <= n ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); + { Seq#Index(Seq#Append(s0, s1), n) } + (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0, s1), n) == Seq#Index(s0, n)) && + (Seq#Length(s0) <= n ==> Seq#Index(Seq#Append(s0, s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); function Seq#Update(s: Seq, i: int, val: Box) : Seq; axiom (forall s: Seq, i: int, v: Box :: - { Seq#Length(Seq#Update(s,i,v)) } - 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s,i,v)) == Seq#Length(s)); + { Seq#Length(Seq#Update(s, i, v)) } + 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s, i, v)) == Seq#Length(s)); axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Index(Seq#Update(s,i,v),n) } + { Seq#Index(Seq#Update(s, i, v), n) } 0 <= n && n < Seq#Length(s) ==> - (i == n ==> Seq#Index(Seq#Update(s,i,v),n) == v) && - (i != n ==> Seq#Index(Seq#Update(s,i,v),n) == Seq#Index(s,n))); + (i == n ==> Seq#Index(Seq#Update(s, i, v), n) == v) && + (i != n ==> Seq#Index(Seq#Update(s, i, v), n) == Seq#Index(s, n))); function Seq#Append(s0: Seq, s1: Seq) : Seq; function Seq#Contains(s: Seq, val: Box) : bool; axiom (forall s: Seq, x: Box :: - { Seq#Contains(s,x) } - Seq#Contains(s,x) <==> + { Seq#Contains(s, x) } + Seq#Contains(s, x) <==> (exists i: int :: - { Seq#Index(s,i) } - 0 <= i && i < Seq#Length(s) && Seq#Index(s,i) == x)); + { Seq#Index(s, i) } + 0 <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); axiom (forall x: Box :: { Seq#Contains(Seq#Empty(), x) } @@ -1080,55 +1080,55 @@ axiom (forall s: Seq, n: int, x: Box :: function Seq#Equal(s0: Seq, s1: Seq) : bool; axiom (forall s0: Seq, s1: Seq :: - { Seq#Equal(s0,s1) } - Seq#Equal(s0,s1) <==> + { Seq#Equal(s0, s1) } + Seq#Equal(s0, s1) <==> Seq#Length(s0) == Seq#Length(s1) && (forall j: int :: - { Seq#Index(s0,j) } - { Seq#Index(s1,j) } - 0 <= j && j < Seq#Length(s0) ==> Seq#Index(s0,j) == Seq#Index(s1,j))); + { Seq#Index(s0, j) } + { Seq#Index(s1, j) } + 0 <= j && j < Seq#Length(s0) ==> Seq#Index(s0, j) == Seq#Index(s1, j))); // extensionality axiom for sequences -axiom (forall a: Seq, b: Seq :: { Seq#Equal(a,b) } Seq#Equal(a,b) ==> a == b); +axiom (forall a: Seq, b: Seq :: { Seq#Equal(a, b) } Seq#Equal(a, b) ==> a == b); function Seq#SameUntil(s0: Seq, s1: Seq, n: int) : bool; axiom (forall s0: Seq, s1: Seq, n: int :: - { Seq#SameUntil(s0,s1,n) } - Seq#SameUntil(s0,s1,n) <==> - (forall j: int :: { Seq#Index(s0,j) } { Seq#Index(s1,j) } - 0 <= j && j < n ==> Seq#Index(s0,j) == Seq#Index(s1,j))); + { Seq#SameUntil(s0, s1, n) } + Seq#SameUntil(s0, s1, n) <==> + (forall j: int :: { Seq#Index(s0, j) } { Seq#Index(s1, j) } + 0 <= j && j < n ==> Seq#Index(s0, j) == Seq#Index(s1, j))); function Seq#Take(s: Seq, howMany: int) : Seq; axiom (forall s: Seq, n: int :: - { Seq#Length(Seq#Take(s,n)) } - 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Take(s,n)) == n); + { Seq#Length(Seq#Take(s, n)) } + 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Take(s, n)) == n); axiom (forall s: Seq, n: int, j: int :: {:weight 25} - { Seq#Index(Seq#Take(s,n), j) } - { Seq#Index(s, j), Seq#Take(s,n) } + { Seq#Index(Seq#Take(s, n), j) } + { Seq#Index(s, j), Seq#Take(s, n) } 0 <= j && j < n && j < Seq#Length(s) ==> - Seq#Index(Seq#Take(s,n), j) == Seq#Index(s, j)); + Seq#Index(Seq#Take(s, n), j) == Seq#Index(s, j)); function Seq#Drop(s: Seq, howMany: int) : Seq; axiom (forall s: Seq, n: int :: - { Seq#Length(Seq#Drop(s,n)) } - 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s,n)) == Seq#Length(s) - n); + { Seq#Length(Seq#Drop(s, n)) } + 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s, n)) == Seq#Length(s) - n); axiom (forall s: Seq, n: int, j: int :: {:weight 25} - { Seq#Index(Seq#Drop(s,n), j) } + { Seq#Index(Seq#Drop(s, n), j) } 0 <= n && 0 <= j && j < Seq#Length(s)-n ==> - Seq#Index(Seq#Drop(s,n), j) == Seq#Index(s, j+n)); + Seq#Index(Seq#Drop(s, n), j) == Seq#Index(s, j+n)); axiom (forall s: Seq, n: int, k: int :: {:weight 25} - { Seq#Index(s, k), Seq#Drop(s,n) } + { Seq#Index(s, k), Seq#Drop(s, n) } 0 <= n && n <= k && k < Seq#Length(s) ==> - Seq#Index(Seq#Drop(s,n), k-n) == Seq#Index(s, k)); + Seq#Index(Seq#Drop(s, n), k-n) == Seq#Index(s, k)); axiom (forall s, t: Seq, n: int :: { Seq#Take(Seq#Append(s, t), n) } From 9855a012add8f6ca2a343359b57626fa9fea0c07 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 15:43:26 -0700 Subject: [PATCH 25/52] chore: Remove trailing spaces --- Source/DafnyCore/DafnyPrelude.bpl | 10 +++++----- Source/DafnyCore/Prelude/Makefile | 4 ++++ Source/DafnyCore/Prelude/PreludeCore.bpl | 10 +++++----- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 3420c8bb3ba..469376549c9 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -117,7 +117,7 @@ axiom (forall x: real :: { $Box(LitReal(x)) } $Box(LitReal(x)) == Lit($Box(x)) ) #if UNICODE_CHAR function {:inline} char#IsChar(n: int): bool { - (0 <= n && n < 55296 /* 0xD800 */) || + (0 <= n && n < 55296 /* 0xD800 */) || (57344 /* 0xE000 */ <= n && n < 1114112 /* 0x11_0000 */ ) } #else @@ -263,7 +263,7 @@ axiom (forall v: Map, t0: Ty, t1: Ty :: Map#Domain(v)[bx] ==> $IsBox(Map#Elements(v)[bx], t1) && $IsBox(bx, t0))); - + axiom (forall v: Map, t0: Ty, t1: Ty :: { $Is(v, TMap(t0, t1)) } $Is(v, TMap(t0, t1)) ==> @@ -291,9 +291,9 @@ axiom(forall h : Heap, v : real :: { $IsAlloc(v,TReal,h) } $IsAlloc(v,TReal,h)); axiom(forall h : Heap, v : bool :: { $IsAlloc(v,TBool,h) } $IsAlloc(v,TBool,h)); axiom(forall h : Heap, v : char :: { $IsAlloc(v,TChar,h) } $IsAlloc(v,TChar,h)); axiom(forall h : Heap, v : ORDINAL :: { $IsAlloc(v,TORDINAL,h) } $IsAlloc(v,TORDINAL,h)); - + axiom (forall v: Bv0, h: Heap :: { $IsAlloc(v, TBitvector(0), h) } $IsAlloc(v, TBitvector(0), h)); - + axiom (forall v: Set, t0: Ty, h: Heap :: { $IsAlloc(v, TSet(t0), h) } $IsAlloc(v, TSet(t0), h) <==> (forall bx: Box :: { v[bx] } @@ -320,7 +320,7 @@ axiom (forall v: Map, t0: Ty, t1: Ty, h: Heap :: Map#Domain(v)[bx] ==> $IsAllocBox(Map#Elements(v)[bx], t1, h) && $IsAllocBox(bx, t0, h))); - + axiom (forall v: IMap, t0: Ty, t1: Ty, h: Heap :: { $IsAlloc(v, TIMap(t0, t1), h) } $IsAlloc(v, TIMap(t0, t1), h) diff --git a/Source/DafnyCore/Prelude/Makefile b/Source/DafnyCore/Prelude/Makefile index 4dedea4f27c..d609705c768 100644 --- a/Source/DafnyCore/Prelude/Makefile +++ b/Source/DafnyCore/Prelude/Makefile @@ -8,9 +8,13 @@ DafnyPrelude.bpl: Sequences.bpl # also, we need to disable preprocessing of Boogie things sed -e "s|^#if|//#if|" -i "" PreludeCore.bpl sed -e "s|^#e|//#e|" -i "" PreludeCore.bpl + # Extract Boogie from the model verified in Dafny cpp -C -P PreludeCore.bpl DafnyPrelude.bpl + # Undo the song and dance with primes and Boogie preprocessing directives sed -e "s|^//#|#|" -i "" PreludeCore.bpl DafnyPrelude.bpl sed -e "s|PRIME|'|g" -i "" PreludeCore.bpl Sequences.bpl DafnyPrelude.bpl Sequences.bpl: Sequences.dfy $(DAFNY) verify Sequences.dfy --extract:Sequences.bpl + # Remove trailing spaces that the Boogie pretty printer emits + sed -e "s| *$$||" -i "" Sequences.bpl diff --git a/Source/DafnyCore/Prelude/PreludeCore.bpl b/Source/DafnyCore/Prelude/PreludeCore.bpl index f37403a4097..c22570ae032 100644 --- a/Source/DafnyCore/Prelude/PreludeCore.bpl +++ b/Source/DafnyCore/Prelude/PreludeCore.bpl @@ -117,7 +117,7 @@ axiom (forall x: real :: { $Box(LitReal(x)) } $Box(LitReal(x)) == Lit($Box(x)) ) #if UNICODE_CHAR function {:inline} char#IsChar(n: int): bool { - (0 <= n && n < 55296 /* 0xD800 */) || + (0 <= n && n < 55296 /* 0xD800 */) || (57344 /* 0xE000 */ <= n && n < 1114112 /* 0x11_0000 */ ) } #else @@ -263,7 +263,7 @@ axiom (forall v: Map, t0: Ty, t1: Ty :: Map#Domain(v)[bx] ==> $IsBox(Map#Elements(v)[bx], t1) && $IsBox(bx, t0))); - + axiom (forall v: Map, t0: Ty, t1: Ty :: { $Is(v, TMap(t0, t1)) } $Is(v, TMap(t0, t1)) ==> @@ -291,9 +291,9 @@ axiom(forall h : Heap, v : real :: { $IsAlloc(v,TReal,h) } $IsAlloc(v,TReal,h)); axiom(forall h : Heap, v : bool :: { $IsAlloc(v,TBool,h) } $IsAlloc(v,TBool,h)); axiom(forall h : Heap, v : char :: { $IsAlloc(v,TChar,h) } $IsAlloc(v,TChar,h)); axiom(forall h : Heap, v : ORDINAL :: { $IsAlloc(v,TORDINAL,h) } $IsAlloc(v,TORDINAL,h)); - + axiom (forall v: Bv0, h: Heap :: { $IsAlloc(v, TBitvector(0), h) } $IsAlloc(v, TBitvector(0), h)); - + axiom (forall v: Set, t0: Ty, h: Heap :: { $IsAlloc(v, TSet(t0), h) } $IsAlloc(v, TSet(t0), h) <==> (forall bx: Box :: { v[bx] } @@ -320,7 +320,7 @@ axiom (forall v: Map, t0: Ty, t1: Ty, h: Heap :: Map#Domain(v)[bx] ==> $IsAllocBox(Map#Elements(v)[bx], t1, h) && $IsAllocBox(bx, t0, h))); - + axiom (forall v: IMap, t0: Ty, t1: Ty, h: Heap :: { $IsAlloc(v, TIMap(t0, t1), h) } $IsAlloc(v, TIMap(t0, t1), h) From f98bde47aa0aa3645681c7d92cc2bc3a51703366 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 16:00:49 -0700 Subject: [PATCH 26/52] Update formatting using pretty-printed Boogie --- Source/DafnyCore/DafnyPrelude.bpl | 72 +++++++++++++++---------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 469376549c9..30b2f98ecf7 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1026,14 +1026,16 @@ function Seq#Index(s: Seq, i: int) : Box; axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#Index(Seq#Append(s0, s1), n) } - (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0, s1), n) == Seq#Index(s0, n)) && - (Seq#Length(s0) <= n ==> Seq#Index(Seq#Append(s0, s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); + (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0, s1), n) == Seq#Index(s0, n)) + && (Seq#Length(s0) <= n + ==> Seq#Index(Seq#Append(s0, s1), n) == Seq#Index(s1, n - Seq#Length(s0)))); function Seq#Update(s: Seq, i: int, val: Box) : Seq; axiom (forall s: Seq, i: int, v: Box :: { Seq#Length(Seq#Update(s, i, v)) } 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s, i, v)) == Seq#Length(s)); + axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Index(Seq#Update(s, i, v), n) } 0 <= n && n < Seq#Length(s) ==> @@ -1046,10 +1048,10 @@ function Seq#Contains(s: Seq, val: Box) : bool; axiom (forall s: Seq, x: Box :: { Seq#Contains(s, x) } - Seq#Contains(s, x) <==> - (exists i: int :: - { Seq#Index(s, i) } - 0 <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); + Seq#Contains(s, x) + <==> (exists i: int :: + { Seq#Index(s, i) } + 0 <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); axiom (forall x: Box :: { Seq#Contains(Seq#Empty(), x) } @@ -1057,8 +1059,8 @@ axiom (forall x: Box :: axiom (forall s0: Seq, s1: Seq, x: Box :: { Seq#Contains(Seq#Append(s0, s1), x) } - Seq#Contains(Seq#Append(s0, s1), x) <==> - Seq#Contains(s0, x) || Seq#Contains(s1, x)); + Seq#Contains(Seq#Append(s0, s1), x) + <==> Seq#Contains(s0, x) || Seq#Contains(s1, x)); axiom (forall s: Seq, v: Box, x: Box :: // needed to prove things like '4 in [2,3,4]', see method TestSequences0 in SmallTests.dfy { Seq#Contains(Seq#Build(s, v), x) } @@ -1066,14 +1068,15 @@ axiom (forall s: Seq, v: Box, x: Box :: // needed to prove things like '4 in [2 axiom (forall s: Seq, n: int, x: Box :: { Seq#Contains(Seq#Take(s, n), x) } - Seq#Contains(Seq#Take(s, n), x) <==> - (exists i: int :: + Seq#Contains(Seq#Take(s, n), x) + <==> (exists i: int :: { Seq#Index(s, i) } 0 <= i && i < n && i < Seq#Length(s) && Seq#Index(s, i) == x)); + axiom (forall s: Seq, n: int, x: Box :: { Seq#Contains(Seq#Drop(s, n), x) } - Seq#Contains(Seq#Drop(s, n), x) <==> - (exists i: int :: + Seq#Contains(Seq#Drop(s, n), x) + <==> (exists i: int :: { Seq#Index(s, i) } 0 <= n && n <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); @@ -1081,11 +1084,10 @@ function Seq#Equal(s0: Seq, s1: Seq) : bool; axiom (forall s0: Seq, s1: Seq :: { Seq#Equal(s0, s1) } - Seq#Equal(s0, s1) <==> - Seq#Length(s0) == Seq#Length(s1) && - (forall j: int :: - { Seq#Index(s0, j) } - { Seq#Index(s1, j) } + Seq#Equal(s0, s1) + <==> Seq#Length(s0) == Seq#Length(s1) + && (forall j: int :: + { Seq#Index(s0, j) } { Seq#Index(s1, j) } 0 <= j && j < Seq#Length(s0) ==> Seq#Index(s0, j) == Seq#Index(s1, j))); // extensionality axiom for sequences @@ -1095,9 +1097,10 @@ function Seq#SameUntil(s0: Seq, s1: Seq, n: int) : bool; axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#SameUntil(s0, s1, n) } - Seq#SameUntil(s0, s1, n) <==> - (forall j: int :: { Seq#Index(s0, j) } { Seq#Index(s1, j) } - 0 <= j && j < n ==> Seq#Index(s0, j) == Seq#Index(s1, j))); + Seq#SameUntil(s0, s1, n) + <==> (forall j: int :: + { Seq#Index(s0, j) } { Seq#Index(s1, j) } + 0 <= j && j < n ==> Seq#Index(s0, j) == Seq#Index(s1, j))); function Seq#Take(s: Seq, howMany: int) : Seq; @@ -1106,11 +1109,9 @@ axiom (forall s: Seq, n: int :: 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Take(s, n)) == n); axiom (forall s: Seq, n: int, j: int :: - {:weight 25} - { Seq#Index(Seq#Take(s, n), j) } - { Seq#Index(s, j), Seq#Take(s, n) } - 0 <= j && j < n && j < Seq#Length(s) ==> - Seq#Index(Seq#Take(s, n), j) == Seq#Index(s, j)); + {:weight 25} { Seq#Index(Seq#Take(s, n), j) } { Seq#Index(s, j), Seq#Take(s, n) } + 0 <= j && j < n && j < Seq#Length(s) + ==> Seq#Index(Seq#Take(s, n), j) == Seq#Index(s, j)); function Seq#Drop(s: Seq, howMany: int) : Seq; @@ -1119,24 +1120,19 @@ axiom (forall s: Seq, n: int :: 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s, n)) == Seq#Length(s) - n); axiom (forall s: Seq, n: int, j: int :: - {:weight 25} - { Seq#Index(Seq#Drop(s, n), j) } - 0 <= n && 0 <= j && j < Seq#Length(s)-n ==> - Seq#Index(Seq#Drop(s, n), j) == Seq#Index(s, j+n)); + {:weight 25} { Seq#Index(Seq#Drop(s, n), j) } + 0 <= n && 0 <= j && j < Seq#Length(s) - n + ==> Seq#Index(Seq#Drop(s, n), j) == Seq#Index(s, j + n)); axiom (forall s: Seq, n: int, k: int :: - {:weight 25} - { Seq#Index(s, k), Seq#Drop(s, n) } - 0 <= n && n <= k && k < Seq#Length(s) ==> - Seq#Index(Seq#Drop(s, n), k-n) == Seq#Index(s, k)); + {:weight 25} { Seq#Index(s, k), Seq#Drop(s, n) } + 0 <= n && n <= k && k < Seq#Length(s) + ==> Seq#Index(Seq#Drop(s, n), k - n) == Seq#Index(s, k)); axiom (forall s, t: Seq, n: int :: - { Seq#Take(Seq#Append(s, t), n) } - { Seq#Drop(Seq#Append(s, t), n) } + { Seq#Take(Seq#Append(s, t), n) } { Seq#Drop(Seq#Append(s, t), n) } n == Seq#Length(s) - ==> - Seq#Take(Seq#Append(s, t), n) == s && - Seq#Drop(Seq#Append(s, t), n) == t); + ==> Seq#Take(Seq#Append(s, t), n) == s && Seq#Drop(Seq#Append(s, t), n) == t); function Seq#FromArray(h: Heap, a: ref): Seq; axiom (forall h: Heap, a: ref :: From fd3efe5c2331329ae90bc7d4710b289dab5ac5a6 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 16:06:42 -0700 Subject: [PATCH 27/52] Fix extraction of logical not --- Source/DafnyCore/Backends/Extractor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index f292e32fd95..cfe214ddad6 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -247,7 +247,7 @@ private Boogie.Expr ExtractExpr(Expression expr) { case UnaryOpExpr unaryOpExpr: { Contract.Assert(unaryOpExpr.ResolvedOp == UnaryOpExpr.ResolvedOpcode.BoolNot); // TODO: fail more gently var e = ExtractExpr(unaryOpExpr.E); - return Boogie.Expr.Neg(e); + return Boogie.Expr.Not(e); } case QuantifierExpr quantifierExpr: { From d0afb380fe9309c30cd750b189b66460c272e6f9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 16:06:59 -0700 Subject: [PATCH 28/52] Adjust formatting --- Source/DafnyCore/DafnyPrelude.bpl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 30b2f98ecf7..2367b0ea617 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1038,9 +1038,9 @@ axiom (forall s: Seq, i: int, v: Box :: axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Index(Seq#Update(s, i, v), n) } - 0 <= n && n < Seq#Length(s) ==> - (i == n ==> Seq#Index(Seq#Update(s, i, v), n) == v) && - (i != n ==> Seq#Index(Seq#Update(s, i, v), n) == Seq#Index(s, n))); + 0 <= n && n < Seq#Length(s) + ==> (i == n ==> Seq#Index(Seq#Update(s, i, v), n) == v) + && (i != n ==> Seq#Index(Seq#Update(s, i, v), n) == Seq#Index(s, n))); function Seq#Append(s0: Seq, s1: Seq) : Seq; @@ -1062,7 +1062,8 @@ axiom (forall s0: Seq, s1: Seq, x: Box :: Seq#Contains(Seq#Append(s0, s1), x) <==> Seq#Contains(s0, x) || Seq#Contains(s1, x)); -axiom (forall s: Seq, v: Box, x: Box :: // needed to prove things like '4 in [2,3,4]', see method TestSequences0 in SmallTests.dfy +// needed to prove things like '4 in [2,3,4]', see method TestSequences0 in SmallTests.dfy +axiom (forall s: Seq, v: Box, x: Box :: { Seq#Contains(Seq#Build(s, v), x) } Seq#Contains(Seq#Build(s, v), x) <==> (v == x || Seq#Contains(s, x))); From 8a6902d05cffc753e0c52f05d0652541d75cfa37 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 16:24:23 -0700 Subject: [PATCH 29/52] Fix order of pattern attributes in output --- Source/DafnyCore/Backends/Extractor.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index cfe214ddad6..13c12e68991 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -80,9 +80,10 @@ public override void VisitMethod(Method method) { var patterns = Attributes.FindAllExpressions(lemma.Attributes, "extract_pattern"); var usedByInfo = Attributes.Find(lemma.Attributes, "extract_used_by"); - if (patterns == null & usedByInfo == null) { + if (patterns == null && usedByInfo == null) { return; } + Contract.Assert((lemma.Ins.Count == 0) == (patterns == null)); // a parameterized lemma must have patterns // TODO: fail more gently Contract.Assert(lemma.TypeArgs.Count == 0); // TODO: fail more gently Contract.Assert(lemma.Outs.Count == 0); // TODO: fail more gently @@ -93,7 +94,7 @@ public override void VisitMethod(Method method) { (Boogie.Variable)new Boogie.BoundVariable(tok, new TypedIdent(tok, formal.Name, ExtractType(formal.Type))) ); - var triggers = GetTriggers(tok, boundVars, patterns); + var triggers = GetTriggers(tok, patterns); var ante = BoogieGenerator.BplAnd(lemma.Req.ConvertAll(req => ExtractExpr(req.E))); var post = BoogieGenerator.BplAnd(lemma.Ens.ConvertAll(ens => ExtractExpr(ens.E))); @@ -117,10 +118,13 @@ public override void VisitMethod(Method method) { } } - private Trigger? GetTriggers(IToken tok, List boundVars, List>? patterns) { + private Trigger? GetTriggers(IToken tok, List>? patterns) { + if (patterns == null) { + return null; + } + Boogie.Trigger? triggers = null; - Contract.Assert(boundVars.Count != 0 || patterns == null); - for (var i = patterns == null ? 0 : patterns.Count; 0 <= --i;) { + for (var i = 0; i < patterns.Count; i++) { var terms = patterns![i].ConvertAll(ExtractExpr); triggers = new Boogie.Trigger(tok, true, terms, triggers); } @@ -251,15 +255,13 @@ private Boogie.Expr ExtractExpr(Expression expr) { } case QuantifierExpr quantifierExpr: { - // TODO: look for :extract_pattern - var boundVars = quantifierExpr.BoundVars.ConvertAll(boundVar => (Boogie.Variable)new Boogie.BoundVariable(tok, new TypedIdent(tok, boundVar.Name, ExtractType(boundVar.Type))) ); var patterns = Attributes.FindAllExpressions(quantifierExpr.Attributes, "extract_pattern"); Contract.Assert(patterns.Count != 0); // don't support pattern-less quantifiers // TODO: fail more gracefully - var triggers = GetTriggers(tok, boundVars, patterns); + var triggers = GetTriggers(tok, patterns); var kv = GetKeyValues(tok, quantifierExpr.Attributes); var body = ExtractExpr(quantifierExpr.LogicalBody()); From 02c45ca58d9d4c7ab24a0944646bfec4bec31d00 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 16:24:39 -0700 Subject: [PATCH 30/52] Adjust formatting --- Source/DafnyCore/DafnyPrelude.bpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 2367b0ea617..e3b3e34fb97 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1065,7 +1065,7 @@ axiom (forall s0: Seq, s1: Seq, x: Box :: // needed to prove things like '4 in [2,3,4]', see method TestSequences0 in SmallTests.dfy axiom (forall s: Seq, v: Box, x: Box :: { Seq#Contains(Seq#Build(s, v), x) } - Seq#Contains(Seq#Build(s, v), x) <==> (v == x || Seq#Contains(s, x))); + Seq#Contains(Seq#Build(s, v), x) <==> v == x || Seq#Contains(s, x)); axiom (forall s: Seq, n: int, x: Box :: { Seq#Contains(Seq#Take(s, n), x) } @@ -1130,7 +1130,7 @@ axiom (forall s: Seq, n: int, k: int :: 0 <= n && n <= k && k < Seq#Length(s) ==> Seq#Index(Seq#Drop(s, n), k - n) == Seq#Index(s, k)); -axiom (forall s, t: Seq, n: int :: +axiom (forall s: Seq, t: Seq, n: int :: { Seq#Take(Seq#Append(s, t), n) } { Seq#Drop(Seq#Append(s, t), n) } n == Seq#Length(s) ==> Seq#Take(Seq#Append(s, t), n) == s && Seq#Drop(Seq#Append(s, t), n) == t); From 4187d2cbfe3cee676838b55fe3305b58f5fb4f03 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 16:25:42 -0700 Subject: [PATCH 31/52] Change order of declarations --- Source/DafnyCore/Prelude/Sequences.dfy | 252 ++++++++++++------------- 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index 2cd22aac102..f22a8396517 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -267,19 +267,6 @@ module {:extract} Sequences { } } - // boogie: function Seq#Index(Seq, int): Box; - function {:extract "Seq#Index"} Index(s: Seq, i: int): Box { - if 0 <= i < Length(s) then - s.At(i) - else - Boxes.arbitrary - } - - // boogie: function Seq#Append(Seq, Seq): Seq; - function {:extract_name "Seq#Append"} Append(s0: Seq, s1: Seq): Seq { - s0.Append(s1) - } - // boogie: // axiom (forall s0: Seq, s1: Seq :: { Seq#Length(Seq#Append(s0,s1)) } // Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); @@ -289,6 +276,14 @@ module {:extract} Sequences { s0.LengthAppend(s1); } + // boogie: function Seq#Index(Seq, int): Box; + function {:extract_name "Seq#Index"} Index(s: Seq, i: int): Box { + if 0 <= i < Length(s) then + s.At(i) + else + Boxes.arbitrary + } + // boogie: // axiom (forall s0: Seq, s1: Seq, n: int :: { Seq#Index(Seq#Append(s0,s1), n) } // (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && @@ -412,6 +407,11 @@ module {:extract} Sequences { } } + // boogie: function Seq#Append(Seq, Seq): Seq; + function {:extract_name "Seq#Append"} Append(s0: Seq, s1: Seq): Seq { + s0.Append(s1) + } + // boogie: function Seq#Contains(Seq, Box): bool; predicate {:extract_name "Seq#Contains"} Contains(s: Seq, val: Box) { exists i :: 0 <= i < Length(s) && Index(s, i) == val @@ -515,6 +515,119 @@ module {:extract} Sequences { } } + // boogie: + // axiom (forall s: Seq, n: int, x: Box :: + // { Seq#Contains(Seq#Take(s, n), x) } + // Seq#Contains(Seq#Take(s, n), x) <==> + // (exists i: int :: { Seq#Index(s, i) } + // 0 <= i && i < n && i < Seq#Length(s) && Seq#Index(s, i) == x)); + lemma {:extract_pattern Contains(Take(s, n), x)} TakeContains(s: Seq, n: int, x: Box) + ensures Contains(Take(s, n), x) <==> + exists i: int {:extract_pattern Index(s, i)} :: 0 <= i < n && i < Length(s) && Index(s, i) == x + { + if + case n < 0 => + calc { + Contains(Take(s, n), x); + { assert Take(s, n) == Empty(); } + Contains(Empty(), x); + false; + exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; + } + + case 0 <= n <= s.Length() => + var (prefix, suffix) := s.Split(n); + var t := s.Take(n); + assert t == prefix; + assert t.Length() == n by { + s.LengthTakeDrop(n); + } + assert L: forall i :: 0 <= i < Length(t) ==> Index(t, i) == Index(s, i) by { + forall i | 0 <= i < Length(t) + ensures Index(t, i) == Index(s, i) + { + prefix.AppendAt(suffix, i); + } + } + + calc { + Contains(t, x); + exists i :: 0 <= i < Length(t) && Index(t, i) == x; + { reveal L; } + exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; + } + + case s.Length() < n => + calc { + Contains(Take(s, n), x); + Contains(s, x); + exists i :: 0 <= i < Length(s) && Index(s, i) == x; + { assert Length(s) < n; } + exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; + } + } + + // boogie: + // axiom (forall s: Seq, n: int, x: Box :: + // { Seq#Contains(Seq#Drop(s, n), x) } + // Seq#Contains(Seq#Drop(s, n), x) <==> + // (exists i: int :: { Seq#Index(s, i) } + // 0 <= n && n <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); + lemma {:extract_pattern Contains(Drop(s, n), x)} DropContains(s: Seq, n: int, x: Box) + ensures Contains(Drop(s, n), x) <==> + exists i: int {:extract_pattern Index(s, i)} :: 0 <= n <= i < Length(s) && Index(s, i) == x + { + if 0 <= n <= s.Length() { + var (prefix, suffix) := s.Split(n); + var t := s.Take(n); + assert t == prefix; + assert t.Length() == n by { + s.LengthTakeDrop(n); + } + assert L: forall i :: 0 <= i < Length(suffix) ==> Index(suffix, i) == Index(s, n + i) by { + forall i | 0 <= i < Length(suffix) { + calc { + Index(s, n + i); + { prefix.LengthAppend(suffix); } + s.At(n + i); + { prefix.AppendAt(suffix, n + i); } + suffix.At(i); + Index(suffix, i); + } + } + } + + if Contains(Drop(s, n), x) { + var j :| 0 <= j < Length(suffix) && Index(suffix, j) == x; + var i := n + j; + assert 0 <= n <= i < Length(s) by { + prefix.LengthAppend(suffix); + } + assert Index(s, i) == x by { + reveal L; + } + } + + if i :| 0 <= n <= i < Length(s) && Index(s, i) == x { + var j := i - n; + assert 0 <= j < Length(suffix) by { + prefix.LengthAppend(suffix); + } + assert Index(suffix, j) == x by { + reveal L; + } + } + + } else { + calc { + Contains(Drop(s, n), x); + Contains(Empty(), x); + false; + exists i :: 0 <= n <= i < Length(s) && Index(s, i) == x; + } + } + } + // boogie: function Seq#Equal(Seq, Seq): bool; predicate {:extract_name "Seq#Equal"} Equal(s0: Seq, s1: Seq) { s0 == s1 @@ -663,119 +776,6 @@ module {:extract} Sequences { } } - // boogie: - // axiom (forall s: Seq, n: int, x: Box :: - // { Seq#Contains(Seq#Take(s, n), x) } - // Seq#Contains(Seq#Take(s, n), x) <==> - // (exists i: int :: { Seq#Index(s, i) } - // 0 <= i && i < n && i < Seq#Length(s) && Seq#Index(s, i) == x)); - lemma {:extract_pattern Contains(Take(s, n), x)} TakeContains(s: Seq, n: int, x: Box) - ensures Contains(Take(s, n), x) <==> - exists i: int {:extract_pattern Index(s, i)} :: 0 <= i < n && i < Length(s) && Index(s, i) == x - { - if - case n < 0 => - calc { - Contains(Take(s, n), x); - { assert Take(s, n) == Empty(); } - Contains(Empty(), x); - false; - exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; - } - - case 0 <= n <= s.Length() => - var (prefix, suffix) := s.Split(n); - var t := s.Take(n); - assert t == prefix; - assert t.Length() == n by { - s.LengthTakeDrop(n); - } - assert L: forall i :: 0 <= i < Length(t) ==> Index(t, i) == Index(s, i) by { - forall i | 0 <= i < Length(t) - ensures Index(t, i) == Index(s, i) - { - prefix.AppendAt(suffix, i); - } - } - - calc { - Contains(t, x); - exists i :: 0 <= i < Length(t) && Index(t, i) == x; - { reveal L; } - exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; - } - - case s.Length() < n => - calc { - Contains(Take(s, n), x); - Contains(s, x); - exists i :: 0 <= i < Length(s) && Index(s, i) == x; - { assert Length(s) < n; } - exists i :: 0 <= i < n && i < Length(s) && Index(s, i) == x; - } - } - - // boogie: - // axiom (forall s: Seq, n: int, x: Box :: - // { Seq#Contains(Seq#Drop(s, n), x) } - // Seq#Contains(Seq#Drop(s, n), x) <==> - // (exists i: int :: { Seq#Index(s, i) } - // 0 <= n && n <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); - lemma {:extract_pattern Contains(Drop(s, n), x)} DropContains(s: Seq, n: int, x: Box) - ensures Contains(Drop(s, n), x) <==> - exists i: int {:extract_pattern Index(s, i)} :: 0 <= n <= i < Length(s) && Index(s, i) == x - { - if 0 <= n <= s.Length() { - var (prefix, suffix) := s.Split(n); - var t := s.Take(n); - assert t == prefix; - assert t.Length() == n by { - s.LengthTakeDrop(n); - } - assert L: forall i :: 0 <= i < Length(suffix) ==> Index(suffix, i) == Index(s, n + i) by { - forall i | 0 <= i < Length(suffix) { - calc { - Index(s, n + i); - { prefix.LengthAppend(suffix); } - s.At(n + i); - { prefix.AppendAt(suffix, n + i); } - suffix.At(i); - Index(suffix, i); - } - } - } - - if Contains(Drop(s, n), x) { - var j :| 0 <= j < Length(suffix) && Index(suffix, j) == x; - var i := n + j; - assert 0 <= n <= i < Length(s) by { - prefix.LengthAppend(suffix); - } - assert Index(s, i) == x by { - reveal L; - } - } - - if i :| 0 <= n <= i < Length(s) && Index(s, i) == x { - var j := i - n; - assert 0 <= j < Length(suffix) by { - prefix.LengthAppend(suffix); - } - assert Index(suffix, j) == x by { - reveal L; - } - } - - } else { - calc { - Contains(Drop(s, n), x); - Contains(Empty(), x); - false; - exists i :: 0 <= n <= i < Length(s) && Index(s, i) == x; - } - } - } - // boogie: // axiom (forall s, t: Seq, n: int :: // { Seq#Take(Seq#Append(s, t), n) } From 2436e655f16c5e868f3b569d2f9c3b28c04fe5e5 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 16:45:44 -0700 Subject: [PATCH 32/52] Split model into separate files --- Source/DafnyCore/Prelude/Boxes.dfy | 8 ++ Source/DafnyCore/Prelude/Lists.dfy | 112 ++++++++++++++++++++++ Source/DafnyCore/Prelude/Makefile | 4 +- Source/DafnyCore/Prelude/Sequences.dfy | 124 ------------------------- 4 files changed, 122 insertions(+), 126 deletions(-) create mode 100644 Source/DafnyCore/Prelude/Boxes.dfy create mode 100644 Source/DafnyCore/Prelude/Lists.dfy diff --git a/Source/DafnyCore/Prelude/Boxes.dfy b/Source/DafnyCore/Prelude/Boxes.dfy new file mode 100644 index 00000000000..660c8d71426 --- /dev/null +++ b/Source/DafnyCore/Prelude/Boxes.dfy @@ -0,0 +1,8 @@ +module Boxes { + export + provides Box, arbitrary + + type Box(==,0) + + const arbitrary: Box +} diff --git a/Source/DafnyCore/Prelude/Lists.dfy b/Source/DafnyCore/Prelude/Lists.dfy new file mode 100644 index 00000000000..e79b7900c19 --- /dev/null +++ b/Source/DafnyCore/Prelude/Lists.dfy @@ -0,0 +1,112 @@ +module Lists { + export + reveals List, List.Length, List.At, List.Append, List.Take, List.Drop, List.Split + provides List.LengthAppend, List.AppendAt, List.AboutDrop + provides List.AppendTake, List.TakeFromAppend, List.AppendDrop, List.DropFromAppend + provides List.AppendTakeDrop, List.LengthTakeDrop + + datatype List = Nil | Cons(head: X, tail: List) + { + function Length(): nat { + if Nil? then 0 else 1 + tail.Length() + } + + function Append(xs: List): List { + match this + case Nil => xs + case Cons(x, tail) => Cons(x, tail.Append(xs)) + } + + lemma LengthAppend(xs: List) + ensures Append(xs).Length() == Length() + xs.Length() + { + } + + function At(i: nat): X + requires i < Length() + { + if i == 0 then head else tail.At(i - 1) + } + + lemma AppendAt(xs: List, i: nat) + requires i < Append(xs).Length() + ensures Append(xs).At(i) == + if i < Length() then At(i) else (LengthAppend(xs); xs.At(i - Length())) + { + } + + function Take(n: nat): List + requires n <= Length() + { + if n == 0 then Nil else Cons(head, tail.Take(n - 1)) + } + + function Drop(n: nat): List + requires n <= Length() + { + if n == 0 then this else tail.Drop(n - 1) + } + + function Split(n: nat): (split: (List, List)) + requires n <= Length() + ensures split.0.Append(split.1) == this + { + AppendTakeDrop(n); + (Take(n), Drop(n)) + } + + lemma AboutDrop(n: nat) + requires n < Length() + ensures Drop(n).Cons? + { + } + + lemma AppendTake(xs: List) + ensures (LengthAppend(xs); Append(xs).Take(Length()) == this) + { + match this + case Nil => + case Cons(x, tail) => + LengthAppend(xs); + } + + lemma TakeFromAppend(xs: List, n: nat) + requires n <= Length() + xs.Length() + ensures (LengthAppend(xs); + Append(xs).Take(n) == + if n <= Length() then Take(n) else Append(xs.Take(n - Length()))) + { + LengthAppend(xs); + } + + lemma AppendDrop(xs: List) + ensures (LengthAppend(xs); Append(xs).Drop(Length()) == xs) + { + match this + case Nil => + case Cons(x, tail) => + LengthAppend(xs); + } + + lemma DropFromAppend(xs: List, n: nat) + requires n <= Length() + xs.Length() + ensures (LengthAppend(xs); + Append(xs).Drop(n) == + if n <= Length() then Drop(n).Append(xs) else xs.Drop(n - Length())) + { + LengthAppend(xs); + } + + lemma AppendTakeDrop(i: nat) + requires i <= Length() + ensures Take(i).Append(Drop(i)) == this + { + } + + lemma LengthTakeDrop(i: nat) + requires i <= Length() + ensures Take(i).Length() == i && Drop(i).Length() == Length() - i + { + } + } +} diff --git a/Source/DafnyCore/Prelude/Makefile b/Source/DafnyCore/Prelude/Makefile index d609705c768..d7439cea405 100644 --- a/Source/DafnyCore/Prelude/Makefile +++ b/Source/DafnyCore/Prelude/Makefile @@ -14,7 +14,7 @@ DafnyPrelude.bpl: Sequences.bpl sed -e "s|^//#|#|" -i "" PreludeCore.bpl DafnyPrelude.bpl sed -e "s|PRIME|'|g" -i "" PreludeCore.bpl Sequences.bpl DafnyPrelude.bpl -Sequences.bpl: Sequences.dfy - $(DAFNY) verify Sequences.dfy --extract:Sequences.bpl +Sequences.bpl: Lists.dfy Boxes.dfy Sequences.dfy + $(DAFNY) verify Lists.dfy Boxes.dfy Sequences.dfy --extract:Sequences.bpl # Remove trailing spaces that the Boogie pretty printer emits sed -e "s| *$$||" -i "" Sequences.bpl diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index f22a8396517..5e0ab85c973 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -1,127 +1,3 @@ -module Lists { - export - reveals List, List.Length, List.At, List.Append, List.Take, List.Drop, List.Split - provides List.LengthAppend, List.AppendAt, List.AboutDrop - provides List.AppendTake, List.TakeFromAppend, List.AppendDrop, List.DropFromAppend - provides List.AppendTakeDrop, List.LengthTakeDrop - - datatype List = Nil | Cons(head: X, tail: List) - { - function Length(): nat { - if Nil? then 0 else 1 + tail.Length() - } - - function Append(xs: List): List { - match this - case Nil => xs - case Cons(x, tail) => Cons(x, tail.Append(xs)) - } - - lemma LengthAppend(xs: List) - ensures Append(xs).Length() == Length() + xs.Length() - { - } - - function At(i: nat): X - requires i < Length() - { - if i == 0 then head else tail.At(i - 1) - } - - lemma AppendAt(xs: List, i: nat) - requires i < Append(xs).Length() - ensures Append(xs).At(i) == - if i < Length() then At(i) else (LengthAppend(xs); xs.At(i - Length())) - { - } - - function Take(n: nat): List - requires n <= Length() - { - if n == 0 then Nil else Cons(head, tail.Take(n - 1)) - } - - function Drop(n: nat): List - requires n <= Length() - { - if n == 0 then this else tail.Drop(n - 1) - } - - function Split(n: nat): (split: (List, List)) - requires n <= Length() - ensures split.0.Append(split.1) == this - { - AppendTakeDrop(n); - (Take(n), Drop(n)) - } - - lemma AboutDrop(n: nat) - requires n < Length() - ensures Drop(n).Cons? - { - } - - lemma AppendTake(xs: List) - ensures (LengthAppend(xs); Append(xs).Take(Length()) == this) - { - match this - case Nil => - case Cons(x, tail) => - LengthAppend(xs); - } - - lemma TakeFromAppend(xs: List, n: nat) - requires n <= Length() + xs.Length() - ensures (LengthAppend(xs); - Append(xs).Take(n) == - if n <= Length() then Take(n) else Append(xs.Take(n - Length()))) - { - LengthAppend(xs); - } - - lemma AppendDrop(xs: List) - ensures (LengthAppend(xs); Append(xs).Drop(Length()) == xs) - { - match this - case Nil => - case Cons(x, tail) => - LengthAppend(xs); - } - - lemma DropFromAppend(xs: List, n: nat) - requires n <= Length() + xs.Length() - ensures (LengthAppend(xs); - Append(xs).Drop(n) == - if n <= Length() then Drop(n).Append(xs) else xs.Drop(n - Length())) - { - LengthAppend(xs); - } - - lemma AppendTakeDrop(i: nat) - requires i <= Length() - ensures Take(i).Append(Drop(i)) == this - { - } - - lemma LengthTakeDrop(i: nat) - requires i <= Length() - ensures Take(i).Length() == i && Drop(i).Length() == Length() - i - { - } - } -} - -module Boxes { - export - provides Box, arbitrary - - type Box(==,0) - - const arbitrary: Box -} - -// -------------------------------------------------------------------------------------------------------------------- - module {:extract} Sequences { export provides Lists, Boxes From a1ff5eef84ecc42b28bd48e45f3f1f0d23cf44bc Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:05:14 -0700 Subject: [PATCH 33/52] Adjust line spacing --- Source/DafnyCore/DafnyPrelude.bpl | 37 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index e3b3e34fb97..3302868d33f 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1163,24 +1163,34 @@ axiom (forall h: Heap, i: int, v: Box, a: ref :: // Commutability of Take and Drop with Update. axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Take(Seq#Update(s, i, v), n) } - 0 <= i && i < n && n <= Seq#Length(s) ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v) ); + 0 <= i && i < n && n <= Seq#Length(s) + ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v) ); + axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Take(Seq#Update(s, i, v), n) } - n <= i && i < Seq#Length(s) ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); + n <= i && i < Seq#Length(s) + ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); + axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Drop(Seq#Update(s, i, v), n) } - 0 <= n && n <= i && i < Seq#Length(s) ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i-n, v) ); + 0 <= n && n <= i && i < Seq#Length(s) + ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i-n, v) ); + axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Drop(Seq#Update(s, i, v), n) } - 0 <= i && i < n && n <= Seq#Length(s) ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); + 0 <= i && i < n && n <= Seq#Length(s) + ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); + // Extension axiom, triggers only on Takes from arrays. axiom (forall h: Heap, a: ref, n0, n1: int :: { Seq#Take(Seq#FromArray(h, a), n0), Seq#Take(Seq#FromArray(h, a), n1) } n0 + 1 == n1 && 0 <= n0 && n1 <= _System.array.Length(a) ==> Seq#Take(Seq#FromArray(h, a), n1) == Seq#Build(Seq#Take(Seq#FromArray(h, a), n0), read(h, a, IndexField(n0): Field)) ); + // drop commutes with build. axiom (forall s: Seq, v: Box, n: int :: { Seq#Drop(Seq#Build(s, v), n) } - 0 <= n && n <= Seq#Length(s) ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v) ); + 0 <= n && n <= Seq#Length(s) + ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v) ); function Seq#Rank(Seq): int; axiom (forall s: Seq, i: int :: @@ -1197,13 +1207,16 @@ axiom (forall s: Seq, i: int, j: int :: 0 <= i && i < j && j <= Seq#Length(s) ==> Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) < Seq#Rank(s) ); // Additional axioms about common things -axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } - n == 0 ==> Seq#Drop(s, n) == s); -axiom (forall s: Seq, n: int :: { Seq#Take(s, n) } - n == 0 ==> Seq#Take(s, n) == Seq#Empty()); -axiom (forall s: Seq, m, n: int :: { Seq#Drop(Seq#Drop(s, m), n) } - 0 <= m && 0 <= n && m+n <= Seq#Length(s) ==> - Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m+n)); +axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } n == 0 ==> Seq#Drop(s, n) == s); + +axiom (forall s: Seq, n: int :: + { Seq#Take(s, n) } + n == 0 ==> Seq#Take(s, n) == Seq#Empty()); + +axiom (forall s: Seq, m, n: int :: + { Seq#Drop(Seq#Drop(s, m), n) } + 0 <= m && 0 <= n && m+n <= Seq#Length(s) + ==> Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m + n)); // --------------------------------------------------------------- // -- Axiomatization of Maps ------------------------------------- From 2a885135c7cdf4c7836a7aac417e190557933dc7 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:05:42 -0700 Subject: [PATCH 34/52] Add missing pattern --- Source/DafnyCore/Prelude/Sequences.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index 5e0ab85c973..51d0d8d7f44 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -582,7 +582,7 @@ module {:extract} Sequences { // boogie: // axiom (forall s: Seq, n: int :: { Seq#Length(Seq#Take(s,n)) } // 0 <= n && n <= Seq#Length(s) ==> Seq#Length(Seq#Take(s,n)) == n); - lemma LengthTake(s: Seq, n: int) + lemma {:extract_pattern Length(Take(s, n))} LengthTake(s: Seq, n: int) requires 0 <= n <= Length(s) ensures Length(Take(s, n)) == n { From e781392f7adab8e9dca116f61a1ad97055653f65 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:05:53 -0700 Subject: [PATCH 35/52] Move declarations --- Source/DafnyCore/Prelude/Sequences.dfy | 67 +++++++++++++------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index 51d0d8d7f44..c26af5d0eff 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -673,39 +673,6 @@ module {:extract} Sequences { s.AppendDrop(t); } - // Additional axioms about common things - - // boogie: - // axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } - // n == 0 ==> Seq#Drop(s, n) == s); - lemma {:extract_pattern Drop(s, n)} DropNothing(s: Seq, n: int) - requires n == 0 - ensures Drop(s, n) == s - { - } - - // boogie: - // axiom (forall s: Seq, n: int :: { Seq#Take(s, n) } - // n == 0 ==> Seq#Take(s, n) == Seq#Empty()); - lemma {: extract_pattern Take(s, n)} TakeNothing(s: Seq, n: int) - requires n == 0 - ensures Take(s, n) == Empty() - { - } - - // boogie: - // axiom (forall s: Seq, m, n: int :: { Seq#Drop(Seq#Drop(s, m), n) } - // 0 <= m && 0 <= n && m+n <= Seq#Length(s) ==> - // Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m+n)); - lemma {:extract_pattern Drop(Drop(s, m), n)} DropDrop(s: Seq, m: int, n: int) - requires 0 <= m && 0 <= n && m + n <= Length(s) - ensures Drop(Drop(s, m), n) == Drop(s, m + n) - { - if m != 0 { - DropDrop(s.tail, m - 1, n); - } - } - // Commutability of Take and Drop with Update. // boogie: @@ -782,4 +749,38 @@ module {:extract} Sequences { } } } + + // Additional axioms about common things + + // boogie: + // axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } + // n == 0 ==> Seq#Drop(s, n) == s); + lemma {:extract_pattern Drop(s, n)} DropNothing(s: Seq, n: int) + requires n == 0 + ensures Drop(s, n) == s + { + } + + // boogie: + // axiom (forall s: Seq, n: int :: { Seq#Take(s, n) } + // n == 0 ==> Seq#Take(s, n) == Seq#Empty()); + lemma {: extract_pattern Take(s, n)} TakeNothing(s: Seq, n: int) + requires n == 0 + ensures Take(s, n) == Empty() + { + } + + // boogie: + // axiom (forall s: Seq, m, n: int :: { Seq#Drop(Seq#Drop(s, m), n) } + // 0 <= m && 0 <= n && m+n <= Seq#Length(s) ==> + // Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m+n)); + lemma {:extract_pattern Drop(Drop(s, m), n)} DropDrop(s: Seq, m: int, n: int) + requires 0 <= m && 0 <= n && m + n <= Length(s) + ensures Drop(Drop(s, m), n) == Drop(s, m + n) + { + if m != 0 { + DropDrop(s.tail, m - 1, n); + } + } + } From 575fbb627e6e718ff17814c14d22a5780715dc48 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:13:11 -0700 Subject: [PATCH 36/52] Adjust whitespace --- Source/DafnyCore/DafnyPrelude.bpl | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 3302868d33f..9960449504e 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1162,24 +1162,24 @@ axiom (forall h: Heap, i: int, v: Box, a: ref :: // Commutability of Take and Drop with Update. axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Take(Seq#Update(s, i, v), n) } - 0 <= i && i < n && n <= Seq#Length(s) - ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v) ); + { Seq#Take(Seq#Update(s, i, v), n) } + 0 <= i && i < n && n <= Seq#Length(s) + ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Update(Seq#Take(s, n), i, v)); axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Take(Seq#Update(s, i, v), n) } - n <= i && i < Seq#Length(s) - ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); + { Seq#Take(Seq#Update(s, i, v), n) } + n <= i && i < Seq#Length(s) + ==> Seq#Take(Seq#Update(s, i, v), n) == Seq#Take(s, n)); axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Drop(Seq#Update(s, i, v), n) } - 0 <= n && n <= i && i < Seq#Length(s) - ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i-n, v) ); + { Seq#Drop(Seq#Update(s, i, v), n) } + 0 <= n && n <= i && i < Seq#Length(s) + ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Update(Seq#Drop(s, n), i - n, v)); axiom (forall s: Seq, i: int, v: Box, n: int :: - { Seq#Drop(Seq#Update(s, i, v), n) } - 0 <= i && i < n && n <= Seq#Length(s) - ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); + { Seq#Drop(Seq#Update(s, i, v), n) } + 0 <= i && i < n && n <= Seq#Length(s) + ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); // Extension axiom, triggers only on Takes from arrays. axiom (forall h: Heap, a: ref, n0, n1: int :: @@ -1188,9 +1188,9 @@ axiom (forall h: Heap, a: ref, n0, n1: int :: // drop commutes with build. axiom (forall s: Seq, v: Box, n: int :: - { Seq#Drop(Seq#Build(s, v), n) } - 0 <= n && n <= Seq#Length(s) - ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v) ); + { Seq#Drop(Seq#Build(s, v), n) } + 0 <= n && n <= Seq#Length(s) + ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v)); function Seq#Rank(Seq): int; axiom (forall s: Seq, i: int :: @@ -1213,9 +1213,9 @@ axiom (forall s: Seq, n: int :: { Seq#Take(s, n) } n == 0 ==> Seq#Take(s, n) == Seq#Empty()); -axiom (forall s: Seq, m, n: int :: +axiom (forall s: Seq, m: int, n: int :: { Seq#Drop(Seq#Drop(s, m), n) } - 0 <= m && 0 <= n && m+n <= Seq#Length(s) + 0 <= m && 0 <= n && m + n <= Seq#Length(s) ==> Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m + n)); // --------------------------------------------------------------- From 41b14b25726deb28cf65210fd4a66d51f916c71b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:13:51 -0700 Subject: [PATCH 37/52] Move declarations --- Source/DafnyCore/DafnyPrelude.bpl | 79 ++++++++++++++++--------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 9960449504e..46317787700 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -1004,20 +1004,6 @@ axiom (forall s: Seq, i: int, v: Box :: (i == Seq#Length(s) ==> Seq#Index(Seq#Build(s, v), i) == v) && (i != Seq#Length(s) ==> Seq#Index(Seq#Build(s, v), i) == Seq#Index(s, i))); -// Build preserves $Is -axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s, bx), TSeq(t)) } - $Is(s, TSeq(t)) && $IsBox(bx, t) ==> $Is(Seq#Build(s, bx), TSeq(t))); - -function Seq#Create(ty: Ty, heap: Heap, len: int, init: HandleType): Seq; -axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType :: - { Seq#Length(Seq#Create(ty, heap, len, init): Seq) } - $IsGoodHeap(heap) && 0 <= len ==> - Seq#Length(Seq#Create(ty, heap, len, init): Seq) == len); -axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: - { Seq#Index(Seq#Create(ty, heap, len, init), i) } - $IsGoodHeap(heap) && 0 <= i && i < len ==> - Seq#Index(Seq#Create(ty, heap, len, init), i) == Apply1(TInt, ty, heap, init, $Box(i))); - axiom (forall s0: Seq, s1: Seq :: { Seq#Length(Seq#Append(s0, s1)) } Seq#Length(Seq#Append(s0, s1)) == Seq#Length(s0) + Seq#Length(s1)); @@ -1135,31 +1121,6 @@ axiom (forall s: Seq, t: Seq, n: int :: n == Seq#Length(s) ==> Seq#Take(Seq#Append(s, t), n) == s && Seq#Drop(Seq#Append(s, t), n) == t); -function Seq#FromArray(h: Heap, a: ref): Seq; -axiom (forall h: Heap, a: ref :: - { Seq#Length(Seq#FromArray(h,a)) } - Seq#Length(Seq#FromArray(h, a)) == _System.array.Length(a)); -axiom (forall h: Heap, a: ref :: - { Seq#FromArray(h, a) } - (forall i: int :: - // it's important to include both triggers, so that assertions about the - // the relation between the array and the sequence can be proved in either - // direction - { read(h, a, IndexField(i)) } - { Seq#Index(Seq#FromArray(h, a): Seq, i) } - 0 <= i && - i < Seq#Length(Seq#FromArray(h, a)) // this will trigger the previous axiom to get a connection with _System.array.Length(a) - ==> - Seq#Index(Seq#FromArray(h, a), i) == read(h, a, IndexField(i)))); -axiom (forall h0, h1: Heap, a: ref :: - { Seq#FromArray(h1, a), $HeapSucc(h0, h1) } - $IsGoodHeap(h0) && $IsGoodHeap(h1) && $HeapSucc(h0, h1) && h0[a] == h1[a] - ==> - Seq#FromArray(h0, a) == Seq#FromArray(h1, a)); -axiom (forall h: Heap, i: int, v: Box, a: ref :: - { Seq#FromArray(update(h, a, IndexField(i), v), a) } - 0 <= i && i < _System.array.Length(a) ==> Seq#FromArray(update(h, a, IndexField(i), v), a) == Seq#Update(Seq#FromArray(h, a), i, v) ); - // Commutability of Take and Drop with Update. axiom (forall s: Seq, i: int, v: Box, n: int :: { Seq#Take(Seq#Update(s, i, v), n) } @@ -1218,6 +1179,46 @@ axiom (forall s: Seq, m: int, n: int :: 0 <= m && 0 <= n && m + n <= Seq#Length(s) ==> Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m + n)); +// Build preserves $Is +axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s, bx), TSeq(t)) } + $Is(s, TSeq(t)) && $IsBox(bx, t) ==> $Is(Seq#Build(s, bx), TSeq(t))); + +function Seq#Create(ty: Ty, heap: Heap, len: int, init: HandleType): Seq; +axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType :: + { Seq#Length(Seq#Create(ty, heap, len, init): Seq) } + $IsGoodHeap(heap) && 0 <= len ==> + Seq#Length(Seq#Create(ty, heap, len, init): Seq) == len); +axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType, i: int :: + { Seq#Index(Seq#Create(ty, heap, len, init), i) } + $IsGoodHeap(heap) && 0 <= i && i < len ==> + Seq#Index(Seq#Create(ty, heap, len, init), i) == Apply1(TInt, ty, heap, init, $Box(i))); + +function Seq#FromArray(h: Heap, a: ref): Seq; +axiom (forall h: Heap, a: ref :: + { Seq#Length(Seq#FromArray(h,a)) } + Seq#Length(Seq#FromArray(h, a)) == _System.array.Length(a)); +axiom (forall h: Heap, a: ref :: + { Seq#FromArray(h, a) } + (forall i: int :: + // it's important to include both triggers, so that assertions about the + // the relation between the array and the sequence can be proved in either + // direction + { read(h, a, IndexField(i)) } + { Seq#Index(Seq#FromArray(h, a): Seq, i) } + 0 <= i && + i < Seq#Length(Seq#FromArray(h, a)) // this will trigger the previous axiom to get a connection with _System.array.Length(a) + ==> + Seq#Index(Seq#FromArray(h, a), i) == read(h, a, IndexField(i)))); +axiom (forall h0, h1: Heap, a: ref :: + { Seq#FromArray(h1, a), $HeapSucc(h0, h1) } + $IsGoodHeap(h0) && $IsGoodHeap(h1) && $HeapSucc(h0, h1) && h0[a] == h1[a] + ==> + Seq#FromArray(h0, a) == Seq#FromArray(h1, a)); +axiom (forall h: Heap, i: int, v: Box, a: ref :: + { Seq#FromArray(update(h, a, IndexField(i), v), a) } + 0 <= i && i < _System.array.Length(a) ==> Seq#FromArray(update(h, a, IndexField(i), v), a) == Seq#Update(Seq#FromArray(h, a), i, v) ); + + // --------------------------------------------------------------- // -- Axiomatization of Maps ------------------------------------- // --------------------------------------------------------------- From 873ed664e0b57a06ce04eae6667f612694f3000e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:24:46 -0700 Subject: [PATCH 38/52] Move declarations --- Source/DafnyCore/DafnyPrelude.bpl | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Source/DafnyCore/DafnyPrelude.bpl b/Source/DafnyCore/DafnyPrelude.bpl index 46317787700..12857ef3ff5 100644 --- a/Source/DafnyCore/DafnyPrelude.bpl +++ b/Source/DafnyCore/DafnyPrelude.bpl @@ -980,10 +980,6 @@ axiom (forall s: Seq :: { Seq#Length(s) } Seq#Length(s) == 0 ==> s == Seq#Empty( // (Seq#Length(s) != 0 ==> (exists x: Box :: Seq#Contains(s, x))) // ); - -// The empty sequence $Is any type -//axiom (forall t: Ty :: {$Is(Seq#Empty(): Seq, TSeq(t))} $Is(Seq#Empty(): Seq, TSeq(t))); - function Seq#Build(s: Seq, val: Box) : Seq; function Seq#Build_inv0(s: Seq) : Seq; @@ -1142,31 +1138,12 @@ axiom (forall s: Seq, i: int, v: Box, n: int :: 0 <= i && i < n && n <= Seq#Length(s) ==> Seq#Drop(Seq#Update(s, i, v), n) == Seq#Drop(s, n)); -// Extension axiom, triggers only on Takes from arrays. -axiom (forall h: Heap, a: ref, n0, n1: int :: - { Seq#Take(Seq#FromArray(h, a), n0), Seq#Take(Seq#FromArray(h, a), n1) } - n0 + 1 == n1 && 0 <= n0 && n1 <= _System.array.Length(a) ==> Seq#Take(Seq#FromArray(h, a), n1) == Seq#Build(Seq#Take(Seq#FromArray(h, a), n0), read(h, a, IndexField(n0): Field)) ); - // drop commutes with build. axiom (forall s: Seq, v: Box, n: int :: { Seq#Drop(Seq#Build(s, v), n) } 0 <= n && n <= Seq#Length(s) ==> Seq#Drop(Seq#Build(s, v), n) == Seq#Build(Seq#Drop(s, n), v)); -function Seq#Rank(Seq): int; -axiom (forall s: Seq, i: int :: - { DtRank($Unbox(Seq#Index(s, i)): DatatypeType) } - 0 <= i && i < Seq#Length(s) ==> DtRank($Unbox(Seq#Index(s, i)): DatatypeType) < Seq#Rank(s) ); -axiom (forall s: Seq, i: int :: - { Seq#Rank(Seq#Drop(s, i)) } - 0 < i && i <= Seq#Length(s) ==> Seq#Rank(Seq#Drop(s, i)) < Seq#Rank(s) ); -axiom (forall s: Seq, i: int :: - { Seq#Rank(Seq#Take(s, i)) } - 0 <= i && i < Seq#Length(s) ==> Seq#Rank(Seq#Take(s, i)) < Seq#Rank(s) ); -axiom (forall s: Seq, i: int, j: int :: - { Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) } - 0 <= i && i < j && j <= Seq#Length(s) ==> Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) < Seq#Rank(s) ); - // Additional axioms about common things axiom (forall s: Seq, n: int :: { Seq#Drop(s, n) } n == 0 ==> Seq#Drop(s, n) == s); @@ -1179,6 +1156,10 @@ axiom (forall s: Seq, m: int, n: int :: 0 <= m && 0 <= n && m + n <= Seq#Length(s) ==> Seq#Drop(Seq#Drop(s, m), n) == Seq#Drop(s, m + n)); + +// The empty sequence $Is any type +//axiom (forall t: Ty :: {$Is(Seq#Empty(): Seq, TSeq(t))} $Is(Seq#Empty(): Seq, TSeq(t))); + // Build preserves $Is axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s, bx), TSeq(t)) } $Is(s, TSeq(t)) && $IsBox(bx, t) ==> $Is(Seq#Build(s, bx), TSeq(t))); @@ -1218,6 +1199,24 @@ axiom (forall h: Heap, i: int, v: Box, a: ref :: { Seq#FromArray(update(h, a, IndexField(i), v), a) } 0 <= i && i < _System.array.Length(a) ==> Seq#FromArray(update(h, a, IndexField(i), v), a) == Seq#Update(Seq#FromArray(h, a), i, v) ); +// Extension axiom, triggers only on Takes from arrays. +axiom (forall h: Heap, a: ref, n0, n1: int :: + { Seq#Take(Seq#FromArray(h, a), n0), Seq#Take(Seq#FromArray(h, a), n1) } + n0 + 1 == n1 && 0 <= n0 && n1 <= _System.array.Length(a) ==> Seq#Take(Seq#FromArray(h, a), n1) == Seq#Build(Seq#Take(Seq#FromArray(h, a), n0), read(h, a, IndexField(n0): Field)) ); + +function Seq#Rank(Seq): int; +axiom (forall s: Seq, i: int :: + { DtRank($Unbox(Seq#Index(s, i)): DatatypeType) } + 0 <= i && i < Seq#Length(s) ==> DtRank($Unbox(Seq#Index(s, i)): DatatypeType) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int :: + { Seq#Rank(Seq#Drop(s, i)) } + 0 < i && i <= Seq#Length(s) ==> Seq#Rank(Seq#Drop(s, i)) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int :: + { Seq#Rank(Seq#Take(s, i)) } + 0 <= i && i < Seq#Length(s) ==> Seq#Rank(Seq#Take(s, i)) < Seq#Rank(s) ); +axiom (forall s: Seq, i: int, j: int :: + { Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) } + 0 <= i && i < j && j <= Seq#Length(s) ==> Seq#Rank(Seq#Append(Seq#Take(s, i), Seq#Drop(s, j))) < Seq#Rank(s) ); // --------------------------------------------------------------- // -- Axiomatization of Maps ------------------------------------- @@ -1547,3 +1546,4 @@ axiom (forall x, y, z: int :: #endif // ------------------------------------------------------------------------- + From eea48b6d1c4054e643a31c331a442b0be893cd35 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:24:58 -0700 Subject: [PATCH 39/52] Add missing dependency --- Source/DafnyCore/Prelude/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Prelude/Makefile b/Source/DafnyCore/Prelude/Makefile index d7439cea405..94e8c4b8341 100644 --- a/Source/DafnyCore/Prelude/Makefile +++ b/Source/DafnyCore/Prelude/Makefile @@ -2,7 +2,7 @@ DAFNY=../../../Scripts/dafny all: DafnyPrelude.bpl -DafnyPrelude.bpl: Sequences.bpl +DafnyPrelude.bpl: PreludeCore.bpl Sequences.bpl # cpp is allergic to primes, so we have to do a song and dance around it sed -e "s|'|PRIME|g" -i "" PreludeCore.bpl Sequences.bpl # also, we need to disable preprocessing of Boogie things From 9fd74ad93bf9513ad50d92ef8d231efdf29b8208 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:26:49 -0700 Subject: [PATCH 40/52] Adjust template file --- Source/DafnyCore/Prelude/PreludeCore.bpl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Source/DafnyCore/Prelude/PreludeCore.bpl b/Source/DafnyCore/Prelude/PreludeCore.bpl index c22570ae032..b40823328f9 100644 --- a/Source/DafnyCore/Prelude/PreludeCore.bpl +++ b/Source/DafnyCore/Prelude/PreludeCore.bpl @@ -960,15 +960,12 @@ axiom (forall s: Seq, x: Box :: { MultiSet#FromSeq(s)[x] } #include "Sequences.bpl" -axiom (forall v: Seq, t0: Ty :: { $Is(v, TSeq(t0)) } - $Is(v, TSeq(t0)) <==> - (forall i : int :: { Seq#Index(v, i) } - 0 <= i && i < Seq#Length(v) ==> - $IsBox(Seq#Index(v, i), t0))); +// The empty sequence $Is any type +//axiom (forall t: Ty :: {$Is(Seq#Empty(): Seq, TSeq(t))} $Is(Seq#Empty(): Seq, TSeq(t))); // Build preserves $Is -axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s,bx),TSeq(t)) } - $Is(s,TSeq(t)) && $IsBox(bx,t) ==> $Is(Seq#Build(s,bx),TSeq(t))); +axiom (forall s: Seq, bx: Box, t: Ty :: { $Is(Seq#Build(s, bx), TSeq(t)) } + $Is(s, TSeq(t)) && $IsBox(bx, t) ==> $Is(Seq#Build(s, bx), TSeq(t))); function Seq#Create(ty: Ty, heap: Heap, len: int, init: HandleType): Seq; axiom (forall ty: Ty, heap: Heap, len: int, init: HandleType :: From eb49dd5065ed79d4902c0f2523b39b4d1655519b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 17:32:36 -0700 Subject: [PATCH 41/52] Add Boogie comments into model file --- Source/DafnyCore/Prelude/Sequences.dfy | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index c26af5d0eff..14ebc3bf012 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -50,6 +50,14 @@ module {:extract} Sequences { { } + // boogie: + // The following would be a nice fact to include, because it would enable verifying the + // GenericPick.SeqPick* methods in Test/dafny0/SmallTests.dfy. However, it substantially + // slows down performance on some other tests, including running seemingly forever on + // some. + // axiom (forall s: Seq :: { Seq#Length(s) } + // Seq#Length(s) != 0 ==> (exists x: Box :: Seq#Contains(s, x))); + // boogie: function Seq#Build(s: Seq, val: Box): Seq; function {:extract_name "Seq#Build"} Build(s: Seq, val: Box): Seq { s.Append(Cons(val, Nil)) @@ -361,8 +369,9 @@ module {:extract} Sequences { } } + // needed to prove things like '4 in [2,3,4]', see method TestSequences0 in SmallTests.dfy // boogie: - // axiom (forall s: Seq, v: Box, x: Box :: // needed to prove things like '4 in [2,3,4]', see method TestSequences0 in SmallTests.dfy + // axiom (forall s: Seq, v: Box, x: Box :: // { Seq#Contains(Seq#Build(s, v), x) } // Seq#Contains(Seq#Build(s, v), x) <==> (v == x || Seq#Contains(s, x))); lemma {:extract_pattern Contains(Build(s, v), x)} BuildContains(s: Seq, v: Box, x: Box) @@ -543,8 +552,9 @@ module {:extract} Sequences { } } + // extensionality axiom for sequences // boogie: - // axiom (forall a: Seq, b: Seq :: { Seq#Equal(a,b) } // extensionality axiom for sequences + // axiom (forall a: Seq, b: Seq :: { Seq#Equal(a,b) } // Seq#Equal(a,b) ==> a == b); lemma {:extract_pattern Equal(a, b)} Extensionality(a: Seq, b: Seq) requires Equal(a, b) @@ -673,7 +683,7 @@ module {:extract} Sequences { s.AppendDrop(t); } - // Commutability of Take and Drop with Update. + // Commutativity of Take and Drop with Update. // boogie: // axiom (forall s: Seq, i: int, v: Box, n: int :: @@ -728,7 +738,7 @@ module {:extract} Sequences { } } - // drop commutes with build. + // Drop commutes with build // boogie: // axiom (forall s: Seq, v: Box, n: int :: From ecd4fdc7e9d1b7fb70f130f5607ca7bc6cbb3f11 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 19:36:56 -0700 Subject: [PATCH 42/52] Add error handling --- Source/DafnyCore/Backends/Extractor.cs | 88 +++++++++++++------- Source/DafnyDriver/Commands/VerifyCommand.cs | 11 ++- 2 files changed, 67 insertions(+), 32 deletions(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index 13c12e68991..0d0db42ccf8 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -7,6 +7,7 @@ #nullable enable +using System; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -15,7 +16,16 @@ using Microsoft.Boogie; namespace Microsoft.Dafny.Compilers { + public class ExtractorError : Exception { + public ExtractorError(string message) + : base(message) { + } + } + public class Extractor : ASTVisitor { + /// + /// Throws an "ExtractorError" if the input is unexpected or unsupported. + /// public static Boogie.Program Extract(Program program) { var extractor = new Extractor(); extractor.VisitModule(program.DefaultModule); @@ -36,7 +46,9 @@ private Extractor() { void FixUpUsedByInformation() { foreach (var (axiom, function) in axiomUsedBy) { - var boogieFunction = functionExtractions[function]; // TODO: do error checking + if (!functionExtractions.TryGetValue(function, out var boogieFunction)) { + throw new ExtractorError($":extract_used_by attribute mentions non-extracted function: {function.Name}"); + } boogieFunction.OtherDefinitionAxioms.Add(axiom); } axiomUsedBy.Clear(); @@ -83,10 +95,16 @@ public override void VisitMethod(Method method) { if (patterns == null && usedByInfo == null) { return; } - Contract.Assert((lemma.Ins.Count == 0) == (patterns == null)); // a parameterized lemma must have patterns // TODO: fail more gently - Contract.Assert(lemma.TypeArgs.Count == 0); // TODO: fail more gently - Contract.Assert(lemma.Outs.Count == 0); // TODO: fail more gently + if ((lemma.Ins.Count == 0) != (patterns == null)) { + throw new ExtractorError($"a parameterized lemma must specify at least one :extract_pattern: {lemma.Name}"); + } + if (lemma.TypeArgs.Count != 0) { + throw new ExtractorError($"an extracted lemma is not allowed to have type parameters: {lemma.Name}"); + } + if (lemma.Outs.Count != 0) { + throw new ExtractorError($"an extracted lemma is not allowed to have out-parameters: {lemma.Name}"); + } var tok = lemma.tok; @@ -111,10 +129,11 @@ public override void VisitMethod(Method method) { declarations.Add(axiom); if (usedByInfo != null) { - Contract.Assert(usedByInfo.Args.Count == 1); - var argument = (MemberSelectExpr)usedByInfo.Args[0].Resolved; // TODO: do error checking - var function = (Function)argument.Member; // TODO: do error checking - axiomUsedBy.Add((axiom, function)); + if (usedByInfo.Args.Count == 1 && usedByInfo.Args[0].Resolved is MemberSelectExpr { Member: Function function }) { + axiomUsedBy.Add((axiom, function)); + } else { + throw new ExtractorError($":extract_used_by argument on lemma '{lemma.Name}' is expected to be an extracted function"); + } } } @@ -136,18 +155,22 @@ public override void VisitMethod(Method method) { Boogie.QKeyValue kv = null; var extractAttributes = Attributes.FindAllExpressions(attributes, "extract_attribute"); if (extractAttributes != null) { + if (extractAttributes.Count == 0) { + throw new ExtractorError($"first argument to :extract_attribute is expected to be a literal string; got no arguments"); + } for (var i = extractAttributes.Count; 0 <= --i;) { string? attrName = null; var parameters = new List(); foreach (var argument in extractAttributes[i]) { - if (attrName == null) { - attrName = (string)((StringLiteralExpr)argument).Value; // TODO: do error checking - } else { + if (attrName != null) { parameters.Add(ExtractExpr(argument)); + } else if (argument is StringLiteralExpr { Value: string name }) { + attrName = name; + } else { + throw new ExtractorError($"first argument to :extract_attribute is expected to be a literal string; got: {argument}"); } } - Contract.Assert(attrName != null); // TODO: fail more gently kv = new Boogie.QKeyValue(tok, attrName, parameters, kv); } } @@ -158,7 +181,9 @@ public override void VisitMethod(Method method) { public override void VisitFunction(Function function) { if (GetExtractName(function.Attributes) is { } extractName) { var tok = function.tok; - Contract.Assert(function.TypeArgs.Count == 0); // TODO: throw an exception or something more gentle + if (function.TypeArgs.Count != 0) { + throw new ExtractorError($"an extracted function is not allowed to have type parameters: {function.Name}"); + } var inParams = function.Ins.ConvertAll(formal => (Boogie.Variable)new Boogie.Formal(tok, new TypedIdent(tok, formal.Name, ExtractType(formal.Type)), true) ); @@ -170,17 +195,18 @@ public override void VisitFunction(Function function) { } private Boogie.Type ExtractType(Type type) { - if (type is IntType) { - return Boogie.Type.Int; - } else if (type is BoolType) { - return Boogie.Type.Bool; - } else if (type is UserDefinedType udt) { - var cl = udt.ResolvedClass; - var name = GetExtractName(cl.Attributes) ?? cl.Name; - return new Boogie.UnresolvedTypeIdentifier(Boogie.Token.NoToken, name, udt.TypeArgs.ConvertAll(ExtractType)); - } else { - Contract.Assert(false); // TODO: fail more gently - return null; // to please compiler + switch (type) { + case IntType: + return Boogie.Type.Int; + case BoolType: + return Boogie.Type.Bool; + case UserDefinedType udt: { + var cl = udt.ResolvedClass; + var name = GetExtractName(cl.Attributes) ?? cl.Name; + return new Boogie.UnresolvedTypeIdentifier(Boogie.Token.NoToken, name, udt.TypeArgs.ConvertAll(ExtractType)); + } + default: + throw new ExtractorError($"type not supported by extractor: {type}"); } } @@ -248,10 +274,12 @@ private Boogie.Expr ExtractExpr(Expression expr) { break; } - case UnaryOpExpr unaryOpExpr: { - Contract.Assert(unaryOpExpr.ResolvedOp == UnaryOpExpr.ResolvedOpcode.BoolNot); // TODO: fail more gently + case UnaryOpExpr unaryOpExpr: + if (unaryOpExpr.ResolvedOp == UnaryOpExpr.ResolvedOpcode.BoolNot) { var e = ExtractExpr(unaryOpExpr.E); return Boogie.Expr.Not(e); + } else { + throw new ExtractorError($"extractor does not support unary operator {unaryOpExpr.ResolvedOp}"); } case QuantifierExpr quantifierExpr: { @@ -260,7 +288,9 @@ private Boogie.Expr ExtractExpr(Expression expr) { ); var patterns = Attributes.FindAllExpressions(quantifierExpr.Attributes, "extract_pattern"); - Contract.Assert(patterns.Count != 0); // don't support pattern-less quantifiers // TODO: fail more gracefully + if (patterns.Count == 0) { + throw new ExtractorError("extraction expects every quantifier to specify at least one :extract_pattern"); + } var triggers = GetTriggers(tok, patterns); var kv = GetKeyValues(tok, quantifierExpr.Attributes); @@ -276,8 +306,8 @@ private Boogie.Expr ExtractExpr(Expression expr) { default: break; } - Contract.Assert(false, $"ExtractExpr TODO: {expr.GetType()}: {expr}"); // TODO: fail more gently - return Boogie.Expr.True; + + throw new ExtractorError($"extraction does not support expression of type {expr.GetType()}: {expr}"); } } } diff --git a/Source/DafnyDriver/Commands/VerifyCommand.cs b/Source/DafnyDriver/Commands/VerifyCommand.cs index 3ced4b5e23a..e8c52702988 100644 --- a/Source/DafnyDriver/Commands/VerifyCommand.cs +++ b/Source/DafnyDriver/Commands/VerifyCommand.cs @@ -85,10 +85,15 @@ public static async Task HandleVerification(DafnyOptions options) { await verificationResultsLogged; await proofDependenciesReported; - if (options.BoogieExtractionTargetFile != null) { + if (!resolution.HasErrors && options.BoogieExtractionTargetFile != null) { using (var engine = ExecutionEngine.CreateWithoutSharedCache(options)) { - var extractedProgram = Extractor.Extract(resolution.ResolvedProgram); - engine.PrintBplFile(options.BoogieExtractionTargetFile, extractedProgram, true, pretty: true); + try { + var extractedProgram = Extractor.Extract(resolution.ResolvedProgram); + engine.PrintBplFile(options.BoogieExtractionTargetFile, extractedProgram, true, pretty: true); + } catch (ExtractorError extractorError) { + options.OutputWriter.WriteLine($"extraction error: {extractorError.Message}"); + return 1; + } } } } From c16a10f64fc647bff2a5ff91674d8d15e67c40de Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 20:32:17 -0700 Subject: [PATCH 43/52] Enable some simplifications --- Source/DafnyCore/Backends/Extractor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/Extractor.cs index 0d0db42ccf8..5a17e9e49ce 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/Extractor.cs @@ -225,7 +225,9 @@ private Boogie.Expr ExtractExpr(Expression expr) { switch (expr) { case LiteralExpr literalExpr: { if (literalExpr.Value is bool boolValue) { - return new Boogie.LiteralExpr(tok, boolValue); + // use the specific literals, rather than Boogie.LiteralExpr(bool), in order for the + // peephole optimizations to kick in + return boolValue ? Boogie.Expr.True : Boogie.Expr.False; } else if (literalExpr.Value is BigInteger intValue) { var n = BigNum.FromBigInt(intValue); return Boogie.Expr.Literal(n); From 797e4c9bac7ec848fd5656a591e9f2c3a99b0e6f Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 11 Jul 2024 20:38:00 -0700 Subject: [PATCH 44/52] Add release notes --- docs/dev/news/5621.feat | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/dev/news/5621.feat diff --git a/docs/dev/news/5621.feat b/docs/dev/news/5621.feat new file mode 100644 index 00000000000..0c7705f9a5e --- /dev/null +++ b/docs/dev/news/5621.feat @@ -0,0 +1,41 @@ +New feature: model extractor + +### CLI option + +The `dafny verify` command now has an option `--extract:`, where (just like for the various print options) `` is allowed to be `-` to denote standard output. + +### Extract mechanism + +Upon successful verification, the new extract mechanism visits the AST of the given program. For any module marked with `{:extract}`, the extract-worthy material from the module is output. The output declarations will be in the same order as they appear textually in the module (in particular, the fact that module-level Dafny declarations are collected in an internal class `_default` has no bearing on the output order). + +Three kinds of declarations are extract-worthy: + +* A type declaration `A` that bears an attribute `{:extract_name B}` is extracted into a Boogie type declaration `type B _ _ _;`. + + The definition of the type is ignored. (The intended usage for an extracted type is that the Dafny program give a definition for the type, which goes to show the existence of such a type.) + +* A function declaration `F(x: X, y: Y): Z` that bears an attribute `{:extract_name G}` is extracted into a Boogie function declaration `function G(x: X, y: Y): Z;`. + + The body of the Dafny function is ignored. (The intended usage for an extracted function is that the Dafny program give a definition for the function, which goes to show the existence of such a function.) + +* A lemma declaration `L(x: X, y: Y) requires P ensures Q` that bears an attribute `{:extract_pattern ...}` or an attribute `{:extract_used_by ...}` is extracted into a Boogie `axiom`. The axiom has the basic form `axiom (forall x: X, y: Y :: P ==> Q);`. + + If the lemma has an attribute `{:extract_used_by F}`, then the axiom will be emitted into the `uses` clause of the Boogie function generated for Dafny function `F`. + + If the lemma has no in-parameters, the axiom is just `P ==> Q`. + + If the lemma has in-parameters, then any attribute `{:extract_pattern E, F, G}` adds a matching pattern `{ E, F, G }` to the emitted quantifier. Also, any attribute `{:extract_attribute "name", E, F, G}` adds an attribute `{:name E, F, G}` to the quantifier. + +### Expressions + +The pre- and postconditions of extracted lemmas turn into analogous Boogie expressions, and the types of function/lemma parameters and bound variables are extracted into analogous Boogie types. The intended usage of the extract mechanism is that these expressions and types do indeed have analogous Boogie types. + +At this time, only a limited set of expressions and types are supported, but more can be added in the future. + +Any `forall` and `exists` quantifiers in expressions are allowed to use `:extract_pattern` and `:extract_attribute` attributes, as described above for lemmas. + +Some extracted expressions are simplified. For example, `true && !!P` is simplified to `P`. + +### Soundness + +The Dafny program that is used as input for the extraction is treated like any other Dafny program. The intended usage of the extraction mechanism is to prove parts of the axiomatization in `DafnyPrelude.bpl` to be logically consistent. Whether or not the extracted Boogie declarations meet this goal depends on the given Dafny program. For example, if the given Dafny program formalizes sequences in terms of maps and formalizes maps in terms of sequences, then the extraction probably does not provide guarantees of consistency. From 8d8d2c813780c4555165c4bee5d346bef39e0619 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 12 Jul 2024 15:54:43 -0700 Subject: [PATCH 45/52] Format Dafny code --- Source/DafnyCore/Prelude/Lists.dfy | 10 +++++----- Source/DafnyCore/Prelude/Sequences.dfy | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Source/DafnyCore/Prelude/Lists.dfy b/Source/DafnyCore/Prelude/Lists.dfy index e79b7900c19..17d505d3b8c 100644 --- a/Source/DafnyCore/Prelude/Lists.dfy +++ b/Source/DafnyCore/Prelude/Lists.dfy @@ -31,7 +31,7 @@ module Lists { lemma AppendAt(xs: List, i: nat) requires i < Append(xs).Length() ensures Append(xs).At(i) == - if i < Length() then At(i) else (LengthAppend(xs); xs.At(i - Length())) + if i < Length() then At(i) else (LengthAppend(xs); xs.At(i - Length())) { } @@ -73,8 +73,8 @@ module Lists { lemma TakeFromAppend(xs: List, n: nat) requires n <= Length() + xs.Length() ensures (LengthAppend(xs); - Append(xs).Take(n) == - if n <= Length() then Take(n) else Append(xs.Take(n - Length()))) + Append(xs).Take(n) == + if n <= Length() then Take(n) else Append(xs.Take(n - Length()))) { LengthAppend(xs); } @@ -91,8 +91,8 @@ module Lists { lemma DropFromAppend(xs: List, n: nat) requires n <= Length() + xs.Length() ensures (LengthAppend(xs); - Append(xs).Drop(n) == - if n <= Length() then Drop(n).Append(xs) else xs.Drop(n - Length())) + Append(xs).Drop(n) == + if n <= Length() then Drop(n).Append(xs) else xs.Drop(n - Length())) { LengthAppend(xs); } diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index 14ebc3bf012..efb9dd85f7f 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -307,7 +307,7 @@ module {:extract} Sequences { // (exists i: int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && Seq#Index(s,i) == x)); lemma {:extract_pattern Contains(s, x)} SeqContainsItsElements(s: Seq, x: Box) ensures Contains(s, x) <==> - exists i: int {:extract_pattern Index(s, i)} :: 0 <= i < Length(s) && Index(s, i) == x + exists i: int {:extract_pattern Index(s, i)} :: 0 <= i < Length(s) && Index(s, i) == x { } @@ -408,7 +408,7 @@ module {:extract} Sequences { // 0 <= i && i < n && i < Seq#Length(s) && Seq#Index(s, i) == x)); lemma {:extract_pattern Contains(Take(s, n), x)} TakeContains(s: Seq, n: int, x: Box) ensures Contains(Take(s, n), x) <==> - exists i: int {:extract_pattern Index(s, i)} :: 0 <= i < n && i < Length(s) && Index(s, i) == x + exists i: int {:extract_pattern Index(s, i)} :: 0 <= i < n && i < Length(s) && Index(s, i) == x { if case n < 0 => @@ -460,7 +460,7 @@ module {:extract} Sequences { // 0 <= n && n <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); lemma {:extract_pattern Contains(Drop(s, n), x)} DropContains(s: Seq, n: int, x: Box) ensures Contains(Drop(s, n), x) <==> - exists i: int {:extract_pattern Index(s, i)} :: 0 <= n <= i < Length(s) && Index(s, i) == x + exists i: int {:extract_pattern Index(s, i)} :: 0 <= n <= i < Length(s) && Index(s, i) == x { if 0 <= n <= s.Length() { var (prefix, suffix) := s.Split(n); @@ -526,9 +526,9 @@ module {:extract} Sequences { // 0 <= j && j < Seq#Length(s0) ==> Seq#Index(s0,j) == Seq#Index(s1,j))); lemma {:extract_pattern Equal(s0, s1)} AboutEqual(s0: Seq, s1: Seq) ensures Equal(s0, s1) <==> - && Length(s0) == Length(s1) - && forall j: int {:extract_pattern Index(s0, j)} {:extract_pattern Index(s1, j)} :: - 0 <= j < Length(s0) ==> Index(s0, j) == Index(s1, j) + && Length(s0) == Length(s1) + && forall j: int {:extract_pattern Index(s0, j)} {:extract_pattern Index(s1, j)} :: + 0 <= j < Length(s0) ==> Index(s0, j) == Index(s1, j) { if Length(s0) == Length(s1) && forall j :: 0 <= j < Length(s0) ==> Index(s0, j) == Index(s1, j) { assert forall s, i :: 0 <= i < Length(s) ==> Index(s, i) == s.At(i); @@ -574,8 +574,8 @@ module {:extract} Sequences { // 0 <= j && j < n ==> Seq#Index(s0,j) == Seq#Index(s1,j))); lemma {:extract_pattern SameUntil(s0, s1, n)} AboutSameUntil(s0: Seq, s1: Seq, n: int) ensures SameUntil(s0, s1, n) <==> - forall j: int {:extract_pattern Index(s0, j)} {:extract_pattern Index(s1, j)} :: - 0 <= j < n ==> Index(s0, j) == Index(s1, j) + forall j: int {:extract_pattern Index(s0, j)} {:extract_pattern Index(s1, j)} :: + 0 <= j < n ==> Index(s0, j) == Index(s1, j) { } @@ -607,7 +607,7 @@ module {:extract} Sequences { // 0 <= j && j < n && j < Seq#Length(s) ==> // Seq#Index(Seq#Take(s,n), j) == Seq#Index(s, j)); lemma {:extract_attribute "weight", 25} {:extract_pattern Index(Take(s, n), j)} {:extract_pattern Index(s, j), Take(s, n)} - IndexTake(s: Seq, n: int, j: int) + IndexTake(s: Seq, n: int, j: int) requires 0 <= j < n && j < Length(s) ensures Index(Take(s, n), j) == Index(s, j) { @@ -759,7 +759,7 @@ module {:extract} Sequences { } } } - + // Additional axioms about common things // boogie: From bfaa289f0115c95da8567fde0c9f69cb8771e620 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 12 Jul 2024 16:11:38 -0700 Subject: [PATCH 46/52] Account for order change of error messages --- .../LitTest/dafny0/Fuel.legacy.dfy.expect | 16 ++++++++-------- .../LitTest/git-issues/git-issue-2026.dfy | 2 +- .../LitTest/git-issues/git-issue-2026.dfy.expect | 2 +- .../server/counterexample_commandline.dfy.expect | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect index cedc561422a..a13a5837473 100755 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect @@ -21,19 +21,19 @@ Fuel.legacy.dfy(324,21): Related location Fuel.legacy.dfy(313,41): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(314,72): Related location +Fuel.legacy.dfy(312,43): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(314,93): Related location +Fuel.legacy.dfy(312,58): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location Fuel.legacy.dfy(314,46): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(312,43): Related location +Fuel.legacy.dfy(314,72): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(312,58): Related location +Fuel.legacy.dfy(314,93): Related location Fuel.legacy.dfy(335,49): Error: destructor 't' can only be applied to datatype values constructed by 'VTuple' Fuel.legacy.dfy(335,50): Error: index out of range Fuel.legacy.dfy(336,38): Error: index out of range @@ -46,16 +46,16 @@ Fuel.legacy.dfy(329,21): Related location Fuel.legacy.dfy(312,43): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(314,72): Related location +Fuel.legacy.dfy(313,41): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(314,93): Related location +Fuel.legacy.dfy(312,58): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(313,41): Related location +Fuel.legacy.dfy(314,72): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(312,58): Related location +Fuel.legacy.dfy(314,93): Related location Fuel.legacy.dfy(336,71): Error: index out of range Fuel.legacy.dfy(397,22): Error: assertion might not hold Fuel.legacy.dfy(398,22): Error: assertion might not hold diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2026.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2026.dfy index 9c250624fb8..9c6c1ff274d 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2026.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2026.dfy @@ -15,8 +15,8 @@ method foo(n: nat) returns (ret: array) var i := 0; while i < n - 1 invariant 0 <= i < n - invariant forall j :: 0 <= j < i ==> j % 2 == 0 ==> ret[j] == "even" invariant forall j :: 0 <= j < i ==> j % 2 == 1 ==> ret[j] == "odd" + invariant forall j :: 0 <= j < i ==> j % 2 == 0 ==> ret[j] == "even" { if i % 2 == 0 { ret[i] := "odd"; diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2026.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2026.dfy.expect index cad8bc15090..83094f7432c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2026.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2026.dfy.expect @@ -1,4 +1,4 @@ -git-issue-2026.dfy(18,18): Error: this invariant could not be proved to be maintained by the loop +git-issue-2026.dfy(19,18): Error: this invariant could not be proved to be maintained by the loop Related message: loop invariant violation Related counterexample: WARNING: the following counterexample may be inconsistent or invalid. See dafny.org/dafny/DafnyRef/DafnyRef#sec-counterexamples diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/server/counterexample_commandline.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/server/counterexample_commandline.dfy.expect index 1a0deba5dfa..e13eaa030b7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/server/counterexample_commandline.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/server/counterexample_commandline.dfy.expect @@ -4,13 +4,13 @@ counterexample_commandline.dfy(31,20): Error: a postcondition could not be prove Temporary variables to describe counterexamples: ghost var counterexampleLoopGuard0 : bool := false; counterexample_commandline.dfy(21,8): initial state: - assume this != null && |this.p| > 0 && |s| > 0 && 1 == |s| && '\U{0002}' == s[0] && 1 == |this.p| && '?' == this.p[0]; + assume this != null && |this.p| > 0 && |s| > 0 && 1 == |s| && '\0' == s[0] && 1 == |this.p| && '?' == this.p[0]; counterexample_commandline.dfy(22,22): - assume this != null && |this.p| > 0 && |s| > 0 && 1 == |s| && '\U{0002}' == s[0] && false == b && 1 == |this.p| && '?' == this.p[0]; + assume this != null && |this.p| > 0 && |s| > 0 && 1 == |s| && '\0' == s[0] && false == b && 1 == |this.p| && '?' == this.p[0]; counterexample_commandline.dfy(23,22): - assume this != null && |this.p| > 0 && |s| > 0 && 1 == |s| && '\U{0002}' == s[0] && false == b && 0 == i && '?' == this.p[0] && 1 == |this.p|; + assume this != null && |this.p| > 0 && |s| > 0 && 1 == |s| && '\0' == s[0] && false == b && 0 == i && '?' == this.p[0] && 1 == |this.p|; counterexample_commandline.dfy(24,12): after some loop iterations: - counterexampleLoopGuard0 := this != null && |this.p| > 0 && |s| > 0 && 1 == |s| && '\U{0002}' == s[0] && false == b && 0 == i && '?' == this.p[0] && 1 == |this.p|; + counterexampleLoopGuard0 := this != null && |this.p| > 0 && |s| > 0 && 1 == |s| && '\0' == s[0] && false == b && 0 == i && '?' == this.p[0] && 1 == |this.p|; counterexample_commandline.dfy(18,22): Related location: this is the postcondition that could not be proved From f010720ee1d251d7278308d39c91b4cb55d23df9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 12 Jul 2024 16:23:27 -0700 Subject: [PATCH 47/52] Use :isolate_assertions with SchorrWaite.dfy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The difference between the legacy/refresh resolver runs is that some bound variables and an empty set are typed with …Node… in legacy and …Node?… in refresh. --- .../TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy | 2 +- .../TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect | 2 +- .../LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy index 93b09e6e221..7a51c10f384 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy @@ -166,7 +166,7 @@ ghost predicate ReachableVia(source: Node, older p: Path, sink: Node, S: set n in S && sink in n.children && ReachableVia(source, prefix, n, S) } -method SchorrWaite(root: Node, ghost S: set) +method {:isolate_assertions} SchorrWaite(root: Node, ghost S: set) requires root in S // S is closed under 'children': requires forall n :: n in S ==> diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect index 4b0fec566c0..4070ea9fe11 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect @@ -1,2 +1,2 @@ -Dafny program verifier finished with 15 verified, 0 errors +Dafny program verifier finished with 277 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect new file mode 100644 index 00000000000..fe753f42e22 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 281 verified, 0 errors From 3e126cc8e082c668dca8c7c99284509c0051d1e5 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 20 Aug 2024 14:18:50 -0700 Subject: [PATCH 48/52] Update Source/DafnyDriver/Commands/VerifyCommand.cs Co-authored-by: Aaron Tomb --- Source/DafnyDriver/Commands/VerifyCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyDriver/Commands/VerifyCommand.cs b/Source/DafnyDriver/Commands/VerifyCommand.cs index e8c52702988..79f77f59d76 100644 --- a/Source/DafnyDriver/Commands/VerifyCommand.cs +++ b/Source/DafnyDriver/Commands/VerifyCommand.cs @@ -91,7 +91,7 @@ public static async Task HandleVerification(DafnyOptions options) { var extractedProgram = Extractor.Extract(resolution.ResolvedProgram); engine.PrintBplFile(options.BoogieExtractionTargetFile, extractedProgram, true, pretty: true); } catch (ExtractorError extractorError) { - options.OutputWriter.WriteLine($"extraction error: {extractorError.Message}"); + options.OutputWriter.WriteLine($"Boogie axiom extraction error: {extractorError.Message}"); return 1; } } From 4efe6174f1ffef21a45a73412a59fd263fbd4c62 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 20 Aug 2024 14:25:14 -0700 Subject: [PATCH 49/52] =?UTF-8?q?Rename=20=E2=80=9CExtractor=E2=80=9D=20?= =?UTF-8?q?=E2=80=9CBoogieExtractor=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DafnyCore/Backends/{Extractor.cs => BoogieExtractor.cs} | 6 +++--- Source/DafnyDriver/Commands/VerifyCommand.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename Source/DafnyCore/Backends/{Extractor.cs => BoogieExtractor.cs} (98%) diff --git a/Source/DafnyCore/Backends/Extractor.cs b/Source/DafnyCore/Backends/BoogieExtractor.cs similarity index 98% rename from Source/DafnyCore/Backends/Extractor.cs rename to Source/DafnyCore/Backends/BoogieExtractor.cs index 5a17e9e49ce..8db33453455 100644 --- a/Source/DafnyCore/Backends/Extractor.cs +++ b/Source/DafnyCore/Backends/BoogieExtractor.cs @@ -22,12 +22,12 @@ public ExtractorError(string message) } } - public class Extractor : ASTVisitor { + public class BoogieExtractor : ASTVisitor { /// /// Throws an "ExtractorError" if the input is unexpected or unsupported. /// public static Boogie.Program Extract(Program program) { - var extractor = new Extractor(); + var extractor = new BoogieExtractor(); extractor.VisitModule(program.DefaultModule); extractor.FixUpUsedByInformation(); @@ -41,7 +41,7 @@ public static Boogie.Program Extract(Program program) { private readonly Dictionary functionExtractions = new(); private readonly List<(Boogie.Axiom, Function)> axiomUsedBy = new(); - private Extractor() { + private BoogieExtractor() { } void FixUpUsedByInformation() { diff --git a/Source/DafnyDriver/Commands/VerifyCommand.cs b/Source/DafnyDriver/Commands/VerifyCommand.cs index 79f77f59d76..971e5f49950 100644 --- a/Source/DafnyDriver/Commands/VerifyCommand.cs +++ b/Source/DafnyDriver/Commands/VerifyCommand.cs @@ -88,7 +88,7 @@ public static async Task HandleVerification(DafnyOptions options) { if (!resolution.HasErrors && options.BoogieExtractionTargetFile != null) { using (var engine = ExecutionEngine.CreateWithoutSharedCache(options)) { try { - var extractedProgram = Extractor.Extract(resolution.ResolvedProgram); + var extractedProgram = BoogieExtractor.Extract(resolution.ResolvedProgram); engine.PrintBplFile(options.BoogieExtractionTargetFile, extractedProgram, true, pretty: true); } catch (ExtractorError extractorError) { options.OutputWriter.WriteLine($"Boogie axiom extraction error: {extractorError.Message}"); From c6e20155168e34ff38f3bdd449a25e8dbb43701a Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 20 Aug 2024 14:55:56 -0700 Subject: [PATCH 50/52] Overhaul the attribute names --- Source/DafnyCore/Backends/BoogieExtractor.cs | 59 ++++++++++++++++---- Source/DafnyCore/Prelude/Sequences.dfy | 30 +++++----- 2 files changed, 62 insertions(+), 27 deletions(-) diff --git a/Source/DafnyCore/Backends/BoogieExtractor.cs b/Source/DafnyCore/Backends/BoogieExtractor.cs index 8db33453455..86b110d7091 100644 --- a/Source/DafnyCore/Backends/BoogieExtractor.cs +++ b/Source/DafnyCore/Backends/BoogieExtractor.cs @@ -23,6 +23,41 @@ public ExtractorError(string message) } public class BoogieExtractor : ASTVisitor { + /// + /// Says to look inside the module for contents to extract. + /// Can be applied to a module. + /// + /// Note: This attribute isn't used yet, so the use of the attribute is just a stylistic thing at the moment. + /// Similarly, one can imagine that the extractor will look at a module's export clauses, to--in some way--check that + /// the set of Boogie declarations exported are self consistent. But that isn't done yet, either. Instead, all contents + /// of all files given to the extractor are considered for extraction. + /// + private const string ExtractAttribute = "extract_boogie"; + + /// + /// Gives the Boogie name to be used in the extraction. + /// Can be applied to types and functions. + /// + private const string NameAttribute = "extract_boogie_name"; + + /// + /// Says to place the extracted axiom declaration in the "uses" block of the given function. + /// Can be applied to lemmas. + /// + private const string UsedByAttribute = "extract_used_by"; + + /// + /// Gives a matching pattern to be applied in the quantifier emitted from a lemma or from a quantifier. + /// Can be applied to lemmas and quantifiers. + /// + private const string PatternAttribute = "extract_pattern"; + + /// + /// Specifies an additional attribute to be attached to the quantifier emitted from a lemma. + /// Can be applied to lemmas. + /// + private const string AttributeAttribute = "extract_attribute"; + /// /// Throws an "ExtractorError" if the input is unexpected or unsupported. /// @@ -47,7 +82,7 @@ private BoogieExtractor() { void FixUpUsedByInformation() { foreach (var (axiom, function) in axiomUsedBy) { if (!functionExtractions.TryGetValue(function, out var boogieFunction)) { - throw new ExtractorError($":extract_used_by attribute mentions non-extracted function: {function.Name}"); + throw new ExtractorError($":{UsedByAttribute} attribute mentions non-extracted function: {function.Name}"); } boogieFunction.OtherDefinitionAxioms.Add(axiom); } @@ -64,7 +99,7 @@ void VisitModule(ModuleDecl module) { VisitDeclarations(module.Signature.TopLevels.Values.ToList()); - if (Attributes.Contains(module.Signature.ModuleDef.Attributes, "extract")) { + if (Attributes.Contains(module.Signature.ModuleDef.Attributes, ExtractAttribute)) { declarations.Sort((d0, d1) => d0.tok.pos - d1.tok.pos); allDeclarations.AddRange(declarations); } @@ -90,14 +125,14 @@ public override void VisitMethod(Method method) { return; } - var patterns = Attributes.FindAllExpressions(lemma.Attributes, "extract_pattern"); - var usedByInfo = Attributes.Find(lemma.Attributes, "extract_used_by"); + var patterns = Attributes.FindAllExpressions(lemma.Attributes, PatternAttribute); + var usedByInfo = Attributes.Find(lemma.Attributes, UsedByAttribute); if (patterns == null && usedByInfo == null) { return; } if ((lemma.Ins.Count == 0) != (patterns == null)) { - throw new ExtractorError($"a parameterized lemma must specify at least one :extract_pattern: {lemma.Name}"); + throw new ExtractorError($"a parameterized lemma must specify at least one :{PatternAttribute}: {lemma.Name}"); } if (lemma.TypeArgs.Count != 0) { throw new ExtractorError($"an extracted lemma is not allowed to have type parameters: {lemma.Name}"); @@ -132,7 +167,7 @@ public override void VisitMethod(Method method) { if (usedByInfo.Args.Count == 1 && usedByInfo.Args[0].Resolved is MemberSelectExpr { Member: Function function }) { axiomUsedBy.Add((axiom, function)); } else { - throw new ExtractorError($":extract_used_by argument on lemma '{lemma.Name}' is expected to be an extracted function"); + throw new ExtractorError($":{UsedByAttribute} argument on lemma '{lemma.Name}' is expected to be an extracted function"); } } } @@ -153,10 +188,10 @@ public override void VisitMethod(Method method) { private QKeyValue? GetKeyValues(IToken tok, Attributes attributes) { Boogie.QKeyValue kv = null; - var extractAttributes = Attributes.FindAllExpressions(attributes, "extract_attribute"); + var extractAttributes = Attributes.FindAllExpressions(attributes, AttributeAttribute); if (extractAttributes != null) { if (extractAttributes.Count == 0) { - throw new ExtractorError($"first argument to :extract_attribute is expected to be a literal string; got no arguments"); + throw new ExtractorError($"first argument to :{AttributeAttribute} is expected to be a literal string; got no arguments"); } for (var i = extractAttributes.Count; 0 <= --i;) { string? attrName = null; @@ -167,7 +202,7 @@ public override void VisitMethod(Method method) { } else if (argument is StringLiteralExpr { Value: string name }) { attrName = name; } else { - throw new ExtractorError($"first argument to :extract_attribute is expected to be a literal string; got: {argument}"); + throw new ExtractorError($"first argument to :{AttributeAttribute} is expected to be a literal string; got: {argument}"); } } @@ -211,7 +246,7 @@ private Boogie.Type ExtractType(Type type) { } private string? GetExtractName(Attributes attributes) { - if (Attributes.Find(attributes, "extract_name") is { } extractNameAttribute) { + if (Attributes.Find(attributes, NameAttribute) is { } extractNameAttribute) { if (extractNameAttribute.Args.Count == 1 && extractNameAttribute.Args[0] is StringLiteralExpr { Value: string extractName }) { return extractName; } @@ -289,9 +324,9 @@ private Boogie.Expr ExtractExpr(Expression expr) { (Boogie.Variable)new Boogie.BoundVariable(tok, new TypedIdent(tok, boundVar.Name, ExtractType(boundVar.Type))) ); - var patterns = Attributes.FindAllExpressions(quantifierExpr.Attributes, "extract_pattern"); + var patterns = Attributes.FindAllExpressions(quantifierExpr.Attributes, PatternAttribute); if (patterns.Count == 0) { - throw new ExtractorError("extraction expects every quantifier to specify at least one :extract_pattern"); + throw new ExtractorError($"extraction expects every quantifier to specify at least one :{PatternAttribute}"); } var triggers = GetTriggers(tok, patterns); diff --git a/Source/DafnyCore/Prelude/Sequences.dfy b/Source/DafnyCore/Prelude/Sequences.dfy index efb9dd85f7f..d4ea7c2e86e 100644 --- a/Source/DafnyCore/Prelude/Sequences.dfy +++ b/Source/DafnyCore/Prelude/Sequences.dfy @@ -1,4 +1,4 @@ -module {:extract} Sequences { +module {:extract_boogie} Sequences { export provides Lists, Boxes provides Seq, Length, AboutLength @@ -19,10 +19,10 @@ module {:extract} Sequences { import opened Boxes // boogie: type Seq; - type {:extract_name "Seq"} Seq = List + type {:extract_boogie_name "Seq"} Seq = List // boogie: function Seq#Length(Seq): int; - function {:extract_name "Seq#Length"} Length(s: Seq): int { + function {:extract_boogie_name "Seq#Length"} Length(s: Seq): int { s.Length() } @@ -33,7 +33,7 @@ module {:extract} Sequences { } // boogie: function Seq#Empty(): Seq; - function {:extract_name "Seq#Empty"} Empty(): Seq { + function {:extract_boogie_name "Seq#Empty"} Empty(): Seq { Nil } @@ -59,16 +59,16 @@ module {:extract} Sequences { // Seq#Length(s) != 0 ==> (exists x: Box :: Seq#Contains(s, x))); // boogie: function Seq#Build(s: Seq, val: Box): Seq; - function {:extract_name "Seq#Build"} Build(s: Seq, val: Box): Seq { + function {:extract_boogie_name "Seq#Build"} Build(s: Seq, val: Box): Seq { s.Append(Cons(val, Nil)) } // boogie: function Seq#Build_inv0(s: Seq) : Seq; - function {:extract_name "Seq#Build_inv0"} BuildInv0(s: Seq): Seq { + function {:extract_boogie_name "Seq#Build_inv0"} BuildInv0(s: Seq): Seq { if s.Nil? then Nil else s.Take(s.Length() - 1) } // boogie: function Seq#Build_inv1(s: Seq) : Box; - function {:extract_name "Seq#Build_inv1"} BuildInv1(s: Seq): Box { + function {:extract_boogie_name "Seq#Build_inv1"} BuildInv1(s: Seq): Box { if s.Nil? then Boxes.arbitrary else @@ -161,7 +161,7 @@ module {:extract} Sequences { } // boogie: function Seq#Index(Seq, int): Box; - function {:extract_name "Seq#Index"} Index(s: Seq, i: int): Box { + function {:extract_boogie_name "Seq#Index"} Index(s: Seq, i: int): Box { if 0 <= i < Length(s) then s.At(i) else @@ -228,7 +228,7 @@ module {:extract} Sequences { } // boogie: function Seq#Update(Seq, int, Box): Seq; - function {:extract_name "Seq#Update"} Update(s: Seq, i: int, val: Box): Seq { + function {:extract_boogie_name "Seq#Update"} Update(s: Seq, i: int, val: Box): Seq { if !(0 <= i < s.Length()) then s else if i == 0 then @@ -292,12 +292,12 @@ module {:extract} Sequences { } // boogie: function Seq#Append(Seq, Seq): Seq; - function {:extract_name "Seq#Append"} Append(s0: Seq, s1: Seq): Seq { + function {:extract_boogie_name "Seq#Append"} Append(s0: Seq, s1: Seq): Seq { s0.Append(s1) } // boogie: function Seq#Contains(Seq, Box): bool; - predicate {:extract_name "Seq#Contains"} Contains(s: Seq, val: Box) { + predicate {:extract_boogie_name "Seq#Contains"} Contains(s: Seq, val: Box) { exists i :: 0 <= i < Length(s) && Index(s, i) == val } @@ -514,7 +514,7 @@ module {:extract} Sequences { } // boogie: function Seq#Equal(Seq, Seq): bool; - predicate {:extract_name "Seq#Equal"} Equal(s0: Seq, s1: Seq) { + predicate {:extract_boogie_name "Seq#Equal"} Equal(s0: Seq, s1: Seq) { s0 == s1 } @@ -563,7 +563,7 @@ module {:extract} Sequences { } // boogie: function Seq#SameUntil(Seq, Seq, int): bool; - predicate {:extract_name "Seq#SameUntil"} SameUntil(s0: Seq, s1: Seq, n: int) { + predicate {:extract_boogie_name "Seq#SameUntil"} SameUntil(s0: Seq, s1: Seq, n: int) { forall j :: 0 <= j < n ==> Index(s0, j) == Index(s1, j) } @@ -580,7 +580,7 @@ module {:extract} Sequences { } // boogie: function Seq#Take(s: Seq, howMany: int): Seq; - function {:extract_name "Seq#Take"} Take(s: Seq, howMany: int): Seq { + function {:extract_boogie_name "Seq#Take"} Take(s: Seq, howMany: int): Seq { if howMany < 0 then Empty() else if howMany <= s.Length() then @@ -617,7 +617,7 @@ module {:extract} Sequences { } // boogie: function Seq#Drop(s: Seq, howMany: int): Seq; - function {:extract_name "Seq#Drop"} Drop(s: Seq, howMany: int): Seq { + function {:extract_boogie_name "Seq#Drop"} Drop(s: Seq, howMany: int): Seq { if 0 <= howMany <= s.Length() then s.Drop(howMany) else From 81c44f3d092c323d47f24d19a14c065640663e31 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 21 Aug 2024 09:17:11 -0700 Subject: [PATCH 51/52] Adjust resource count in expect file --- .../TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect index f627136c12e..26eb0510a5f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect @@ -91,5 +91,5 @@ SubsetTypes.dfy(459,15): Error: assertion might not hold SubsetTypes.dfy(464,13): Error: assertion might not hold Dafny program verifier finished with 13 verified, 91 errors -Total resources used is 738270 +Total resources used is 737920 Max resources used by VC is 67520 From 6a3f5f66825975ebadaa8e71618d5a0ef5fa6883 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 29 Aug 2024 12:42:18 -0700 Subject: [PATCH 52/52] Update resource counts for SchorrWaite.dfy --- .../LitTests/LitTest/dafny1/SchorrWaite.dfy.expect | 6 +++--- .../LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect index a62235a4feb..bfd5af7ea0e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect @@ -1,4 +1,4 @@ -Dafny program verifier finished with 277 verified, 0 errors -Total resources used is 27322925 -Max resources used by VC is 929628 +Dafny program verifier finished with 272 verified, 0 errors +Total resources used is 30533110 +Max resources used by VC is 2074326 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect index 088141e38f1..f94a7aece4c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect @@ -1,4 +1,4 @@ -Dafny program verifier finished with 281 verified, 0 errors -Total resources used is 27345253 -Max resources used by VC is 840700 +Dafny program verifier finished with 276 verified, 0 errors +Total resources used is 28989926 +Max resources used by VC is 1092418