summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorGravatar Rustan Leino <leino@microsoft.com>2011-05-19 11:53:56 -0700
committerGravatar Rustan Leino <leino@microsoft.com>2011-05-19 11:53:56 -0700
commitde0a78a69aac57b2189caf7d8490975f25aea91a (patch)
treec94831db7e3d99c333afe55d5d586b8147dace31 /Source
parentb665a914c81085cf3ac4e97ea3c73673cfe8ca4b (diff)
Dafny: let verifier, not the resolver, check for missing cases in match expressions/statements
Diffstat (limited to 'Source')
-rw-r--r--Source/Dafny/Compiler.cs27
-rw-r--r--Source/Dafny/DafnyAst.cs5
-rw-r--r--Source/Dafny/Resolver.cs22
-rw-r--r--Source/Dafny/Translator.cs76
4 files changed, 106 insertions, 24 deletions
diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs
index f1a649ee..800585bd 100644
--- a/Source/Dafny/Compiler.cs
+++ b/Source/Dafny/Compiler.cs
@@ -795,21 +795,22 @@ namespace Microsoft.Dafny {
// } else if (true) {
// ...
// }
+ if (s.Cases.Count != 0) {
+ string source = "_source" + tmpVarCount;
+ tmpVarCount++;
+ Indent(indent);
+ wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type)), source);
+ TrExpr(s.Source);
+ wr.WriteLine(";");
- string source = "_source" + tmpVarCount;
- tmpVarCount++;
- Indent(indent);
- wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type)), source);
- TrExpr(s.Source);
- wr.WriteLine(";");
-
- int i = 0;
- foreach (MatchCaseStmt mc in s.Cases) {
- MatchCasePrelude(source, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent);
- TrStmtList(mc.Body, indent);
- i++;
+ int i = 0;
+ foreach (MatchCaseStmt mc in s.Cases) {
+ MatchCasePrelude(source, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent);
+ TrStmtList(mc.Body, indent);
+ i++;
+ }
+ Indent(indent); wr.WriteLine("}");
}
- Indent(indent); wr.WriteLine("}");
} else {
Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement
diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs
index d83560e0..04d92539 100644
--- a/Source/Dafny/DafnyAst.cs
+++ b/Source/Dafny/DafnyAst.cs
@@ -1642,10 +1642,12 @@ namespace Microsoft.Dafny {
void ObjectInvariant() {
Contract.Invariant(Source != null);
Contract.Invariant(cce.NonNullElements(Cases));
+ Contract.Invariant(cce.NonNullElements(MissingCases));
}
public readonly Expression Source;
public readonly List<MatchCaseStmt/*!*/>/*!*/ Cases;
+ public readonly List<DatatypeCtor/*!*/> MissingCases = new List<DatatypeCtor>(); // filled in during resolution
public MatchStmt(IToken tok, Expression source, [Captured] List<MatchCaseStmt/*!*/>/*!*/ cases)
: base(tok) {
@@ -2490,10 +2492,13 @@ namespace Microsoft.Dafny {
public class MatchExpr : Expression { // a MatchExpr is an "extended expression" and is only allowed in certain places
public readonly Expression Source;
public readonly List<MatchCaseExpr/*!*/>/*!*/ Cases;
+ public readonly List<DatatypeCtor/*!*/> MissingCases = new List<DatatypeCtor>(); // filled in during resolution
+
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(Source != null);
Contract.Invariant(cce.NonNullElements(Cases));
+ Contract.Invariant(cce.NonNullElements(MissingCases));
}
public MatchExpr(IToken tok, Expression source, [Captured] List<MatchCaseExpr/*!*/>/*!*/ cases)
diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs
index d8ca3448..eb40ac82 100644
--- a/Source/Dafny/Resolver.cs
+++ b/Source/Dafny/Resolver.cs
@@ -1473,7 +1473,16 @@ namespace Microsoft.Dafny {
scope.PopMarker();
}
if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) {
- Error(stmt, "match expression does not cover all constructors");
+ // We could complain about the syntactic omission of constructors:
+ // Error(stmt, "match statement does not cover all constructors");
+ // but instead we let the verifier do a semantic check.
+ // So, for now, record the missing constructors:
+ foreach (var ctr in dtd.Ctors) {
+ if (!memberNamesUsed.ContainsKey(ctr.Name)) {
+ s.MissingCases.Add(ctr);
+ }
+ }
+ Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count);
}
@@ -2390,7 +2399,16 @@ namespace Microsoft.Dafny {
scope.PopMarker();
}
if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) {
- Error(expr, "match expression does not cover all constructors");
+ // We could complain about the syntactic omission of constructors:
+ // Error(expr, "match expression does not cover all constructors");
+ // but instead we let the verifier do a semantic check.
+ // So, for now, record the missing constructors:
+ foreach (var ctr in dtd.Ctors) {
+ if (!memberNamesUsed.ContainsKey(ctr.Name)) {
+ me.MissingCases.Add(ctr);
+ }
+ }
+ Contract.Assert(memberNamesUsed.Count + me.MissingCases.Count == dtd.Ctors.Count);
}
} else {
diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs
index bba7b734..b529b984 100644
--- a/Source/Dafny/Translator.cs
+++ b/Source/Dafny/Translator.cs
@@ -1371,8 +1371,26 @@ namespace Microsoft.Dafny {
Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(mc.tok, mc.Ctor.FullName, predef.DatatypeType);
return new Bpl.NAryExpr(mc.tok, new Bpl.FunctionCall(id), args);
}
-
- Bpl.Expr IsTotal(Expression expr, ExpressionTranslator etran){
+
+ Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etran, Bpl.VariableSeq locals, StmtListBuilder localTypeAssumptions) {
+ Contract.Requires(tok != null);
+ Contract.Requires(ctor != null);
+ Contract.Requires(etran != null);
+ Contract.Requires(locals != null);
+ Contract.Requires(localTypeAssumptions != null);
+ Contract.Requires(predef != null);
+ Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
+
+ VariableSeq bvars;
+ List<Bpl.Expr> args;
+ CreateBoundVariables(ctor.Formals, out bvars, out args);
+ locals.AddRange(bvars);
+
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(tok, ctor.FullName, predef.DatatypeType);
+ return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(id), new ExprSeq(args.ToArray()));
+ }
+
+ Bpl.Expr IsTotal(Expression expr, ExpressionTranslator etran) {
Contract.Requires(expr != null);Contract.Requires(etran != null);
Contract.Requires(predef != null);
Contract.Ensures(Contract.Result<Bpl.Expr>() != null);
@@ -2030,20 +2048,40 @@ namespace Microsoft.Dafny {
MatchExpr me = (MatchExpr)expr;
CheckWellformed(me.Source, options, locals, builder, etran);
Bpl.Expr src = etran.TrExpr(me.Source);
- Bpl.IfCmd ifcmd = null;
+ Bpl.IfCmd ifCmd = null;
StmtListBuilder elsBldr = new StmtListBuilder();
elsBldr.Add(new Bpl.AssumeCmd(expr.tok, Bpl.Expr.False));
StmtList els = elsBldr.Collect(expr.tok);
+ foreach (var missingCtor in me.MissingCases) {
+ // havoc all bound variables
+ var b = new Bpl.StmtListBuilder();
+ VariableSeq newLocals = new VariableSeq();
+ Bpl.Expr r = CtorInvocation(me.tok, missingCtor, etran, newLocals, b);
+ locals.AddRange(newLocals);
+
+ if (newLocals.Length != 0) {
+ Bpl.IdentifierExprSeq havocIds = new Bpl.IdentifierExprSeq();
+ foreach (Variable local in newLocals) {
+ havocIds.Add(new Bpl.IdentifierExpr(local.tok, local));
+ }
+ builder.Add(new Bpl.HavocCmd(me.tok, havocIds));
+ }
+ b.Add(Assert(me.tok, Bpl.Expr.False, "missing case in case statement: " + missingCtor.Name));
+
+ Bpl.Expr guard = Bpl.Expr.Eq(src, r);
+ ifCmd = new Bpl.IfCmd(me.tok, guard, b.Collect(me.tok), ifCmd, els);
+ els = null;
+ }
for (int i = me.Cases.Count; 0 <= --i; ) {
MatchCaseExpr mc = me.Cases[i];
Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
Bpl.Expr ct = CtorInvocation(mc, etran, locals, b);
// generate: if (src == ctor(args)) { assume args-is-well-typed; mc.Body is well-formed; assume Result == TrExpr(case); } else ...
CheckWellformedWithResult(mc.Body, options, result, resultType, locals, b, etran);
- ifcmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifcmd, els);
+ ifCmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifCmd, els);
els = null;
}
- builder.Add(ifcmd);
+ builder.Add(ifCmd);
result = null;
} else {
@@ -3233,16 +3271,36 @@ namespace Microsoft.Dafny {
builder.Add(CaptureState(stmt.Tok));
} else if (stmt is MatchStmt) {
- MatchStmt s = (MatchStmt)stmt;
+ var s = (MatchStmt)stmt;
TrStmt_CheckWellformed(s.Source, builder, locals, etran, true);
Bpl.Expr source = etran.TrExpr(s.Source);
- Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ var b = new Bpl.StmtListBuilder();
b.Add(new Bpl.AssumeCmd(stmt.Tok, Bpl.Expr.False));
Bpl.StmtList els = b.Collect(stmt.Tok);
Bpl.IfCmd ifCmd = null;
+ foreach (var missingCtor in s.MissingCases) {
+ // havoc all bound variables
+ b = new Bpl.StmtListBuilder();
+ VariableSeq newLocals = new VariableSeq();
+ Bpl.Expr r = CtorInvocation(s.Tok, missingCtor, etran, newLocals, b);
+ locals.AddRange(newLocals);
+
+ if (newLocals.Length != 0) {
+ Bpl.IdentifierExprSeq havocIds = new Bpl.IdentifierExprSeq();
+ foreach (Variable local in newLocals) {
+ havocIds.Add(new Bpl.IdentifierExpr(local.tok, local));
+ }
+ builder.Add(new Bpl.HavocCmd(s.Tok, havocIds));
+ }
+ b.Add(Assert(s.Tok, Bpl.Expr.False, "missing case in case statement: " + missingCtor.Name));
+
+ Bpl.Expr guard = Bpl.Expr.Eq(source, r);
+ ifCmd = new Bpl.IfCmd(s.Tok, guard, b.Collect(s.Tok), ifCmd, els);
+ els = null;
+ }
for (int i = s.Cases.Count; 0 <= --i; ) {
- MatchCaseStmt mc = (MatchCaseStmt)s.Cases[i];
+ var mc = (MatchCaseStmt)s.Cases[i];
// havoc all bound variables
b = new Bpl.StmtListBuilder();
VariableSeq newLocals = new VariableSeq();
@@ -3266,7 +3324,7 @@ namespace Microsoft.Dafny {
ifCmd = new Bpl.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els);
els = null;
}
- Contract.Assert(ifCmd != null); // follows from the fact that s.Cases.Count != 0.
+ Contract.Assert(ifCmd != null); // follows from the fact that s.Cases.Count + s.MissingCases.Count != 0.
builder.Add(ifCmd);
} else {