Skip to content

Commit

Permalink
Isolate paths (dafny-lang#5832)
Browse files Browse the repository at this point in the history
Requires Boogie boogie-org/boogie#970

### Changes
- Allows using `{:isolate "paths"}` on `assert`, `return` and `continue`
statements.

### Out of scope
- `{:isolate "paths"}` for implicit assertions
- Custom syntax for {:isolate}, possibly in combination with "by
clauses"

### Testing
- Added the CLI test `isolatePaths.dfy`

<small>By submitting this pull request, I confirm that my contribution
is made under the terms of the [MIT
license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt).</small>

---------

Co-authored-by: Robin Salkeld <salkeldr@amazon.com>
  • Loading branch information
keyboardDrummer and robin-aws authored Oct 25, 2024
1 parent fc90edc commit 84dbe69
Show file tree
Hide file tree
Showing 59 changed files with 579 additions and 386 deletions.
8 changes: 4 additions & 4 deletions Source/DafnyCore/AST/Statements/ControlFlow/BreakStmt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public BreakStmt(Cloner cloner, BreakStmt original) : base(cloner, original) {
}
}

public BreakStmt(RangeToken rangeToken, IToken targetLabel, bool isContinue)
: base(rangeToken) {
public BreakStmt(RangeToken rangeToken, IToken targetLabel, bool isContinue, Attributes attributes = null)
: base(rangeToken, attributes) {
Contract.Requires(rangeToken != null);
Contract.Requires(targetLabel != null);
this.TargetLabel = targetLabel;
Expand All @@ -43,8 +43,8 @@ public BreakStmt(RangeToken rangeToken, IToken targetLabel, bool isContinue)
/// For "isContinue == false", represents the statement "break ^breakAndContinueCount ;".
/// For "isContinue == true", represents the statement "break ^(breakAndContinueCount - 1) continue;".
/// </summary>
public BreakStmt(RangeToken rangeToken, int breakAndContinueCount, bool isContinue)
: base(rangeToken) {
public BreakStmt(RangeToken rangeToken, int breakAndContinueCount, bool isContinue, Attributes attributes = null)
: base(rangeToken, attributes) {
Contract.Requires(rangeToken != null);
Contract.Requires(1 <= breakAndContinueCount);
this.BreakAndContinueCount = breakAndContinueCount;
Expand Down
4 changes: 2 additions & 2 deletions Source/DafnyCore/AST/Statements/Methods/ProduceStmt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ protected ProduceStmt(Cloner cloner, ProduceStmt original) : base(cloner, origin
}
}

public ProduceStmt(RangeToken rangeToken, List<AssignmentRhs> rhss)
: base(rangeToken) {
protected ProduceStmt(RangeToken rangeToken, List<AssignmentRhs> rhss, Attributes attributes)
: base(rangeToken, attributes) {
this.Rhss = rhss;
HiddenUpdate = null;
}
Expand Down
4 changes: 2 additions & 2 deletions Source/DafnyCore/AST/Statements/Methods/ReturnStmt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public ReturnStmt(Cloner cloner, ReturnStmt original) : base(cloner, original) {
ReverifyPost = original.ReverifyPost;
}

public ReturnStmt(RangeToken rangeToken, List<AssignmentRhs> rhss)
: base(rangeToken, rhss) {
public ReturnStmt(RangeToken rangeToken, List<AssignmentRhs> rhss, Attributes attributes = null)
: base(rangeToken, rhss, attributes) {
Contract.Requires(rangeToken != null);
}
}
2 changes: 1 addition & 1 deletion Source/DafnyCore/AST/Statements/Methods/YieldStmt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public YieldStmt(Cloner cloner, YieldStmt original) : base(cloner, original) {
}

public YieldStmt(RangeToken rangeToken, List<AssignmentRhs> rhss)
: base(rangeToken, rhss) {
: base(rangeToken, rhss, null) {
}

public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) {
Expand Down
14 changes: 3 additions & 11 deletions Source/DafnyCore/AST/Statements/Statement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,7 @@ public abstract class Statement : RangeNode, IAttributeBearingDeclaration {
public int ScopeDepth { get; set; }
public LList<Label> Labels; // mutable during resolution

private Attributes attributes;
public Attributes Attributes {
get {
return attributes;
}
set {
attributes = value;
}
}
public Attributes Attributes { get; set; }
string IAttributeBearingDeclaration.WhatKind => "statement";

[ContractInvariantMethod]
Expand All @@ -36,7 +28,7 @@ public virtual void GenResolve(INewOrOldResolver resolver, ResolutionContext res

protected Statement(Cloner cloner, Statement original) : base(cloner.Tok(original.RangeToken)) {
cloner.AddStatementClone(original, this);
this.attributes = cloner.CloneAttributes(original.Attributes);
this.Attributes = cloner.CloneAttributes(original.Attributes);

if (cloner.CloneResolvedFields) {
IsGhost = original.IsGhost;
Expand All @@ -45,7 +37,7 @@ protected Statement(Cloner cloner, Statement original) : base(cloner.Tok(origina
}

protected Statement(RangeToken rangeToken, Attributes attrs) : base(rangeToken) {
this.attributes = attrs;
this.Attributes = attrs;
}

protected Statement(RangeToken rangeToken)
Expand Down
2 changes: 1 addition & 1 deletion Source/DafnyCore/AST/Tokens.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Text;
Expand Down Expand Up @@ -160,6 +159,7 @@ public Uri Uri {
public bool IsValid {
get { return WrappedToken.IsValid; }
}

public int kind {
get { return WrappedToken.kind; }
set { WrappedToken.kind = value; }
Expand Down
2 changes: 1 addition & 1 deletion Source/DafnyCore/CompileNestedMatch/MatchAst.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public IEnumerable<IHasNavigationToken> GetReferences() {
}
}

interface IMatch {
public interface IMatch {
IEnumerable<MatchCase> Cases { get; }
Expression Source { get; }
List<DatatypeCtor> MissingCases { get; }
Expand Down
8 changes: 6 additions & 2 deletions Source/DafnyCore/Dafny.atg
Original file line number Diff line number Diff line change
Expand Up @@ -2264,8 +2264,10 @@ BreakStmt<out Statement/*!*/ s>
IToken start = Token.NoToken;
IToken label = null;
int breakAndContinueCount = 1;
Attributes attributes = null;
.)
( "continue" (. start = t; isContinue = true; .)
[ Attribute<ref attributes> ]
[ LabelName<out label> ]
| "break" (. start = t; .)
( LabelName<out label>
Expand All @@ -2280,7 +2282,7 @@ BreakStmt<out Statement/*!*/ s>
(. Contract.Assert(label == null || breakAndContinueCount == 1);
s = label != null ?
new BreakStmt(new RangeToken(start, t), label, isContinue) :
new BreakStmt(new RangeToken(start, t), breakAndContinueCount, isContinue);
new BreakStmt(new RangeToken(start, t), breakAndContinueCount, isContinue, attributes);
.)
.

Expand All @@ -2291,14 +2293,16 @@ ReturnStmt<out Statement/*!*/ s>
IToken returnTok = t;
List<AssignmentRhs> rhss = null;
AssignmentRhs r;
Attributes attributes = null;
.)
[ Attribute<ref attributes> ]
[ Rhs<out r> (. rhss = new List<AssignmentRhs>();
rhss.Add(r);
.)
{ "," Rhs<out r> (. rhss.Add(r); .)
}
]
";" (. s = new ReturnStmt(new RangeToken(returnTok, t), rhss);
";" (. s = new ReturnStmt(new RangeToken(returnTok, t), rhss, attributes);
.)
.

Expand Down
2 changes: 1 addition & 1 deletion Source/DafnyCore/DafnyCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="System.Runtime.Numerics" Version="4.3.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.7.1" />
<PackageReference Include="Boogie.ExecutionEngine" Version="3.3.3" />
<PackageReference Include="Boogie.ExecutionEngine" Version="3.4.1" />
<PackageReference Include="Tomlyn" Version="0.16.2" />
</ItemGroup>

Expand Down
1 change: 0 additions & 1 deletion Source/DafnyCore/Options/BoogieOptionBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ static BoogieOptionBag() {
DafnyOptions.RegisterLegacyBinding(VerificationErrorLimit, (options, value) => { options.ErrorLimit = value; });
DafnyOptions.RegisterLegacyBinding(IsolateAssertions, (o, v) => o.VcsSplitOnEveryAssert = v);


OptionRegistry.RegisterGlobalOption(BoogieArguments, OptionCompatibility.CheckOptionMatches);
OptionRegistry.RegisterGlobalOption(NoVerify, OptionCompatibility.OptionLibraryImpliesLocalError);
OptionRegistry.RegisterOption(HiddenNoVerify, OptionScope.Cli);
Expand Down
2 changes: 2 additions & 0 deletions Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,8 @@ public static IToken ToDafnyToken(bool reportRanges, Bpl.IToken boogieToken) {
return null;
} else if (boogieToken is IToken dafnyToken) {
return dafnyToken;
} else if (boogieToken is VCGeneration.TokenWrapper tokenWrapper) {
return ToDafnyToken(reportRanges, tokenWrapper.Inner);
} else if (boogieToken == Boogie.Token.NoToken) {
return Token.NoToken;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1939,6 +1939,7 @@ public static bool RewriteInExpr(Expression s, bool aggressive) {

private static readonly HashSet<string> NullaryAttributesToCopy = new(new[] {
"focus",
"isolate",
"ignore",
"selective_checking",
"split",
Expand All @@ -1965,6 +1966,7 @@ public static bool RewriteInExpr(Expression s, bool aggressive) {

private static readonly HashSet<string> StringAttributesToCopy = new(new[] {
"captureState",
"isolate",
"error"
});

Expand Down Expand Up @@ -1999,7 +2001,7 @@ private QKeyValue TrStringAttribute(string name, Expression arg, QKeyValue rest)
: rest;
}

public QKeyValue TrAttributes(Attributes attrs, string skipThisAttribute) {
public QKeyValue TrAttributes(Attributes attrs, string skipThisAttribute = null) {
QKeyValue kv = null;
var hasNewTimeLimit = Attributes.Contains(attrs, "_timeLimit");
var hasNewRLimit = Attributes.Contains(attrs, "_rlimit");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Numerics;
using System.Diagnostics.Contracts;
using DafnyCore.Verifier;
using DafnyCore.Verifier.Statements;
using Bpl = Microsoft.Boogie;
using Microsoft.Boogie;
using Std.Wrappers;
Expand Down Expand Up @@ -261,13 +262,12 @@ public void CheckWellformed(Expression expr, WFOptions wfOptions, Variables loca
/// <summary>
/// Adds to "builder" code that checks the well-formedness of "expr". Any local variables introduced
/// in this code are added to "locals".
/// If "result" is non-null, then after checking the well-formedness of "expr", the generated code will
/// assume the equivalent of "result == expr".
/// See class WFOptions for descriptions of the specified options.
/// </summary>
public void CheckWellformedWithResult(Expression expr, WFOptions wfOptions,
Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran,
AddResultCommands addResultCommands) {

var origOptions = wfOptions;
if (wfOptions.LValueContext) {
// Turn off LValueContext for any recursive call
Expand Down Expand Up @@ -1313,7 +1313,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) {
break;
}
case MatchExpr matchExpr:
MatchStatementVerifier.TrMatchExpr(this, matchExpr, wfOptions, locals, builder, etran, addResultCommands);
MatchStmtVerifier.TrMatchExpr(this, matchExpr, wfOptions, locals, builder, etran, addResultCommands);
addResultCommands = null;
break;
case DatatypeUpdateExpr updateExpr: {
Expand Down
8 changes: 4 additions & 4 deletions Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ Bpl.Expr GetField(MemberSelectExpr fse) {
void AddWellformednessCheck(ConstantField decl) {
Contract.Requires(decl != null);
Contract.Requires(sink != null && Predef != null);
Contract.Requires(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null);
Contract.Ensures(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null);
Contract.Requires(currentModule == null && codeContext == null && IsAllocContext == null && fuelContext == null);
Contract.Ensures(currentModule == null && codeContext == null && IsAllocContext == null && fuelContext == null);

proofDependencies.SetCurrentDefinition(MethodVerboseName(decl.FullDafnyName, MethodTranslationKind.SpecWellformedness), null);
if (!InVerificationScope(decl)) {
Expand Down Expand Up @@ -209,7 +209,7 @@ void AddWellformednessCheck(ConstantField decl) {
var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false));
builder.Add(new CommentCmd($"AddWellformednessCheck for {decl.WhatKind} {decl}"));
builder.AddCaptureState(decl.tok, false, "initial state");
isAllocContext = new IsAllocContext(options, true);
IsAllocContext = new IsAllocContext(options, true);

DefineFrame(decl.tok, etran.ReadsFrame(decl.tok), new List<FrameExpression>(), builder, locals, null);

Expand All @@ -229,7 +229,7 @@ void AddWellformednessCheck(ConstantField decl) {

Contract.Assert(currentModule == decl.EnclosingModule);
Contract.Assert(codeContext == decl);
isAllocContext = null;
IsAllocContext = null;
fuelContext = null;
Reset();
}
Expand Down
4 changes: 2 additions & 2 deletions Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void AddIteratorSpecAndBody(IteratorDecl iter) {

FuelContext oldFuelContext = this.fuelContext;
this.fuelContext = FuelSetting.NewFuelContext(iter);
isAllocContext = new IsAllocContext(options, false);
IsAllocContext = new IsAllocContext(options, false);

// wellformedness check for method specification
Bpl.Procedure proc = AddIteratorProc(iter, MethodTranslationKind.SpecWellformedness);
Expand All @@ -53,7 +53,7 @@ void AddIteratorSpecAndBody(IteratorDecl iter) {
AddIteratorImpl(iter, proc);
}
this.fuelContext = oldFuelContext;
isAllocContext = null;
IsAllocContext = null;
}


Expand Down
34 changes: 17 additions & 17 deletions Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void AddClassMembers(TopLevelDeclWithMembers c, bool includeAllMethods, bool inc
}

foreach (MemberDecl member in c.Members.FindAll(VisibleInScope)) {
Contract.Assert(isAllocContext == null);
Contract.Assert(IsAllocContext == null);
CurrentDeclaration = member;
if (!filterOnlyMembers || member.HasUserAttribute("only", out _)) {
SetAssertionOnlyFilter(member);
Expand Down Expand Up @@ -143,7 +143,7 @@ private void AddIsAndIsAllocForReferenceType(ClassLikeDecl c) {
void AddFunction_Top(Function f, bool includeAllMethods) {
FuelContext oldFuelContext = this.fuelContext;
this.fuelContext = FuelSetting.NewFuelContext(f);
isAllocContext = new IsAllocContext(options, true);
IsAllocContext = new IsAllocContext(options, true);

AddClassMember_Function(f);

Expand All @@ -161,7 +161,7 @@ void AddFunction_Top(Function f, bool includeAllMethods) {
}

this.fuelContext = oldFuelContext;
isAllocContext = null;
IsAllocContext = null;
}

void AddMethod_Top(Method m, bool isByMethod, bool includeAllMethods) {
Expand Down Expand Up @@ -528,13 +528,13 @@ private void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc
Contract.Requires(proc != null);
Contract.Requires(sink != null && Predef != null);
Contract.Requires(wellformednessProc || m.Body != null);
Contract.Requires(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && isAllocContext == null);
Contract.Ensures(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && isAllocContext == null);
Contract.Requires(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && IsAllocContext == null);
Contract.Ensures(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && IsAllocContext == null);

proofDependencies.SetCurrentDefinition(proc.VerboseName, m);
currentModule = m.EnclosingClass.EnclosingModuleDefinition;
codeContext = m;
isAllocContext = new IsAllocContext(options, m.IsGhost);
IsAllocContext = new IsAllocContext(options, m.IsGhost);

List<Variable> inParams = Boogie.Formal.StripWhereClauses(proc.InParams);
List<Variable> outParams = Boogie.Formal.StripWhereClauses(proc.OutParams);
Expand Down Expand Up @@ -579,7 +579,7 @@ private void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc
}
}

isAllocContext = null;
IsAllocContext = null;
Reset();
}

Expand Down Expand Up @@ -819,13 +819,13 @@ private void AddMethodOverrideCheckImpl(Method m, Boogie.Procedure proc) {
Contract.Requires(m.Ins.Count == m.OverriddenMethod.Ins.Count);
Contract.Requires(m.Outs.Count == m.OverriddenMethod.Outs.Count);
//Contract.Requires(wellformednessProc || m.Body != null);
Contract.Requires(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && isAllocContext == null);
Contract.Ensures(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && isAllocContext == null);
Contract.Requires(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && IsAllocContext == null);
Contract.Ensures(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && IsAllocContext == null);

proofDependencies.SetCurrentDefinition(proc.VerboseName, m);
currentModule = m.EnclosingClass.EnclosingModuleDefinition;
codeContext = m;
isAllocContext = new IsAllocContext(options, m.IsGhost);
IsAllocContext = new IsAllocContext(options, m.IsGhost);

List<Variable> inParams = Boogie.Formal.StripWhereClauses(proc.InParams);
List<Variable> outParams = Boogie.Formal.StripWhereClauses(proc.OutParams);
Expand Down Expand Up @@ -893,7 +893,7 @@ private void AddMethodOverrideCheckImpl(Method m, Boogie.Procedure proc) {
}
}

isAllocContext = null;
IsAllocContext = null;
Reset();
}

Expand All @@ -917,8 +917,8 @@ private void AddFunctionOverrideCheckImpl(Function f) {
Contract.Requires(sink != null && Predef != null);
Contract.Requires(f.OverriddenFunction != null);
Contract.Requires(f.Ins.Count == f.OverriddenFunction.Ins.Count);
Contract.Requires(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && isAllocContext != null);
Contract.Ensures(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && isAllocContext != null);
Contract.Requires(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && IsAllocContext != null);
Contract.Ensures(currentModule == null && codeContext == null && _tmpIEs.Count == 0 && IsAllocContext != null);

proofDependencies.SetCurrentDefinition(MethodVerboseName(f.FullDafnyName, MethodTranslationKind.OverrideCheck), f);
#region first procedure, no impl yet
Expand Down Expand Up @@ -1688,15 +1688,15 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) {
Contract.Requires(m != null);
Contract.Requires(m.EnclosingClass != null);
Contract.Requires(Predef != null);
Contract.Requires(currentModule == null && codeContext == null && isAllocContext == null);
Contract.Ensures(currentModule == null && codeContext == null && isAllocContext == null);
Contract.Requires(currentModule == null && codeContext == null && IsAllocContext == null);
Contract.Ensures(currentModule == null && codeContext == null && IsAllocContext == null);
Contract.Ensures(Contract.Result<Boogie.Procedure>() != null);
Contract.Assert(VisibleInScope(m));

proofDependencies.SetCurrentDefinition(MethodVerboseName(m.FullDafnyName, kind), m);
currentModule = m.EnclosingClass.EnclosingModuleDefinition;
codeContext = m;
isAllocContext = new IsAllocContext(options, m.IsGhost);
IsAllocContext = new IsAllocContext(options, m.IsGhost);
Boogie.Expr prevHeap = null;
Boogie.Expr currHeap = null;
var ordinaryEtran = new ExpressionTranslator(this, Predef, m.tok, m);
Expand Down Expand Up @@ -1732,7 +1732,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) {

currentModule = null;
codeContext = null;
isAllocContext = null;
IsAllocContext = null;

return proc;

Expand Down
Loading

0 comments on commit 84dbe69

Please sign in to comment.