Skip to content

Commit

Permalink
Run integration tests in single process (dafny-lang#3715)
Browse files Browse the repository at this point in the history
### Changes

- `dafny run --target=cs` now uses a separate process to run, so the
statics of the compiled source code, such as `Console`, are not shared
with the Dafny Compiler
- Uses of `Console.` are replaced with non-static references.
- Move `DafnyFile` class into its own file
- Remove `ThreadTaskScheduler` which creates one-shot threads, and
instead use a thread pool. Also consistently use large threads for
resolution, translation to Boogie, printing and compilation.
- Made counter example IDE support thread-safe
- The TestDafny project now uses the new CLI UI

<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>
  • Loading branch information
keyboardDrummer authored May 11, 2023
1 parent b592aa7 commit 47056fd
Show file tree
Hide file tree
Showing 173 changed files with 1,765 additions and 945 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check-deep-tests-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v3
with:
path: dafny
submodules: true
submodules: recursive
- uses: actions/github-script@v6
if: github.repository_owner == 'dafny-lang'
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/deep-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
uses: actions/checkout@v3
with:
ref: ${{ inputs.sha }}
submodules: recursive

- name: Get short sha
run: echo "sha_short=`git rev-parse --short HEAD`" >> $GITHUB_ENV
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integration-tests-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ jobs:
uses: actions/checkout@v3
with:
path: dafny
submodules: false # Until the libraries work again
submodules: true # Until the libraries work again
# - name: Clean up libraries for testing
# run: |
# rm dafny/Test/libraries/lit.site.cfg # we remove the lit configuration file in the library repo (a git submodule) to prevent override
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/msbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
uses: actions/checkout@v3
with:
path: dafny
submodules: recursive
- name: Restore tools
working-directory: dafny
run: dotnet tool restore
Expand Down Expand Up @@ -86,6 +87,7 @@ jobs:
uses: actions/checkout@v3
with:
path: dafny
submodules: recursive

- name: Setup dotnet
uses: actions/setup-dotnet@v3
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/runtime-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
steps:
- name: Checkout Dafny
uses: actions/checkout@v3
with:
submodules: true
- name: Set up JDK 18
uses: actions/setup-java@v3
with:
Expand Down
8 changes: 4 additions & 4 deletions Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public override Expression CloneExpr(Expression expr) {
var e = (FunctionCallExpr)expr;
#if DEBUG_PRINT
if (e.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => e.Function.Name == p.Name + "#")) {
Console.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(e));
Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(e));
}
#endif
// Note, we don't actually ever get here, because all calls will have been parsed as ApplySuffix.
Expand All @@ -37,7 +37,7 @@ public override Expression CloneExpr(Expression expr) {
if (f != null && focalPredicates.Contains(f)) {
#if DEBUG_PRINT
var r = CloneCallAndAddK(e);
Console.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(r));
Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(r));
return r;
#else
return CloneCallAndAddK(e);
Expand All @@ -58,14 +58,14 @@ public override Expression CloneExpr(Expression expr) {
if (fce != null) {
#if DEBUG_PRINT
if (fce.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => fce.Function.Name == p.Name + "#")) {
Console.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(fce));
Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(fce));
}
#endif
var f = fce.Function as ExtremePredicate;
if (f != null && focalPredicates.Contains(f)) {
#if DEBUG_PRINT
var r = CloneCallAndAddK(fce);
Console.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(r));
Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(r));
return r;
#else
return CloneCallAndAddK(fce);
Expand Down
4 changes: 4 additions & 0 deletions Source/DafnyCore/AST/Grammar/Printer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ public static string ToStringWithoutNewline(System.IO.StringWriter wr) {
return sb.ToString(0, len);
}

public void PrintProgramLargeStack(Program prog, bool afterResolver) {
DafnyMain.LargeStackFactory.StartNew(() => PrintProgram(prog, afterResolver)).Wait();
}

public void PrintProgram(Program prog, bool afterResolver) {
Contract.Requires(prog != null);
this.afterResolver = afterResolver;
Expand Down
4 changes: 2 additions & 2 deletions Source/DafnyCore/AST/Types/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ public static Type Join(Type a, Type b, BuiltIns builtIns) {
Contract.Requires(builtIns != null);
var j = JoinX(a, b, builtIns);
if (builtIns.Options.Get(CommonOptionBag.TypeInferenceDebug)) {
Console.WriteLine("DEBUG: Join( {0}, {1} ) = {2}", a, b, j);
builtIns.Options.OutputWriter.WriteLine("DEBUG: Join( {0}, {1} ) = {2}", a, b, j);
}
return j;
}
Expand Down Expand Up @@ -1506,7 +1506,7 @@ public static Type Meet(Type a, Type b, BuiltIns builtIns) {
}
}
if (builtIns.Options.Get(CommonOptionBag.TypeInferenceDebug)) {
Console.WriteLine("DEBUG: Meet( {0}, {1} ) = {2}", a, b, j);
builtIns.Options.OutputWriter.WriteLine("DEBUG: Meet( {0}, {1} ) = {2}", a, b, j);
}
return j;
}
Expand Down
2 changes: 1 addition & 1 deletion Source/DafnyCore/Auditor/Auditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ internal override void PostResolve(Program program) {
_ => $"Internal error: unknown format {reportFormat}"
};
if (reportFileName is null) {
Console.Write(text);
Options.OutputWriter.Write(text);
} else {
if (compareReport) {
try {
Expand Down
2 changes: 1 addition & 1 deletion Source/DafnyCore/Compilers/CSharp/Compiler-Csharp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ void EmitDafnySourceAttribute(Program program, ConcreteSyntaxTree wr) {
stringWriter.NewLine = Environment.NewLine;
var oldValue = Options.ShowEnv;
Options.ShowEnv = ExecutionEngineOptions.ShowEnvironment.DuringPrint;
new Printer(stringWriter, Options, PrintModes.Serialization).PrintProgram(program, true);
new Printer(stringWriter, Options, PrintModes.Serialization).PrintProgramLargeStack(program, true);
Options.ShowEnv = oldValue;
var programString = stringWriter.GetStringBuilder().Replace("\"", "\"\"").ToString();

Expand Down
101 changes: 37 additions & 64 deletions Source/DafnyCore/Compilers/CSharp/CsharpBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Text.Json;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand Down Expand Up @@ -44,7 +45,6 @@ public override bool CompileTargetProgram(string dafnyProgramName, string target
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Assembly.Load("mscorlib").Location));

var inMemory = runAfterCompile;
compilation = compilation.WithOptions(compilation.Options.WithOutputKind(callToMain != null ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary));

var tempCompilationResult = new CSharpCompilationResult();
Expand Down Expand Up @@ -93,54 +93,40 @@ public override bool CompileTargetProgram(string dafnyProgramName, string target
var outputDir = targetFilename == null ? Directory.GetCurrentDirectory() : Path.GetDirectoryName(Path.GetFullPath(targetFilename));
var outputPath = Path.Join(outputDir, Path.GetFileNameWithoutExtension(Path.GetFileName(dafnyProgramName)) + ".dll");
var outputJson = Path.Join(outputDir, Path.GetFileNameWithoutExtension(Path.GetFileName(dafnyProgramName)) + ".runtimeconfig.json");
if (inMemory) {
using var stream = new MemoryStream();
var emitResult = compilation.Emit(stream);
if (emitResult.Success) {
tempCompilationResult.CompiledAssembly = Assembly.Load(stream.GetBuffer());
} else {
outputWriter.WriteLine("Errors compiling program:");
var errors = emitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).ToList();
foreach (var ce in errors) {
outputWriter.WriteLine(ce.ToString());
outputWriter.WriteLine();
}
return false;
var emitResult = compilation.Emit(outputPath);

if (emitResult.Success) {
tempCompilationResult.CompiledAssembly = Assembly.LoadFile(outputPath);
if (Options.CompileVerbose) {
outputWriter.WriteLine("Compiled assembly into {0}.dll", compilation.AssemblyName);
}
} else {
var emitResult = compilation.Emit(outputPath);

if (emitResult.Success) {
tempCompilationResult.CompiledAssembly = Assembly.LoadFile(outputPath);
if (Options.CompileVerbose) {
outputWriter.WriteLine("Compiled assembly into {0}.dll", compilation.AssemblyName);
}
try {
var configuration = JsonSerializer.Serialize(
new {
runtimeOptions = new {
tfm = "net6.0",
framework = new {
name = "Microsoft.NETCore.App",
version = "6.0.0",
rollForward = "LatestMinor"
}
try {
var configuration = JsonSerializer.Serialize(
new {
runtimeOptions = new {
tfm = "net6.0",
framework = new {
name = "Microsoft.NETCore.App",
version = "6.0.0",
rollForward = "LatestMinor"
}
}, new JsonSerializerOptions() { WriteIndented = true });
File.WriteAllText(outputJson, configuration + Environment.NewLine);
} catch (Exception e) {
outputWriter.WriteLine($"Error trying to write '{outputJson}': {e.Message}");
return false;
}
} else {
outputWriter.WriteLine("Errors compiling program into {0}", compilation.AssemblyName);
var errors = emitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).ToList();
foreach (var ce in errors) {
outputWriter.WriteLine(ce.ToString());
outputWriter.WriteLine();
}
}
}, new JsonSerializerOptions() { WriteIndented = true });
File.WriteAllText(outputJson, configuration + Environment.NewLine);
} catch (Exception e) {
outputWriter.WriteLine($"Error trying to write '{outputJson}': {e.Message}");
return false;
}
} else {
outputWriter.WriteLine("Errors compiling program into {0}", compilation.AssemblyName);
var errors = emitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).ToList();
foreach (var ce in errors) {
outputWriter.WriteLine(ce.ToString());
outputWriter.WriteLine();
}

return false;
}

compilationResult = tempCompilationResult;
Expand All @@ -151,37 +137,24 @@ private class CSharpCompilationResult {
public Assembly CompiledAssembly;
}

public override bool RunTargetProgram(string dafnyProgramName, string targetProgramText, string callToMain, string/*?*/ targetFilename, ReadOnlyCollection<string> otherFileNames,
object compilationResult, TextWriter outputWriter) {
public override bool RunTargetProgram(string dafnyProgramName, string targetProgramText, string callToMain,
string targetFilename /*?*/, ReadOnlyCollection<string> otherFileNames,
object compilationResult, TextWriter outputWriter, TextWriter errorWriter) {

var crx = (CSharpCompilationResult)compilationResult;

foreach (var otherFileName in otherFileNames) {
if (Path.GetExtension(otherFileName) == ".dll") {
AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(otherFileName));
var targetDirectory = Path.GetDirectoryName(crx.CompiledAssembly.Location);
File.Copy(otherFileName, Path.Combine(targetDirectory!, Path.GetFileName(otherFileName)), true);
}
}

if (crx.CompiledAssembly == null) {
throw new Exception("Cannot call run target program on a compilation that failed");
}
var entry = crx.CompiledAssembly.EntryPoint;
if (entry == null) {
throw new Exception("Cannot call run target on a compilation whose assembly has no entry.");
}
try {
Console.OutputEncoding = System.Text.Encoding.UTF8; // Force UTF-8 output in dafny run (#2999)
object[] parameters = entry.GetParameters().Length == 0 ? new object[] { } : new object[] { Options.MainArgs.ToArray() };
entry.Invoke(null, parameters);
return true;
} catch (System.Reflection.TargetInvocationException e) {
outputWriter.WriteLine("Error: Execution resulted in exception: {0}", e.Message);
outputWriter.WriteLine(e.InnerException.ToString());
} catch (System.Exception e) {
outputWriter.WriteLine("Error: Execution resulted in exception: {0}", e.Message);
outputWriter.WriteLine(e.ToString());
}
return false;
var psi = PrepareProcessStartInfo("dotnet", new[] { crx.CompiledAssembly.Location }.Concat(Options.MainArgs));
return RunProcess(psi, outputWriter, errorWriter) == 0;
}

public CsharpBackend(DafnyOptions options) : base(options) {
Expand Down
4 changes: 2 additions & 2 deletions Source/DafnyCore/Compilers/CoverageInstrumenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public void WriteLegendFile() {
if (legend != null) {
var filename = compiler.Options.CoverageLegendFile;
Contract.Assert(filename != null);
TextWriter wr = filename == "-" ? System.Console.Out : new StreamWriter(new FileStream(Path.GetFullPath(filename), System.IO.FileMode.Create));
TextWriter wr = filename == "-" ? compiler.Options.OutputWriter : new StreamWriter(new FileStream(Path.GetFullPath(filename), System.IO.FileMode.Create));
{
for (var i = 0; i < legend.Count; i++) {
var e = legend[i];
Expand All @@ -82,4 +82,4 @@ public void WriteLegendFile() {
legend = null;
}
}
}
}
2 changes: 1 addition & 1 deletion Source/DafnyCore/Compilers/Cplusplus/Compiler-cpp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ protected override void EmitJumpToTailCallStart(ConcreteSyntaxTree wr) {
}

protected void Warn(string msg, IToken tok) {
Console.Error.WriteLine("WARNING: {3} ({0}:{1}:{2})", tok.Filepath, tok.line, tok.col, msg);
Options.ErrorWriter.WriteLine("WARNING: {3} ({0}:{1}:{2})", tok.Filepath, tok.line, tok.col, msg);
}

// Because we use reference counting (via shared_ptr), the TypeName of a class differs
Expand Down
11 changes: 6 additions & 5 deletions Source/DafnyCore/Compilers/Cplusplus/CppCompilerBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public override bool CompileTargetProgram(string dafnyProgramName, string target
bool runAfterCompile, TextWriter outputWriter, out object compilationResult) {
var assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
Contract.Assert(assemblyLocation != null);
var codebase = System.IO.Path.GetDirectoryName(assemblyLocation);
var codebase = Path.GetDirectoryName(assemblyLocation);
Contract.Assert(codebase != null);
compilationResult = null;
var psi = PrepareProcessStartInfo("g++", new List<string> {
Expand All @@ -37,13 +37,14 @@ public override bool CompileTargetProgram(string dafnyProgramName, string target
"-o", ComputeExeName(targetFilename),
targetFilename
});
return 0 == RunProcess(psi, outputWriter, "Error while compiling C++ files.");
return 0 == RunProcess(psi, outputWriter, outputWriter, "Error while compiling C++ files.");
}

public override bool RunTargetProgram(string dafnyProgramName, string targetProgramText, string/*?*/ callToMain, string targetFilename, ReadOnlyCollection<string> otherFileNames,
object compilationResult, TextWriter outputWriter) {
public override bool RunTargetProgram(string dafnyProgramName, string targetProgramText, string callToMain /*?*/,
string targetFilename, ReadOnlyCollection<string> otherFileNames,
object compilationResult, TextWriter outputWriter, TextWriter errorWriter) {
var psi = PrepareProcessStartInfo(ComputeExeName(targetFilename), Options.MainArgs);
return 0 == RunProcess(psi, outputWriter);
return 0 == RunProcess(psi, outputWriter, errorWriter);
}

public override IReadOnlySet<string> SupportedExtensions => new HashSet<string> { ".h" };
Expand Down
9 changes: 5 additions & 4 deletions Source/DafnyCore/Compilers/Dafny/DafnyBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ public override bool CompileTargetProgram(string dafnyProgramName, string target
* The Dafny backend is different from the other backends in that the output code needs to be compiled
* by the Dafny compiler itself to another backend for execution.
*/
public override bool RunTargetProgram(string dafnyProgramName, string targetProgramText, string /*?*/ callToMain,
string targetFilename, ReadOnlyCollection<string> otherFileNames, object compilationResult, TextWriter outputWriter) {
public override bool RunTargetProgram(string dafnyProgramName, string targetProgramText, string callToMain, /*?*/
string targetFilename, ReadOnlyCollection<string> otherFileNames, object compilationResult, TextWriter outputWriter,
TextWriter errorWriter) {
Contract.Requires(targetFilename != null || otherFileNames.Count == 0);

return RunTargetDafnyProgram(targetFilename, outputWriter, false);
return RunTargetDafnyProgram(targetFilename, outputWriter, errorWriter, false);
}

public DafnyBackend(DafnyOptions options) : base(options) {
}
}
}
Loading

0 comments on commit 47056fd

Please sign in to comment.