Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement verification coverage analysis #741

Merged
merged 29 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
49d5d23
Recognize {:id ...} attributes on assignments
atomb May 9, 2023
0ac0824
Recognize {:id ...} on requires clauses
atomb May 10, 2023
32398ee
Recognize {:id ...} on loop invariants
atomb May 10, 2023
5c4afaf
Add a field to VCResult to track unsat cores
atomb May 10, 2023
e945230
Basic support for labeled assertions
atomb May 15, 2023
ac56302
Fix concurrency bug accidentally discovered
atomb May 31, 2023
a97032b
Mostly complete verification coverage tracking
atomb May 31, 2023
5919289
Add initial test for verification coverage
atomb Jun 1, 2023
8ad7c16
Tweak RUN commands for verification coverage test
atomb Jun 1, 2023
e95aab5
Verification coverage unsupported in batch mode
atomb Jun 1, 2023
3ce4244
Attempt to make timeouts test more robust
atomb Jun 1, 2023
950080b
Another try for more deterministic timeout testing
atomb Jun 1, 2023
7b6679b
Tiny tweaks to coverage tests
atomb Jun 1, 2023
b67d227
Merge remote-tracking branch 'upstream/master' into broader-unsat-cores
atomb Jun 1, 2023
20e06a0
Rename variables
atomb Jun 2, 2023
abcae74
Adjust how IDs (and other attributes) are tracked
atomb Jun 2, 2023
6b48078
Remove `/printNecessaryAssumes` option
atomb Jun 2, 2023
d1861c0
Typo in comment
atomb Jun 2, 2023
a07fe28
Documentation for /printVerificationCoverage, :id
atomb Jun 2, 2023
82e7e24
Remove conditionals on magic variable prefixes
atomb Jun 2, 2023
98dcf9f
Copy `:id` attributes unconditionally
atomb Jun 2, 2023
d68b1f9
Rename variable for `id` on `CallCmd`s
atomb Jun 5, 2023
6d48d32
Add comment about dummy operators
atomb Jun 5, 2023
e43e4f2
PrintVerificationCoverage -> TrackVerificationCoverage
atomb Jun 5, 2023
94cf1ea
CurrentCoveredElements -> ProofRun.CoveredElements
atomb Jun 5, 2023
449364c
Rename command variables in VCGen for loops
atomb Jun 5, 2023
725fe2d
Update verification coverage test
atomb Jun 5, 2023
cd50eef
Remove uses of /printVerificationCoverage
atomb Jun 5, 2023
70cd55d
Merge branch 'master' into broader-unsat-cores
atomb Jun 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions Source/Core/AST/Absy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,33 @@ public List<int> FindLayers()
}
return layers.Distinct().OrderBy(l => l).ToList();
}

// Look for {:name string} in list of attributes.
public string FindStringAttribute(string name)
Copy link
Collaborator

@keyboardDrummer keyboardDrummer Jun 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you define these methods as extension methods, then you can also call them without casting to ICarriesAttributes first.

{
return QKeyValue.FindStringAttribute(Attributes, name);
}

public void AddStringAttribute(IToken tok, string name, string parameter)
{
Attributes = new QKeyValue(tok, name, new List<object>() {parameter}, Attributes);
}

public void CopyIdFrom(IToken tok, ICarriesAttributes src)
{
var id = src.FindStringAttribute("id");
if (id is not null) {
AddStringAttribute(tok, "id", id);
}
}

public void CopyIdWithSuffixFrom(IToken tok, ICarriesAttributes src, string suffix)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

target.Copy reads strangely. I would expect the src to be on the left side of the copy verb

{
var id = src.FindStringAttribute("id");
if (id is not null) {
AddStringAttribute(tok, "id", id + suffix);
}
}
}

[ContractClassFor(typeof(Absy))]
Expand Down Expand Up @@ -410,13 +437,6 @@ public Expr FindExprAttribute(string name)
return res;
}

// Look for {:name string} in list of attributes.
public string FindStringAttribute(string name)
{
Contract.Requires(name != null);
return QKeyValue.FindStringAttribute(this.Attributes, name);
}

// Look for {:name N} in list of attributes. Return false if attribute
// 'name' does not exist or if N is not an integer constant. Otherwise,
// return true and update 'result' with N.
Expand Down Expand Up @@ -1810,7 +1830,7 @@ public byte[] MD5DependencyChecksum

public string Checksum
{
get { return FindStringAttribute("checksum"); }
get { return (this as ICarriesAttributes).FindStringAttribute("checksum"); }
}

string dependencyChecksum;
Expand Down Expand Up @@ -3616,7 +3636,7 @@ public string Id
{
get
{
var id = FindStringAttribute("id");
var id = (this as ICarriesAttributes).FindStringAttribute("id");
if (id == null)
{
id = Name + GetHashCode().ToString() + ":0";
Expand Down
19 changes: 19 additions & 0 deletions Source/Core/AST/AbsyCmd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3720,6 +3720,7 @@ protected override Cmd ComputeDesugaring(PrintOptions options)
Dictionary<Variable, Expr> substMapBound = new Dictionary<Variable, Expr>();
List<Variable> /*!*/
tempVars = new List<Variable>();
string callId = (this as ICarriesAttributes).FindStringAttribute("id");

// proc P(ins) returns (outs)
// requires Pre
Expand Down Expand Up @@ -3838,6 +3839,7 @@ protected override Cmd ComputeDesugaring(PrintOptions options)
AssertCmd /*!*/
a = new AssertRequiresCmd(this, reqCopy);
Contract.Assert(a != null);

if (Attributes != null)
{
// Inherit attributes of call.
Expand All @@ -3846,6 +3848,11 @@ protected override Cmd ComputeDesugaring(PrintOptions options)
a.Attributes = attrCopy;
}

// Do this after copying the attributes so it doesn't get overwritten
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method being changed is very large, and uses regions instead of submethods to organize code. I want to ask you apply the boyscout principle of "leaving things behind nicer than they were", and to chop this method up into pieces, and also to move the class SugaredCmd out into it's own file, since it's in a file that's way too large.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #743 to track this, as I think we need to do it very carefully.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to do it very carefully

Why? Splitting a method up into pieces can be done in a mostly automated way using Rider. Same for moving a class into a separate file.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but reorganizing those methods so that they are well-organized and use better abstractions, rather than just slicing them into pieces, would require more thought.

Copy link
Collaborator

@keyboardDrummer keyboardDrummer Jun 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm paraphrasing an offline discussion I had with @atomb on the above, for the sake of getting some additional eyes on it.

@atomb prefers keeping the pacify implementations for various commands in a single method, since that allows having them be vertically adjacent to each other which follows the structure of Figure 3 in this paper, which is what the code is based on. Keeping the structure of the two most similar provides the best way of doing a side-by-side comparison.

I would prefer keeping the code for different commands separate. I would even prefer having an abstract Pacify method in the Cmd class, and having the commands in separate files. The number of commands is not fixed, we might add more in the future, and I think in this way the codebase scales well to that.

I believe that while the paper is structured by putting the concepts at the top-level, and then discussing all commands within each concept, this doesn't mean that's also the right structure for the code. It's easier to discuss just one pair of concept and command at a time, and while the paper doesn't have room to go that slow, in our codebase we can use as many separators, such as files and methods, as we like. Also, the code is more verbose so there is more need to keep the scope of each block to a minimum.

I don't think optimizing for comparability between the code and the paper is important. Modern IDEs make it easy to navigate between the code for various commands, so I don't think there's a particular difficulty in making the comparison in any case.

if (callId is not null) {
(a as ICarriesAttributes).CopyIdWithSuffixFrom(tok, req, $"${callId}$requires");
}

a.ErrorDataEnhanced = reqCopy.ErrorDataEnhanced;
newBlockBody.Add(a);
}
Expand All @@ -3857,6 +3864,11 @@ protected override Cmd ComputeDesugaring(PrintOptions options)
AssumeCmd /*!*/
a = new AssumeCmd(req.tok, Substituter.Apply(s, req.Condition));
Contract.Assert(a != null);
// These probably won't have IDs, but copy if they do.
if (callId is not null) {
(a as ICarriesAttributes).CopyIdWithSuffixFrom(tok, req, $"${callId}$requires_assumed");
}

newBlockBody.Add(a);
}
}
Expand Down Expand Up @@ -4019,6 +4031,10 @@ protected override Cmd ComputeDesugaring(PrintOptions options)

#endregion

if (callId is not null) {
(assume as ICarriesAttributes).CopyIdWithSuffixFrom(tok, e, $"${callId}$ensures");
}

newBlockBody.Add(assume);
}

Expand All @@ -4036,6 +4052,9 @@ protected override Cmd ComputeDesugaring(PrintOptions options)
cout_exp = new IdentifierExpr(cce.NonNull(couts[i]).tok, cce.NonNull(couts[i]));
Contract.Assert(cout_exp != null);
AssignCmd assign = Cmd.SimpleAssign(param_i.tok, cce.NonNull(this.Outs[i]), cout_exp);
if (callId is not null) {
Attributes = new QKeyValue(param_i.tok, "id", new List<object>(){ $"{callId}$out{i}" }, Attributes);
}
newBlockBody.Add(assign);
}
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/AST/AbsyType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3604,7 +3604,7 @@ public override bool IsSeq
// be represented using the provided string (and also does not need to be explicitly declared).
public string GetBuiltin()
{
return this.Decl.FindStringAttribute("builtin");
return (this.Decl as ICarriesAttributes).FindStringAttribute("builtin");
}

//----------- Cloning ----------------------------------
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/AST/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ public List<GlobalVariable /*!*/> /*!*/ GlobalVariables
}
}

public readonly ISet<string> NecessaryAssumes = new HashSet<string>();
public readonly ISet<string> AllCoveredElements = new HashSet<string>();

public IEnumerable<Block> Blocks()
{
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Monomorphization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class MonomorphismChecker : ReadOnlyVisitor

public static bool DoesTypeCtorDeclNeedMonomorphization(TypeCtorDecl typeCtorDecl)
{
return typeCtorDecl.Arity > 0 && typeCtorDecl.FindStringAttribute("builtin") == null;
return typeCtorDecl.Arity > 0 && (typeCtorDecl as ICarriesAttributes).FindStringAttribute("builtin") == null;
}

public static bool IsMonomorphic(Program program)
Expand Down
26 changes: 18 additions & 8 deletions Source/ExecutionEngine/CommandLineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ public bool NormalizeDeclarationOrder

public bool ImmediatelyAcceptCommands => StratifiedInlining > 0 || ContractInfer;

public bool ProduceUnsatCores => PrintNecessaryAssumes || EnableUnSatCoreExtract == 1 ||
public bool ProduceUnsatCores => TrackVerificationCoverage ||
EnableUnSatCoreExtract == 1 ||
ContractInfer && (UseUnsatCoreForContractInfer || ExplainHoudini);

public bool BatchModeSolver { get; set; }
Expand Down Expand Up @@ -279,10 +280,9 @@ public bool UseUnsatCoreForContractInfer {

public bool PrintAssignment { get; set; }

// TODO(wuestholz): Add documentation for this flag.
public bool PrintNecessaryAssumes {
get => printNecessaryAssumes;
set => printNecessaryAssumes = value;
public bool TrackVerificationCoverage {
get => trackVerificationCoverage;
set => trackVerificationCoverage = value;
}

public int InlineDepth { get; set; } = -1;
Expand Down Expand Up @@ -544,7 +544,7 @@ void ObjectInvariant5()
private bool reverseHoudiniWorklist = false;
private bool houdiniUseCrossDependencies = false;
private bool useUnsatCoreForContractInfer = false;
private bool printNecessaryAssumes = false;
private bool trackVerificationCoverage = false;
private bool useProverEvaluate;
private bool trustMoverTypes = false;
private bool trustNoninterference = false;
Expand Down Expand Up @@ -1297,7 +1297,7 @@ protected override bool ParseOption(string name, CommandLineParseState ps)
ps.CheckBooleanFlag("crossDependencies", x => houdiniUseCrossDependencies = x) ||
ps.CheckBooleanFlag("useUnsatCoreForContractInfer", x => useUnsatCoreForContractInfer = x) ||
ps.CheckBooleanFlag("printAssignment", x => PrintAssignment = x) ||
ps.CheckBooleanFlag("printNecessaryAssumes", x => printNecessaryAssumes = x) ||
ps.CheckBooleanFlag("trackVerificationCoverage", x => trackVerificationCoverage = x) ||
ps.CheckBooleanFlag("useProverEvaluate", x => useProverEvaluate = x) ||
ps.CheckBooleanFlag("deterministicExtractLoops", x => DeterministicExtractLoops = x) ||
ps.CheckBooleanFlag("verifySeparately", x => VerifySeparately = x) ||
Expand Down Expand Up @@ -1508,7 +1508,10 @@ order in which implementations are verified (default: N = 1).

{:id <string>}
Assign a unique ID to an implementation to be used for verification
result caching (default: ""<impl. name>:0"").
result caching (default: ""<impl. name>:0""), or assign a unique ID
to a statement or contract clause for use in identifying which program
elements were necessary to complete verification. The latter form is
used by the `/trackVerificationCoverage` option.

{:timeLimit N}
Set the time limit for verifying a given implementation.
Expand Down Expand Up @@ -1896,6 +1899,13 @@ without requiring the user to actually make those changes.
This option is implemented by renaming variables and reordering
declarations in the input, and by setting solver options that have
similar effects.
/trackVerificationCoverage
Track and report which program elements labeled with an
`{:id ...}` attribute were necessary to complete verification.
Assumptions, assertions, requires clauses, ensures clauses,
assignments, and calls can be labeled for inclusion in this
report. This generalizes and replaces the previous
(undocumented) `/printNecessaryAssertions` option.

---- Verification-condition splitting --------------------------------------

Expand Down
4 changes: 2 additions & 2 deletions Source/ExecutionEngine/ExecutionEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -673,8 +673,8 @@ private async Task<PipelineOutcome> VerifyEachImplementation(TextWriter output,
CleanupRequest(requestId);
}

if (Options.PrintNecessaryAssumes && processedProgram.Program.NecessaryAssumes.Any()) {
Options.OutputWriter.WriteLine("Necessary assume command(s): {0}", string.Join(", ", processedProgram.Program.NecessaryAssumes.OrderBy(s => s)));
if (Options.TrackVerificationCoverage && processedProgram.Program.AllCoveredElements.Any()) {
Options.OutputWriter.WriteLine("Elements covered by verification: {0}", string.Join(", ", processedProgram.Program.AllCoveredElements.OrderBy(s => s)));
}

cce.NonNull(Options.TheProverFactory).Close();
Expand Down
1 change: 1 addition & 0 deletions Source/ExecutionEngine/ExecutionEngine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<ProjectReference Include="..\Houdini\Houdini.csproj" />
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\VCGeneration\VCGeneration.csproj" />
<ProjectReference Include="..\Provers\SMTLib\SMTLib.csproj" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be removed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build of Boogie itself works without it, but I discovered it was necessary for Dafny (and presumably other Boogie clients?) to build against a Boogie source tree instead of a pre-built package.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, the alternative is that the Boogie clients such as Dafny also depend on SMTLib, but this seems like a nicer UX.

</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion Source/ExecutionEngine/VerificationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public sealed class VerificationResult
{
private readonly Implementation implementation;
public readonly string ProgramId;
public string MessageIfVerifies => implementation.FindStringAttribute("msg_if_verifies");
public string MessageIfVerifies => (implementation as ICarriesAttributes).FindStringAttribute("msg_if_verifies");
public string Checksum => implementation.Checksum;
public string DependenciesChecksum => implementation.DependencyChecksum;

Expand Down
1 change: 1 addition & 0 deletions Source/Houdini/HoudiniSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public class HoudiniStatistics
private readonly Houdini houdini;
public HoudiniStatistics stats;
public List<Counterexample> Counterexamples { get; } = new();
public HashSet<string> CoveredElements { get; } = new();
private VCExpr conjecture;
private ProverInterface.ErrorHandler handler;
ConditionGeneration.VerificationResultCollector collector;
Expand Down
2 changes: 1 addition & 1 deletion Source/Provers/SMTLib/ProverInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ protected ErrorHandler(SMTLibOptions options)
this.options = options;
}

public virtual void AddNecessaryAssume(string id)
public virtual void AddCoveredElement(string id)
{
throw new System.NotImplementedException();
}
Expand Down
8 changes: 4 additions & 4 deletions Source/Provers/SMTLib/SMTLibInteractiveTheoremProver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,18 +223,18 @@ public async Task<Outcome> CheckSat(CancellationToken cancellationToken,
if (resp.Name != "")
{
usedNamedAssumes.Add(resp.Name);
if (libOptions.PrintNecessaryAssumes)
if (libOptions.TrackVerificationCoverage)
{
reporter.AddNecessaryAssume(resp.Name.Substring("aux$$assume$$".Length));
reporter.AddCoveredElement(resp.Name.Substring("aux$$assume$$".Length));
}
}

foreach (var arg in resp.Arguments)
{
usedNamedAssumes.Add(arg.Name);
if (libOptions.PrintNecessaryAssumes)
if (libOptions.TrackVerificationCoverage)
{
reporter.AddNecessaryAssume(arg.Name.Substring("aux$$assume$$".Length));
reporter.AddCoveredElement(arg.Name.Substring("aux$$assume$$".Length));
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions Source/Provers/SMTLib/SMTLibLineariser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ public string ExtractBuiltin(Function f)
{
Contract.Requires(f != null);
string retVal = null;
retVal = f.FindStringAttribute("bvbuiltin");
retVal = (f as ICarriesAttributes).FindStringAttribute("bvbuiltin");

// It used to be "sign_extend 12" in Simplify, and is "(_ sign_extend 12)" with SMT
if (retVal != null && (retVal.StartsWith("sign_extend ") || retVal.StartsWith("zero_extend ")))
Expand All @@ -249,7 +249,7 @@ public string ExtractBuiltin(Function f)

if (retVal == null)
{
retVal = f.FindStringAttribute("builtin");
retVal = (f as ICarriesAttributes).FindStringAttribute("builtin");
}

if (retVal != null && !LibOptions.UseArrayTheory && SMTLibOpLineariser.ArrayOps.Contains(retVal))
Expand Down Expand Up @@ -398,7 +398,7 @@ public bool Visit(VCExprNAry node, LineariserOptions options)
return true;
}

if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp))
if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp) || node.Op.Equals(VCExpressionGenerator.NamedAssertOp))
keyboardDrummer marked this conversation as resolved.
Show resolved Hide resolved
{
var exprVar = node[0] as VCExprVar;
NamedAssumes.Add(exprVar);
Expand Down
2 changes: 1 addition & 1 deletion Source/Provers/SMTLib/SMTLibOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface SMTLibOptions : CoreOptions
bool ProduceUnsatCores { get; }
bool ImmediatelyAcceptCommands { get; }
bool RunningBoogieFromCommandLine { get; }
bool PrintNecessaryAssumes { get; }
bool TrackVerificationCoverage { get; }
string ProverPreamble { get; }
bool TraceDiagnosticsOnTimeout { get; }
uint TimeLimitPerAssertionInPercent { get; }
Expand Down
7 changes: 3 additions & 4 deletions Source/Provers/SMTLib/TypeDeclCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ public override bool Visit(VCExprNAry node, bool arg)
AddDeclaration(string.Format("(declare-fun {0} () Bool)", exprVar.Name));
AddDeclaration(string.Format("(assert-soft {0} :weight {1})", exprVar.Name, ((VCExprSoftOp) node.Op).Weight));
}
else if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp))
else if (node.Op.Equals(VCExpressionGenerator.NamedAssumeOp) || node.Op.Equals(VCExpressionGenerator.NamedAssertOp))
{
var exprVar = node[0] as VCExprVar;
AddDeclaration(string.Format("(declare-fun {0} () Bool)", exprVar.Name));
if (options.PrintNecessaryAssumes)
if (options.TrackVerificationCoverage)
{
AddDeclaration(string.Format("(assert (! {0} :named {1}))", exprVar.Name, "aux$$" + exprVar.Name));
}
Expand Down Expand Up @@ -270,8 +270,7 @@ public override bool Visit(VCExprVar node, bool arg)
RegisterType(node.Type);
string decl =
"(declare-fun " + printedName + " () " + TypeToString(node.Type) + ")";
if (!(printedName.StartsWith("assume$$") || printedName.StartsWith("soft$$") ||
printedName.StartsWith("try$$")))
if (node.VarKind == VCExprVarKind.Normal)
{
AddDeclaration(decl);
}
Expand Down
3 changes: 2 additions & 1 deletion Source/VCExpr/QuantifierInstantiationEngine.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
Expand Down Expand Up @@ -50,7 +51,7 @@ public QuantifierInstantiationInfo(Dictionary<VCExprVar, HashSet<string>> boundV
private string skolemConstantNamePrefix;
internal VCExpressionGenerator vcExprGen;
private Boogie2VCExprTranslator exprTranslator;
internal static Dictionary<string, Type> labelToType = new();
internal static ConcurrentDictionary<string, Type> labelToType = new();

public static VCExpr Instantiate(Implementation impl, VCExpressionGenerator vcExprGen, Boogie2VCExprTranslator exprTranslator, VCExpr vcExpr)
{
Expand Down
Loading