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

feat: Dafny Main method to accept optional seq<string> argument #2594

Merged
merged 43 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
dd08b02
MVP for compile arguments
MikaelMayer Aug 16, 2022
9f49d95
Multiple /arg instead of /mainArgs
MikaelMayer Aug 16, 2022
bf5b81c
Works for Java as well
MikaelMayer Aug 16, 2022
9413f53
Support for compiled tests
MikaelMayer Aug 16, 2022
7e99661
Documentation options
MikaelMayer Aug 16, 2022
c53df3d
Support for js
MikaelMayer Aug 16, 2022
b308899
Support for go with arguments
MikaelMayer Aug 17, 2022
aa57f69
Tested the first failing error message
MikaelMayer Aug 17, 2022
e86a74a
Second test for checking failure
MikaelMayer Aug 17, 2022
e159db4
Support for no-arg main
MikaelMayer Aug 17, 2022
37a3e81
Works for c#, C++, Java, Go
MikaelMayer Aug 23, 2022
b080587
Fixed support for C++
MikaelMayer Aug 23, 2022
af0f559
Count the first argument
MikaelMayer Aug 23, 2022
3f081e5
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 23, 2022
b28e077
Fixed the test for main methods everywhere + documentation.
MikaelMayer Aug 23, 2022
ab3e0a0
Merge branch 'feat-compile-arguments-main' of https://github.com/dafn…
MikaelMayer Aug 23, 2022
eef5c3f
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 23, 2022
3089663
Fixed branch coverage.
MikaelMayer Aug 23, 2022
1805a3d
Fixed hopefully the last test
MikaelMayer Aug 23, 2022
652df6e
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 23, 2022
1d9066f
New way of entering arguments with '--'
MikaelMayer Aug 24, 2022
803fda9
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 24, 2022
e00ef86
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 25, 2022
f79ade5
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 25, 2022
62a6cc8
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 26, 2022
b1364a1
Use of "--args" instead of "--"
MikaelMayer Aug 26, 2022
e69c668
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 26, 2022
ebf8dcb
Update RELEASE_NOTES.md
MikaelMayer Aug 26, 2022
0ca5a2f
Moved the code to transform arguments into the DafnyRuntime.
MikaelMayer Aug 29, 2022
bc7ab88
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 29, 2022
722824d
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 30, 2022
2472b61
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 30, 2022
e748bf1
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 31, 2022
fbfc0ca
Arguments fixed
MikaelMayer Aug 31, 2022
c784ad9
Merge branch 'feat-compile-arguments-main' of https://github.com/dafn…
MikaelMayer Aug 31, 2022
955539b
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Aug 31, 2022
34ff94a
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Sep 1, 2022
a6668e1
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Sep 1, 2022
1ca45b8
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Sep 2, 2022
440f0a5
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Sep 2, 2022
c0cfb9e
Fixed verb handling in Go
MikaelMayer Sep 2, 2022
189aafe
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Sep 2, 2022
6348703
Merge branch 'master' into feat-compile-arguments-main
MikaelMayer Sep 6, 2022
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
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Upcoming

- feat: Command-line arguments are now available from `Main` in Dafny programs, using `Main(args: seq<string>)` (https://github.com/dafny-lang/dafny/pull/2594)
- fix: Missing related position in failing precondition (https://github.com/dafny-lang/dafny/pull/2658)
- fix: No more IDE crashing on the elephant operator (https://github.com/dafny-lang/dafny/pull/2668)
- feat: generate warning when 'old' has no effect


# 3.8.1

- feat: Support for the `{:opaque}` attibute on `const` (https://github.com/dafny-lang/dafny/pull/2545)
Expand Down
8 changes: 4 additions & 4 deletions Source/Dafny/Compilers/Compiler-Csharp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ private void EmitInitNewArrays(BuiltIns builtIns, ConcreteSyntaxTree dafnyNamesp
}
}

protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw) {
protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw, string argsParameterName) {
var wr = ((ClassWriter)cw).StaticMemberWriter;
// See EmitCallToMain() - this is named differently because otherwise C# tries
// to resolve the reference to the instance-level Main method
return wr.NewBlock("public static void _StaticMain()");
return wr.NewBlock($"public static void _StaticMain(Dafny.ISequence<Dafny.ISequence<char>> {argsParameterName})");
}

protected override ConcreteSyntaxTree CreateModule(string moduleName, bool isDefault, bool isExtern, string /*?*/ libraryName, ConcreteSyntaxTree wr) {
Expand Down Expand Up @@ -3270,7 +3270,7 @@ public override bool RunTargetProgram(string dafnyProgramName, string targetProg
throw new Exception("Cannot call run target on a compilation whose assembly has no entry.");
}
try {
object[] parameters = entry.GetParameters().Length == 0 ? new object[] { } : new object[] { new string[0] };
object[] parameters = entry.GetParameters().Length == 0 ? new object[] { } : new object[] { DafnyOptions.O.MainArgs.ToArray() };
entry.Invoke(null, parameters);
return true;
} catch (System.Reflection.TargetInvocationException e) {
Expand Down Expand Up @@ -3325,7 +3325,7 @@ public override void EmitCallToMain(Method mainMethod, string baseName, Concrete
var idName = IssueCreateStaticMain(mainMethod) ? "_StaticMain" : IdName(mainMethod);

Coverage.EmitSetup(wBody);
wBody.WriteLine($"{GetHelperModuleName()}.WithHaltHandling({companion}.{idName});");
wBody.WriteLine($"{GetHelperModuleName()}.WithHaltHandling(() => {companion}.{idName}(Dafny.Sequence<Dafny.ISequence<char>>.FromMainArguments(args)));");
Coverage.EmitTearDown(wBody);
}

Expand Down
21 changes: 15 additions & 6 deletions Source/Dafny/Compilers/Compiler-cpp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,16 @@ protected override void EmitFooter(Program program, ConcreteSyntaxTree wr) {
}

public override void EmitCallToMain(Method mainMethod, string baseName, ConcreteSyntaxTree wr) {
var w = wr.NewBlock("int main()");
var w = wr.NewBlock("int main(int argc, char *argv[])");
var tryWr = w.NewBlock("try");
tryWr.WriteLine(string.Format("{0}::{1}::{2}();", mainMethod.EnclosingClass.EnclosingModuleDefinition.CompileName, mainMethod.EnclosingClass.CompileName, mainMethod.Name));
tryWr.WriteLine(string.Format("{0}::{1}::{2}(dafny_get_args(argc, argv));", mainMethod.EnclosingClass.EnclosingModuleDefinition.CompileName, mainMethod.EnclosingClass.CompileName, mainMethod.Name));
var catchWr = w.NewBlock("catch (DafnyHaltException & e)");
catchWr.WriteLine("std::cout << \"Program halted: \" << e.what() << std::endl;");
}

protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw) {
protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw, string argsParameterName) {
var wr = (cw as CppCompiler.ClassWriter).MethodWriter;
return wr.NewBlock("int main()");
return wr.NewBlock($"int main(DafnySequence<DafnySequence<char>> {argsParameterName})");
}

protected override ConcreteSyntaxTree CreateModule(string moduleName, bool isDefault, bool isExtern, string/*?*/ libraryName, ConcreteSyntaxTree wr) {
Expand Down Expand Up @@ -1146,7 +1146,12 @@ protected void DeclareField(string className, List<TypeParameter> targs, string

private string DeclareFormalString(string prefix, string name, Type type, IToken tok, bool isInParam) {
if (isInParam) {
return String.Format("{0}{2} {1}", prefix, name, TypeName(type, null, tok));
var result = String.Format("{0}{2} {1}", prefix, name, TypeName(type, null, tok));
if (name == "__noArgsParameter") {
result += " __attribute__((unused))";
}

return result;
} else {
return null;
}
Expand Down Expand Up @@ -2500,8 +2505,12 @@ public override bool RunTargetProgram(string dafnyProgramName, string targetProg
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardError = true
};
foreach (var arg in DafnyOptions.O.MainArgs) {
psi.ArgumentList.Add(arg);
}

var proc = Process.Start(psi);
while (!proc.StandardOutput.EndOfStream) {
outputWriter.WriteLine(proc.StandardOutput.ReadLine());
Expand Down
21 changes: 15 additions & 6 deletions Source/Dafny/Compilers/Compiler-go.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ static string FormatDefaultTypeParameterValue(TopLevelDecl tp) {
private static List<Import> StandardImports =
new List<Import> {
new Import { Name = "_dafny", Path = "dafny" },
new Import { Name = "os", Path = "os" },
};
private static string DummyTypeName = "Dummy__";

Expand Down Expand Up @@ -118,7 +119,7 @@ public override void EmitCallToMain(Method mainMethod, string baseName, Concrete
var idName = IssueCreateStaticMain(mainMethod) ? "Main" : IdName(mainMethod);

Coverage.EmitSetup(wBody);
wBody.WriteLine("{0}.{1}()", companion, idName);
wBody.WriteLine("{0}.{1}({2}.FromMainArguments(os.Args))", companion, idName, GetHelperModuleName());
Coverage.EmitTearDown(wBody);
}

Expand All @@ -130,9 +131,9 @@ ConcreteSyntaxTree CreateDescribedSection(string desc, ConcreteSyntaxTree wr, pa
return body;
}

protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw) {
protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw, string argsParameterName) {
var wr = ((GoCompiler.ClassWriter)cw).ConcreteMethodWriter;
return wr.NewNamedBlock("func (_this * {0}) Main()", FormatCompanionTypeName(((GoCompiler.ClassWriter)cw).ClassName));
return wr.NewNamedBlock("func (_this * {0}) Main({1} _dafny.Seq)", FormatCompanionTypeName(((GoCompiler.ClassWriter)cw).ClassName), argsParameterName);
}

protected override ConcreteSyntaxTree CreateModule(string moduleName, bool isDefault, bool isExtern, string/*?*/ libraryName, ConcreteSyntaxTree wr) {
Expand Down Expand Up @@ -196,7 +197,11 @@ private void EmitImport(Import import, ConcreteSyntaxTree importWriter, Concrete
importWriter.WriteLine("{0} \"{1}\"", id, path);

if (!import.SuppressDummy) {
importDummyWriter.WriteLine("var _ {0}.{1}", id, DummyTypeName);
if (id == "os") {
importDummyWriter.WriteLine("var _ = os.Args");
} else {
importDummyWriter.WriteLine("var _ {0}.{1}", id, DummyTypeName);
}
Copy link
Member

Choose a reason for hiding this comment

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

This looks odd — what happens if someone has a Dafny module called os?

Copy link
Member Author

Choose a reason for hiding this comment

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

Then there will be a compilation failure. What would be the alternative?

Copy link
Member

Choose a reason for hiding this comment

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

A clean error produced by Dafny; but what is bothering me is the special case on the string os. We could store tuples in the list of modules (maybe ("os", "Args") and (id, DummyTypeName))

Copy link
Member Author

@MikaelMayer MikaelMayer Aug 30, 2022

Choose a reason for hiding this comment

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

What do you mean by "we could store tuples in the list of modules"?
One could make this error go away by suppressing these dummy calls.
But overall, I think that if this is a problem down the road, the Go compiler will have to compile a Dafny module named "os" to something else, since "os" is a base package in Go anyway.

}
}

Expand Down Expand Up @@ -3603,14 +3608,18 @@ private bool SendToNewGoProcess(string dafnyProgramName, string targetFilename,
verb = string.Format("build -o \"{0}\"", output);
}

var args = string.Format("{0} \"{1}\"", verb, targetFilename);
var psi = new ProcessStartInfo("go", args) {
var psi = new ProcessStartInfo("go") {
CreateNoWindow = Environment.OSVersion.Platform != PlatformID.Win32NT,
UseShellExecute = false,
RedirectStandardInput = false,
RedirectStandardOutput = false,
RedirectStandardError = false,
};
psi.ArgumentList.Add(verb);
psi.ArgumentList.Add(targetFilename);
foreach (var arg in DafnyOptions.O.MainArgs) {
psi.ArgumentList.Add(arg);
}
psi.EnvironmentVariables["GOPATH"] = GoPath(targetFilename);
// Dafny compiles to the old Go package system, whereas Go has moved on to a module
// system. Until Dafny's Go compiler catches up, the GO111MODULE variable has to be set.
Expand Down
12 changes: 8 additions & 4 deletions Source/Dafny/Compilers/Compiler-java.cs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ public override void EmitCallToMain(Method mainMethod, string baseName, Concrete
var modName = mainMethod.EnclosingClass.EnclosingModuleDefinition.CompileName == "_module" ? "_System." : "";
companion = modName + companion;
Coverage.EmitSetup(wBody);
wBody.WriteLine($"{DafnyHelpersClass}.withHaltHandling({companion}::__Main);");
wBody.WriteLine($"{DafnyHelpersClass}.withHaltHandling(() -> {{ {companion}.__Main({DafnyHelpersClass}.FromMainArguments(args)); }} );");
Coverage.EmitTearDown(wBody);
}

Expand Down Expand Up @@ -2278,13 +2278,17 @@ public override bool CompileTargetProgram(string dafnyProgramName, string target

public override bool RunTargetProgram(string dafnyProgramName, string targetProgramText, string callToMain, string /*?*/ targetFilename,
ReadOnlyCollection<string> otherFileNames, object compilationResult, TextWriter outputWriter) {
var psi = new ProcessStartInfo("java", Path.GetFileNameWithoutExtension(targetFilename)) {
var psi = new ProcessStartInfo("java") {
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDirectory = Path.GetFullPath(Path.GetDirectoryName(targetFilename))
};
psi.ArgumentList.Add(Path.GetFileNameWithoutExtension(targetFilename));
foreach (var arg in DafnyOptions.O.MainArgs) {
psi.ArgumentList.Add(arg);
}
psi.EnvironmentVariables["CLASSPATH"] = GetClassPath(targetFilename);
var proc = Process.Start(psi);
while (!proc.StandardOutput.EndOfStream) {
Expand Down Expand Up @@ -3995,9 +3999,9 @@ protected override void EmitTypeTest(string localName, Type fromType, Type toTyp
protected override bool IssueCreateStaticMain(Method m) {
return true;
}
protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw) {
protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw, string argsParameterName) {
var wr = ((ClassWriter)cw).StaticMemberWriter;
return wr.NewBlock("public static void __Main()");
return wr.NewBlock($"public static void __Main(dafny.DafnySequence<? extends dafny.DafnySequence<? extends Character>> {argsParameterName})");
}

protected override void CreateIIFE(string bvName, Type bvType, IToken bvTok, Type bodyType, IToken bodyTok,
Expand Down
13 changes: 10 additions & 3 deletions Source/Dafny/Compilers/Compiler-js.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ protected override void EmitHeader(Program program, ConcreteSyntaxTree wr) {

public override void EmitCallToMain(Method mainMethod, string baseName, ConcreteSyntaxTree wr) {
Coverage.EmitSetup(wr);
wr.WriteLine("_dafny.HandleHaltExceptions({0}.{1});", mainMethod.EnclosingClass.FullCompileName, mainMethod.IsStatic ? IdName(mainMethod) : "Main");
wr.WriteLine("_dafny.HandleHaltExceptions(() => {0}.{1}(require('process').argv));", mainMethod.EnclosingClass.FullCompileName, mainMethod.IsStatic ? IdName(mainMethod) : "Main");
Coverage.EmitTearDown(wr);
}

protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw) {
protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw, string argsParameterName) {
var wr = (cw as JavaScriptCompiler.ClassWriter).MethodWriter;
return wr.NewBlock("static Main()");
return wr.NewBlock($"static Main({argsParameterName})");
}

protected override ConcreteSyntaxTree CreateModule(string moduleName, bool isDefault, bool isExtern, string/*?*/ libraryName, ConcreteSyntaxTree wr) {
Expand Down Expand Up @@ -2441,6 +2441,12 @@ public override bool RunTargetProgram(string dafnyProgramName, string targetProg
return SendToNewNodeProcess(dafnyProgramName, targetProgramText, callToMain, targetFilename, otherFileNames, outputWriter);
}

public string ToStringLiteral(string s) {
var wr = new ConcreteSyntaxTree();
EmitStringLiteral(s, false, wr);
return wr.ToString();
}

bool SendToNewNodeProcess(string dafnyProgramName, string targetProgramText, string/*?*/ callToMain, string targetFilename, ReadOnlyCollection<string> otherFileNames,
TextWriter outputWriter) {
Contract.Requires(targetFilename != null || otherFileNames.Count == 0);
Expand All @@ -2461,6 +2467,7 @@ bool SendToNewNodeProcess(string dafnyProgramName, string targetProgramText, str
}
nodeProcess.StandardInput.Write(targetProgramText);
if (callToMain != null && DafnyOptions.O.RunAfterCompile) {
nodeProcess.StandardInput.WriteLine("require('process').argv = [\"node\", " + string.Join(",", DafnyOptions.O.MainArgs.Select(ToStringLiteral)) + "];");
Copy link
Member

Choose a reason for hiding this comment

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

Don't we need Javascript quoting here (instead of Dafny quoting?)

Copy link
Member

Choose a reason for hiding this comment

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

And more importantly, can't we pass these arguments on node's command line directly?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is JavaScript quoting (ToStringLiteral is a compiler to JavaScript function)
More importantly, the problem is that passing anything in command line (e.g. node arg1 arg2) will attempt to load the file, so this is also why the javascript compiler's run method is pushing everything in the standard input.

nodeProcess.StandardInput.Write(callToMain);
}
nodeProcess.StandardInput.Flush();
Expand Down
15 changes: 10 additions & 5 deletions Source/Dafny/Compilers/Compiler-python.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,17 @@ protected override void EmitHeader(Program program, ConcreteSyntaxTree wr) {

public override void EmitCallToMain(Method mainMethod, string baseName, ConcreteSyntaxTree wr) {
Coverage.EmitSetup(wr);
var dafnyArgs = "dafnyArgs";
wr.NewBlockPy("try:")
.WriteLine($"{mainMethod.EnclosingClass.FullCompileName}.{(IssueCreateStaticMain(mainMethod) ? "Main" : IdName(mainMethod))}()");
.WriteLine($"{dafnyArgs} = [{DafnyRuntimeModule}.Seq(a) for a in sys.argv]")
.WriteLine($"{mainMethod.EnclosingClass.FullCompileName}.{(IssueCreateStaticMain(mainMethod) ? "Main" : IdName(mainMethod))}({dafnyArgs})");
wr.NewBlockPy($"except {DafnyRuntimeModule}.HaltException as e:")
.WriteLine($"{DafnyRuntimeModule}.print(\"[Program halted] \" + {DafnyRuntimeModule}.string_of(e.message) + \"\\n\")");
Coverage.EmitTearDown(wr);
}

protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw) {
return ((ClassWriter)cw).MethodWriter.NewBlockPy("def Main():");
protected override ConcreteSyntaxTree CreateStaticMain(IClassWriter cw, string argsParameterName) {
return ((ClassWriter)cw).MethodWriter.NewBlockPy($"def Main({argsParameterName}):");
}

protected override ConcreteSyntaxTree CreateModule(string moduleName, bool isDefault, bool isExtern,
Expand Down Expand Up @@ -1695,14 +1697,17 @@ public override bool CompileTargetProgram(string dafnyProgramName, string target
public override bool RunTargetProgram(string dafnyProgramName, string targetProgramText, string /*?*/ callToMain,
string targetFilename, ReadOnlyCollection<string> otherFileNames, object compilationResult, TextWriter outputWriter) {
Contract.Requires(targetFilename != null || otherFileNames.Count == 0);

var psi = new ProcessStartInfo("python3", targetFilename) {
var psi = new ProcessStartInfo("python3") {
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = false,
RedirectStandardError = false,
};
psi.ArgumentList.Add(targetFilename);
foreach (var arg in DafnyOptions.O.MainArgs) {
psi.ArgumentList.Add(arg);
}
MikaelMayer marked this conversation as resolved.
Show resolved Hide resolved

try {
using var pythonProcess = Process.Start(psi);
Expand Down
Loading