Skip to content

Commit 27297d8

Browse files
davidcokRustanLeinodavidcok
authored
Removing duplicate error messages for multiple Main methods (#1075)
* Removing duplicate error messages for multiple Main * Adding to the test * Update docs/DafnyRef/Programs.md * Embellishing the solution * Edits in response to review * Bug fix * Debugging * Debugging * Fixing for instance main methods * Moving not-yet-working test * Fixing a wishlist test * Additional tests Co-authored-by: Rustan Leino <[email protected]> Co-authored-by: davidcok <[email protected]>
1 parent c2c5c2c commit 27297d8

28 files changed

+713
-78
lines changed

Source/Dafny/Compiler-Csharp.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2914,7 +2914,7 @@ private void AddTestCheckerIfNeeded(string name, Declaration decl, TargetWriter
29142914
}
29152915

29162916
public override void EmitCallToMain(Method mainMethod, string baseName, TargetWriter wr) {
2917-
var companion = TypeName_Companion(mainMethod.EnclosingClass as ClassDecl, wr, mainMethod.tok);
2917+
var companion = TypeName_Companion(mainMethod.EnclosingClass as TopLevelDecl, wr, mainMethod.tok);
29182918
var wClass = wr.NewNamedBlock("class __CallToMain");
29192919
var wBody = wClass.NewNamedBlock("public static void Main(string[] args)");
29202920
var modName = mainMethod.EnclosingClass.EnclosingModuleDefinition.CompileName == "_module" ? "_module." : "";

Source/Dafny/Compiler-go.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private struct Import {
4949
protected override void EmitHeader(Program program, TargetWriter wr) {
5050
wr.WriteLine("// Dafny program {0} compiled into Go", program.Name);
5151

52-
ModuleName = MainModuleName = HasMain(program, out _) ? "main" : Path.GetFileNameWithoutExtension(program.Name);
52+
ModuleName = MainModuleName = program.MainMethod != null ? "main" : Path.GetFileNameWithoutExtension(program.Name);
5353

5454
wr.WriteLine("package {0}", ModuleName);
5555
wr.WriteLine();
@@ -94,7 +94,7 @@ public override void EmitCallToMain(Method mainMethod, string baseName, TargetWr
9494
var wBody = wr.NewNamedBlock("func main()");
9595
wBody.WriteLine("defer _dafny.CatchHalt()");
9696
Coverage.EmitSetup(wBody);
97-
wBody.WriteLine("{0}.{1}()", companion, IdName(mainMethod));
97+
wBody.WriteLine("{0}.{1}()", companion, mainMethod.IsStatic ? IdName(mainMethod) : "Main");
9898
Coverage.EmitTearDown(wBody);
9999
}
100100

Source/Dafny/Compiler-java.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ protected override TargetWriter EmitIngredients(TargetWriter wr, string ingredie
294294

295295
protected override void EmitHeader(Program program, TargetWriter wr){
296296
wr.WriteLine($"// Dafny program {program.Name} compiled into Java");
297-
ModuleName = HasMain(program, out _) ? "main" : Path.GetFileNameWithoutExtension(program.Name);
297+
ModuleName = program.MainMethod != null ? "main" : Path.GetFileNameWithoutExtension(program.Name);
298298
wr.WriteLine();
299299
// Keep the import writers so that we can import subsequent modules into the main one
300300
EmitImports(wr, out RootImportWriter);
@@ -313,7 +313,7 @@ public override void EmitCallToMain(Method mainMethod, string baseName, TargetWr
313313
var modName = mainMethod.EnclosingClass.EnclosingModuleDefinition.CompileName == "_module" ? "_System." : "";
314314
companion = modName + companion;
315315
Coverage.EmitSetup(wBody);
316-
wBody.WriteLine($"{DafnyHelpersClass}.withHaltHandling({companion}::{IdName(mainMethod)});");
316+
wBody.WriteLine($"{DafnyHelpersClass}.withHaltHandling({companion}::__Main);");
317317
Coverage.EmitTearDown(wBody);
318318
}
319319

@@ -3811,9 +3811,12 @@ protected override void EmitConversionExpr(ConversionExpr e, bool inLetExprBody,
38113811
}
38123812
}
38133813

3814+
protected override bool IssueCreateStaticMain(Method m) {
3815+
return true;
3816+
}
38143817
protected override BlockTargetWriter CreateStaticMain(IClassWriter cw) {
38153818
var wr = ((ClassWriter) cw).StaticMemberWriter;
3816-
return wr.NewBlock("public static void Main(string[] args)");
3819+
return wr.NewBlock("public static void __Main()");
38173820
}
38183821

38193822
protected override void CreateIIFE(string bvName, Type bvType, Bpl.IToken bvTok, Type bodyType, Bpl.IToken bodyTok, TargetWriter wr, out TargetWriter wrRhs, out TargetWriter wrBody) {

Source/Dafny/Compiler-js.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ protected override void EmitHeader(Program program, TargetWriter wr) {
3737

3838
public override void EmitCallToMain(Method mainMethod, string baseName, TargetWriter wr) {
3939
Coverage.EmitSetup(wr);
40-
wr.WriteLine("_dafny.HandleHaltExceptions({0}.{1});", mainMethod.EnclosingClass.FullCompileName, IdName(mainMethod));
40+
wr.WriteLine("_dafny.HandleHaltExceptions({0}.{1});", mainMethod.EnclosingClass.FullCompileName, mainMethod.IsStatic ? IdName(mainMethod) : "Main");
4141
Coverage.EmitTearDown(wr);
4242
}
4343

Source/Dafny/Compiler.cs

Lines changed: 140 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public Compiler(ErrorReporter reporter) {
2828
Coverage = new CoverageInstrumenter(this);
2929
}
3030

31+
public static string DefaultNameMain = "Main";
32+
3133
public abstract string TargetLanguage { get; }
3234
protected virtual string ModuleSeparator { get => "."; }
3335
protected virtual string ClassAccessor { get => "."; }
@@ -1156,7 +1158,7 @@ public void Compile(Program program, TargetWriter wrx) {
11561158
var nt = (NewtypeDecl)d;
11571159
var w = DeclareNewtype(nt, wr);
11581160
v.Visit(nt);
1159-
CompileClassMembers(nt, w);
1161+
CompileClassMembers(program, nt, w);
11601162
} else if (d is DatatypeDecl) {
11611163
var dt = (DatatypeDecl)d;
11621164
CheckForCapitalizationConflicts(dt.Ctors);
@@ -1165,7 +1167,7 @@ public void Compile(Program program, TargetWriter wrx) {
11651167
}
11661168
var w = DeclareDatatype(dt, wr);
11671169
if (w != null) {
1168-
CompileClassMembers(dt, w);
1170+
CompileClassMembers(program, dt, w);
11691171
}
11701172
} else if (d is IteratorDecl) {
11711173
var iter = (IteratorDecl)d;
@@ -1183,7 +1185,7 @@ public void Compile(Program program, TargetWriter wrx) {
11831185
} else if (d is TraitDecl trait) {
11841186
// writing the trait
11851187
var w = CreateTrait(trait.CompileName, trait.IsExtern(out _, out _), trait.TypeArgs, trait.ParentTypeInformation.UniqueParentTraits(), trait.tok, wr);
1186-
CompileClassMembers(trait, w);
1188+
CompileClassMembers(program, trait, w);
11871189
} else if (d is ClassDecl cl) {
11881190
var include = true;
11891191
if (cl.IsDefaultClass) {
@@ -1201,12 +1203,12 @@ public void Compile(Program program, TargetWriter wrx) {
12011203
if (include) {
12021204
var cw = CreateClass(IdProtect(d.EnclosingModuleDefinition.CompileName), IdName(cl), classIsExtern, cl.FullName,
12031205
cl.TypeArgs, cl, cl.ParentTypeInformation.UniqueParentTraits(), cl.tok, wr);
1204-
CompileClassMembers(cl, cw);
1206+
CompileClassMembers(program, cl, cw);
12051207
cw.Finish();
12061208
} else {
12071209
// still check that given members satisfy compilation rules
12081210
var abyss = new NullClassWriter();
1209-
CompileClassMembers(cl, abyss);
1211+
CompileClassMembers(program, cl, abyss);
12101212
}
12111213
} else if (d is ValuetypeDecl) {
12121214
// nop
@@ -1335,38 +1337,117 @@ public bool HasMain(Program program, out Method mainMethod) {
13351337
Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn(out mainMethod) != null));
13361338
mainMethod = null;
13371339
bool hasMain = false;
1340+
string name = DafnyOptions.O.MainMethod;
1341+
if (name != null && name == "-") return false;
1342+
if (name != null && name != "") {
1343+
foreach (var module in program.CompileModules) {
1344+
if (module.IsAbstract) {
1345+
// the purpose of an abstract module is to skip compilation
1346+
continue;
1347+
}
1348+
foreach (var decl in module.TopLevelDecls) {
1349+
var c = decl as TopLevelDeclWithMembers;
1350+
if (c != null) {
1351+
foreach (MemberDecl member in c.Members) {
1352+
var m = member as Method;
1353+
if (m == null) continue;
1354+
if (member.FullDafnyName == name) {
1355+
mainMethod = m;
1356+
if (!IsPermittedAsMain(mainMethod, out string reason)) {
1357+
Error(mainMethod.tok, "The method \"{0}\" is not permitted as a main method ({1}).", null, name, reason);
1358+
mainMethod = null;
1359+
return false;
1360+
} else {
1361+
return true;
1362+
}
1363+
}
1364+
}
1365+
}
1366+
}
1367+
}
1368+
Error(program.DefaultModule.tok, "Could not find the method named by the -Main option: {0}", null, name);
1369+
}
13381370
foreach (var module in program.CompileModules) {
13391371
if (module.IsAbstract) {
13401372
// the purpose of an abstract module is to skip compilation
13411373
continue;
13421374
}
13431375
foreach (var decl in module.TopLevelDecls) {
1344-
var c = decl as ClassDecl;
1376+
var c = decl as TopLevelDeclWithMembers;
13451377
if (c != null) {
13461378
foreach (var member in c.Members) {
13471379
var m = member as Method;
1348-
if (m != null && IsMain(m)) {
1380+
if (m != null && Attributes.Contains(m.Attributes, "main")) {
13491381
if (mainMethod == null) {
13501382
mainMethod = m;
13511383
hasMain = true;
13521384
} else {
13531385
// more than one main in the program
1354-
Error(m.tok, "More than one method is declared as \"main\". First declaration appeared at {0}.", null, ErrorReporter.TokenToString(mainMethod.tok));
1386+
Error(m.tok, "More than one method is marked \"{{:main}}\". First declaration appeared at {0}.", null,
1387+
ErrorReporter.TokenToString(mainMethod.tok));
13551388
hasMain = false;
13561389
}
13571390
}
13581391
}
13591392
}
13601393
}
13611394
}
1362-
if (!hasMain) {
1395+
if (hasMain) {
1396+
if (!IsPermittedAsMain(mainMethod, out string reason)) {
1397+
Error(mainMethod.tok, "This method marked \"{{:main}}\" is not permitted as a main method ({0}).", null, reason);
1398+
mainMethod = null;
1399+
return false;
1400+
} else {
1401+
return true;
1402+
}
1403+
}
1404+
if (mainMethod != null) {
1405+
mainMethod = null;
1406+
return false;
1407+
}
1408+
1409+
mainMethod = null;
1410+
foreach (var module in program.CompileModules) {
1411+
if (module.IsAbstract) {
1412+
// the purpose of an abstract module is to skip compilation
1413+
continue;
1414+
}
1415+
foreach (var decl in module.TopLevelDecls) {
1416+
var c = decl as TopLevelDeclWithMembers;
1417+
if (c != null) {
1418+
foreach (var member in c.Members) {
1419+
var m = member as Method;
1420+
if (m != null && m.Name == DefaultNameMain) {
1421+
if (mainMethod == null) {
1422+
mainMethod = m;
1423+
hasMain = true;
1424+
} else {
1425+
// more than one main in the program
1426+
Error(m.tok, "More than one method is declared as \"{0}\". First declaration appeared at {1}.", null,
1427+
DefaultNameMain, ErrorReporter.TokenToString(mainMethod.tok));
1428+
hasMain = false;
1429+
}
1430+
}
1431+
}
1432+
}
1433+
}
1434+
}
1435+
1436+
if (hasMain) {
1437+
if (!IsPermittedAsMain(mainMethod, out string reason)) {
1438+
Error(mainMethod.tok, "This method \"Main\" is not permitted as a main method ({0}).", null, reason);
1439+
return false;
1440+
} else {
1441+
return true;
1442+
}
1443+
} else {
13631444
// make sure "mainMethod" returns as null
13641445
mainMethod = null;
1446+
return false;
13651447
}
1366-
return hasMain;
13671448
}
13681449

1369-
public static bool IsMain(Method m) {
1450+
public static bool IsPermittedAsMain(Method m, out String reason) {
13701451
Contract.Requires(m.EnclosingClass is TopLevelDeclWithMembers);
13711452
// In order to be a legal Main() method, the following must be true:
13721453
// The method is not a ghost method
@@ -1382,18 +1463,45 @@ public static bool IsMain(Method m) {
13821463
// Note, in the case where the method is annotated with {:main}, the method is allowed to have preconditions and modifies clauses.
13831464
// This lets the programmer add some explicit assumptions about the outside world, modeled, for example, via ghost parameters.
13841465
var cl = (TopLevelDeclWithMembers)m.EnclosingClass;
1385-
if (!m.IsGhost && m.TypeArgs.Count == 0 && cl.TypeArgs.Count == 0) {
1386-
if (m.Ins.TrueForAll(f => f.IsGhost) && m.Outs.TrueForAll(f => f.IsGhost)) {
1387-
if (m.IsStatic || (cl is ClassDecl klass && !(klass is TraitDecl) && !klass.HasConstructor)) {
1388-
if (m.Name == "Main" && m.Req.Count == 0 && m.Mod.Expressions.Count == 0) {
1389-
return true;
1390-
} else if (Attributes.Contains(m.Attributes, "main")) {
1391-
return true;
1392-
}
1393-
}
1394-
}
1466+
if (m.IsGhost) {
1467+
reason = "the method is ghost";
1468+
return false;
13951469
}
1396-
return false;
1470+
if (m.TypeArgs.Count != 0) {
1471+
reason = "the method has type parameters";
1472+
return false;
1473+
}
1474+
if (cl.TypeArgs.Count != 0) {
1475+
reason = "the enclosing class has type parameters";
1476+
return false;
1477+
}
1478+
if (!m.IsStatic && !cl.Members.TrueForAll(f => !(f is Constructor))) {
1479+
reason = "the method is not static and the enclosing class has constructors";
1480+
return false;
1481+
}
1482+
if (!m.Ins.TrueForAll(f => f.IsGhost)) {
1483+
reason = "the method has non-ghost parameters";
1484+
return false;
1485+
}
1486+
if (!m.Outs.TrueForAll(f => f.IsGhost)) {
1487+
reason = "the method has non-ghost out parameters";
1488+
return false;
1489+
}
1490+
if (Attributes.Contains(m.Attributes, "main")) {
1491+
reason = "";
1492+
return true;
1493+
}
1494+
if (m.Req.Count != 0)
1495+
{
1496+
reason = "the method has requires clauses";
1497+
return false;
1498+
}
1499+
if (m.Mod.Expressions.Count != 0) {
1500+
reason = "the method has modifies clauses";
1501+
return false;
1502+
}
1503+
reason = "";
1504+
return true;
13971505
}
13981506

13991507
void OrderedBySCC(List<MemberDecl> decls, TopLevelDeclWithMembers c) {
@@ -1426,7 +1534,7 @@ public static bool NeedsCustomReceiver(MemberDecl member) {
14261534
return false;
14271535
}
14281536

1429-
void CompileClassMembers(TopLevelDeclWithMembers c, IClassWriter classWriter) {
1537+
void CompileClassMembers(Program program, TopLevelDeclWithMembers c, IClassWriter classWriter) {
14301538
Contract.Requires(c != null);
14311539
Contract.Requires(classWriter != null);
14321540
Contract.Requires(thisContext == null);
@@ -1641,10 +1749,10 @@ void CompileClassMembers(TopLevelDeclWithMembers c, IClassWriter classWriter) {
16411749
RedeclareInheritedMember(m, classWriter);
16421750
}
16431751
if (m.Body != null) {
1644-
CompileMethod(m, classWriter, true);
1752+
CompileMethod(program, m, classWriter, true);
16451753
}
16461754
} else {
1647-
CompileMethod(m, classWriter, false);
1755+
CompileMethod(program, m, classWriter, false);
16481756
}
16491757
v.Visit(m);
16501758
} else {
@@ -2052,7 +2160,7 @@ private void CompileFunction(Function f, IClassWriter cw, bool lookasideBody) {
20522160
}
20532161
}
20542162

2055-
private void CompileMethod(Method m, IClassWriter cw, bool lookasideBody) {
2163+
private void CompileMethod(Program program, Method m, IClassWriter cw, bool lookasideBody) {
20562164
Contract.Requires(cw != null);
20572165
Contract.Requires(m != null);
20582166
Contract.Requires(m.Body != null || Attributes.Contains(m.Attributes, "dllimport") || (IncludeExternMembers && Attributes.Contains(m.Attributes, "extern")));
@@ -2086,8 +2194,7 @@ private void CompileMethod(Method m, IClassWriter cw, bool lookasideBody) {
20862194
}
20872195
}
20882196

2089-
// allow the Main method to be an instance method
2090-
if (IsMain(m) && (!m.IsStatic || m.CompileName != "Main")) {
2197+
if (m == program.MainMethod && IssueCreateStaticMain(m)) {
20912198
w = CreateStaticMain(cw);
20922199
if (!m.IsStatic) {
20932200
var c = m.EnclosingClass;
@@ -2102,6 +2209,11 @@ private void CompileMethod(Method m, IClassWriter cw, bool lookasideBody) {
21022209
}
21032210
}
21042211

2212+
protected virtual bool IssueCreateStaticMain(Method m) {
2213+
return !m.IsStatic;
2214+
}
2215+
2216+
21052217
void TrCasePatternOpt<VT>(CasePattern<VT> pat, Expression rhs, TargetWriter wr, bool inLetExprBody) where VT: IVariable {
21062218
TrCasePatternOpt(pat, rhs, null, rhs.Type, rhs.tok, wr, inLetExprBody);
21072219
}

0 commit comments

Comments
 (0)