diff --git a/Source/DafnyCore/Compilers/Java/Compiler-java.cs b/Source/DafnyCore/Compilers/Java/Compiler-java.cs
index 03fd4a3bd9e..1d110cd315f 100644
--- a/Source/DafnyCore/Compilers/Java/Compiler-java.cs
+++ b/Source/DafnyCore/Compilers/Java/Compiler-java.cs
@@ -1801,8 +1801,8 @@ IClassWriter CompileDatatypeBase(DatatypeDecl dt, ConcreteSyntaxTree wr) {
foreach (var ctor in dt.Ctors.Where(ctor => !ctor.IsGhost)) {
wr.Write($"public static{justTypeArgs} {DtT_protected} {DtCreateName(ctor)}(");
WriteFormals("", ctor.Formals, wr);
- var w = wr.NewBlock(")");
- w.Write($"return new {DtCtorDeclarationName(ctor, dt.TypeArgs)}({ctor.Formals.Where(f => !f.IsGhost).Comma(FormalName)});");
+ wr.NewBlock(")")
+ .WriteLine($"return new {DtCtorDeclarationName(ctor, dt.TypeArgs)}({ctor.Formals.Where(f => !f.IsGhost).Comma(FormalName)});");
}
if (dt.IsRecordType) {
@@ -1813,7 +1813,7 @@ IClassWriter CompileDatatypeBase(DatatypeDecl dt, ConcreteSyntaxTree wr) {
wr.Write($"public static{justTypeArgs} {DtT_protected} create_{ctor.CompileName}(");
WriteFormals("", ctor.Formals, wr);
wr.NewBlock(")")
- .Write($"return create({ctor.Formals.Where(f => !f.IsGhost).Comma(FormalName)});");
+ .WriteLine($"return create({ctor.Formals.Where(f => !f.IsGhost).Comma(FormalName)});");
}
// query properties
diff --git a/Source/DafnyCore/Resolver/CallGraphBuilder.cs b/Source/DafnyCore/Resolver/CallGraphBuilder.cs
index 1122c328080..d6377bfb229 100644
--- a/Source/DafnyCore/Resolver/CallGraphBuilder.cs
+++ b/Source/DafnyCore/Resolver/CallGraphBuilder.cs
@@ -295,6 +295,9 @@ public static void AddCallGraphEdge(IASTVisitorContext callingContext, ICallable
ModuleDefinition calleeModule = callable is SpecialFunction ? null : callable.EnclosingModule;
if (callerModule != calleeModule) {
// inter-module call; don't record in call graph
+ if (callingContext is ICallable context && callable is Function { EnclosingClass: TraitDecl }) {
+ callerModule.CallGraph.AddEdge(context, callable);
+ }
return;
}
diff --git a/Source/DafnyCore/Verifier/FunctionCallSubstituter.cs b/Source/DafnyCore/Verifier/FunctionCallSubstituter.cs
index be765714d9b..bea1d11dfae 100644
--- a/Source/DafnyCore/Verifier/FunctionCallSubstituter.cs
+++ b/Source/DafnyCore/Verifier/FunctionCallSubstituter.cs
@@ -1,30 +1,38 @@
using System.Collections.Generic;
+using System.Linq;
namespace Microsoft.Dafny {
public class FunctionCallSubstituter : Substituter {
- public readonly Function A, B;
- public FunctionCallSubstituter(Expression receiverReplacement, Dictionary/*!*/ substMap, Function a, Function b)
- : base(receiverReplacement, substMap, new Dictionary()) {
- A = a;
- B = b;
+ public readonly TraitDecl Tr;
+ public readonly ClassDecl Cl;
+
+ // We replace all occurrences of the trait version of the function with the class version. This is only allowed if
+ // the receiver is `this`. We underapproximate this by looking for a `ThisExpr`, which misses more complex
+ // expressions that evaluate to one.
+ public FunctionCallSubstituter(Dictionary/*!*/ substMap, Dictionary typeMap, TraitDecl Tr, ClassDecl Cl)
+ : base(null, substMap, typeMap) {
+ this.Tr = Tr;
+ this.Cl = Cl;
}
+
public override Expression Substitute(Expression expr) {
- if (expr is FunctionCallExpr) {
- FunctionCallExpr e = (FunctionCallExpr)expr;
- Expression receiver = Substitute(e.Receiver);
- List newArgs = SubstituteExprList(e.Args);
- FunctionCallExpr newFce = new FunctionCallExpr(expr.tok, e.Name, receiver, e.OpenParen, e.CloseParen, newArgs, e.AtLabel);
- if (e.Function == A) {
- newFce.Function = B;
- newFce.Type = e.Type; // TODO: this may not work with type parameters.
+ if (expr is FunctionCallExpr e) {
+ var receiver = Substitute(e.Receiver);
+ var newArgs = SubstituteExprList(e.Args);
+ Function function;
+ if ((e.Function.EnclosingClass == Tr || Tr.InheritedMembers.Contains(e.Function)) && e.Receiver is ThisExpr && receiver is ThisExpr && Cl.Members.Find(m => m.OverriddenMember == e.Function) is { } f) {
+ receiver = new ThisExpr((TopLevelDeclWithMembers)f.EnclosingClass);
+ function = (Function)f;
} else {
- newFce.Function = e.Function;
- newFce.Type = e.Type;
+ function = e.Function;
}
- newFce.TypeApplication_AtEnclosingClass = e.TypeApplication_AtEnclosingClass; // resolve here
- newFce.TypeApplication_JustFunction = e.TypeApplication_JustFunction; // resolve here
- newFce.IsByMethodCall = e.IsByMethodCall;
- return newFce;
+ return new FunctionCallExpr(e.tok, e.Name, receiver, e.OpenParen, e.CloseParen, newArgs, e.AtLabel) {
+ Function = function,
+ Type = e.Type, // TODO: this may not work with type parameters.
+ TypeApplication_AtEnclosingClass = SubstituteTypeList(e.TypeApplication_AtEnclosingClass), // resolve here
+ TypeApplication_JustFunction = SubstituteTypeList(e.TypeApplication_JustFunction), // resolve here
+ IsByMethodCall = e.IsByMethodCall
+ };
}
return base.Substitute(expr);
}
diff --git a/Source/DafnyCore/Verifier/Translator.ClassMembers.cs b/Source/DafnyCore/Verifier/Translator.ClassMembers.cs
index 10c96e1a5c3..fa0aee6f06b 100644
--- a/Source/DafnyCore/Verifier/Translator.ClassMembers.cs
+++ b/Source/DafnyCore/Verifier/Translator.ClassMembers.cs
@@ -173,7 +173,7 @@ void AddMethod_Top(Method m, bool isByMethod, bool includeAllMethods) {
AddMethodImpl(m, proc, true);
}
}
- if (m.OverriddenMethod != null && InVerificationScope(m)) //method has overrided a parent method
+ if (m.OverriddenMethod != null && InVerificationScope(m)) //method has overridden a parent method
{
var procOverrideChk = AddMethod(m, MethodTranslationKind.OverrideCheck);
sink.AddTopLevelDeclaration(procOverrideChk);
@@ -877,8 +877,8 @@ private void AddFunctionOverrideCheckImpl(Function f) {
}
// the procedure itself
var req = new List();
- // free requires mh == ModuleContextHeight && fh == FunctionContextHeight;
- req.Add(Requires(f.tok, true, etran.HeightContext(f.OverriddenFunction), null, null));
+ // free requires fh == FunctionContextHeight;
+ req.Add(Requires(f.tok, true, etran.HeightContext(f), null, null));
if (f is TwoStateFunction) {
// free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap)
var a0 = Boogie.Expr.Eq(prevHeap, ordinaryEtran.HeapExpr);
@@ -920,18 +920,16 @@ private void AddFunctionOverrideCheckImpl(Function f) {
}
var substMap = new Dictionary();
- for (int i = 0; i < f.Formals.Count; i++) {
- //get corresponsing formal in the class
- var ie = new IdentifierExpr(f.Formals[i].tok, f.Formals[i].AssignUniqueName(f.IdGenerator));
- ie.Var = f.Formals[i]; ie.Type = ie.Var.Type;
- substMap.Add(f.OverriddenFunction.Formals[i], ie);
+ foreach (var (formal, overriddenFormal) in f.Formals.Zip(f.OverriddenFunction.Formals, Tuple.Create)) {
+ // get corresponding formal in the class
+ var ie = new IdentifierExpr(formal.tok, formal.AssignUniqueName(f.IdGenerator)) { Var = formal, Type = formal.Type };
+ substMap.Add(overriddenFormal, ie);
}
if (f.OverriddenFunction.Result != null) {
Contract.Assert(pOut != null);
- //get corresponsing formal in the class
- var ie = new IdentifierExpr(pOut.tok, pOut.AssignUniqueName(f.IdGenerator));
- ie.Var = pOut; ie.Type = ie.Var.Type;
+ //get corresponding formal in the class
+ var ie = new IdentifierExpr(pOut.tok, pOut.AssignUniqueName(f.IdGenerator)) { Var = pOut, Type = pOut.Type };
substMap.Add(f.OverriddenFunction.Result, ie);
}
@@ -947,7 +945,6 @@ private void AddFunctionOverrideCheckImpl(Function f) {
AddFunctionOverrideSubsetChk(f, builder, etran, localVariables, substMap, typeMap);
//adding assume Q; assert Post’;
- //adding assume J.F(ins) == C.F(ins);
AddFunctionOverrideEnsChk(f, builder, etran, substMap, typeMap, implInParams, implOutParams.Count == 0 ? null : implOutParams[0]);
var stmts = builder.Collect(f.tok);
@@ -956,9 +953,9 @@ private void AddFunctionOverrideCheckImpl(Function f) {
// emit the impl only when there are proof obligations.
QKeyValue kv = etran.TrAttributes(f.Attributes, null);
- var impl = AddImplementationWithVerboseName(f.tok, proc,
- Util.Concat(Util.Concat(typeInParams, inParams_Heap), implInParams),
- implOutParams, localVariables, stmts, kv);
+ AddImplementationWithVerboseName(f.tok, proc,
+ Util.Concat(Util.Concat(typeInParams, inParams_Heap), implInParams),
+ implOutParams, localVariables, stmts, kv);
}
if (InsertChecksums) {
@@ -999,7 +996,7 @@ private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, Boogi
/// axiom (forall $heap: HeapType, typeArgs: Ty, this: ref, x#0: int, fuel: LayerType ::
/// { J.F(fuel, $heap, G(typeArgs), this, x#0), C.F(fuel, $heap, typeArgs, this, x#0) }
/// { J.F(fuel, $heap, G(typeArgs), this, x#0), $Is(this, C) }
- /// this != null && $Is(this, C)
+ /// C.F#canCall(args) || (fh < FunctionContextHeight && this != null && $Is(this, C))
/// ==>
/// J.F(fuel, $heap, G(typeArgs), this, x#0) == C.F(fuel, $heap, typeArgs, this, x#0));
/// (without the other usual antecedents). Essentially, the override gives a part of the body of the
@@ -1043,19 +1040,20 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti
argsJF.AddRange(GetTypeArguments(f, overridingFunction).ConvertAll(TypeToTy));
argsCF.AddRange(GetTypeArguments(overridingFunction, null).ConvertAll(TypeToTy));
+ var moreArgsCF = new List();
+ Expr layer = null;
+
// Add the fuel argument
if (f.IsFuelAware()) {
Contract.Assert(overridingFunction.IsFuelAware()); // f.IsFuelAware() ==> overridingFunction.IsFuelAware()
var fuel = new Boogie.BoundVariable(f.tok, new Boogie.TypedIdent(f.tok, "$fuel", predef.LayerType));
forallFormals.Add(fuel);
- var ly = new Boogie.IdentifierExpr(f.tok, fuel);
- argsJF.Add(ly);
- argsCF.Add(ly);
+ layer = new Boogie.IdentifierExpr(f.tok, fuel);
+ argsJF.Add(layer);
} else if (overridingFunction.IsFuelAware()) {
// We can't use a bound variable $fuel, because then one of the triggers won't be mentioning this $fuel.
// Instead, we do the next best thing: use the literal $LZ.
- var ly = new Boogie.IdentifierExpr(f.tok, "$LZ", predef.LayerType); // $LZ
- argsCF.Add(ly);
+ layer = new Boogie.IdentifierExpr(f.tok, "$LZ", predef.LayerType); // $LZ
}
// Add heap arguments
@@ -1063,7 +1061,7 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti
Contract.Assert(bvPrevHeap != null);
forallFormals.Add(bvPrevHeap);
argsJF.Add(etran.Old.HeapExpr);
- argsCF.Add(etran.Old.HeapExpr);
+ moreArgsCF.Add(etran.Old.HeapExpr);
}
if (AlwaysUseHeap || f.ReadsHeap || overridingFunction.ReadsHeap) {
var heap = new Boogie.BoundVariable(f.tok, new Boogie.TypedIdent(f.tok, predef.HeapVarName, predef.HeapType));
@@ -1072,7 +1070,7 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti
argsJF.Add(new Boogie.IdentifierExpr(f.tok, heap));
}
if (AlwaysUseHeap || overridingFunction.ReadsHeap) {
- argsCF.Add(new Boogie.IdentifierExpr(overridingFunction.tok, heap));
+ moreArgsCF.Add(new Boogie.IdentifierExpr(overridingFunction.tok, heap));
}
}
@@ -1082,10 +1080,12 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti
forallFormals.Add(bvThis);
var bvThisExpr = new Boogie.IdentifierExpr(f.tok, bvThis);
argsJF.Add(bvThisExpr);
- argsCF.Add(bvThisExpr);
+ moreArgsCF.Add(bvThisExpr);
// $Is(this, C)
var isOfSubtype = GetWhereClause(overridingFunction.tok, bvThisExpr, thisType, f is TwoStateFunction ? etran.Old : etran, IsAllocType.NEVERALLOC);
+ Bpl.Expr ante = Boogie.Expr.And(ReceiverNotNull(bvThisExpr), isOfSubtype);
+
// Add other arguments
var typeMap = GetTypeArgumentSubstitutionMap(f, overridingFunction);
foreach (Formal p in f.Formals) {
@@ -1094,13 +1094,26 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti
forallFormals.Add(bv);
var jfArg = new Boogie.IdentifierExpr(p.tok, bv);
argsJF.Add(ModeledAsBoxType(p.Type) ? BoxIfUnboxed(jfArg, pType) : jfArg);
- argsCF.Add(new Boogie.IdentifierExpr(p.tok, bv));
+ moreArgsCF.Add(new Boogie.IdentifierExpr(p.tok, bv));
}
- // useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight)
- ModuleDefinition mod = f.EnclosingClass.EnclosingModuleDefinition;
- Boogie.Expr useViaContext = !InVerificationScope(overridingFunction) ? (Boogie.Expr)Boogie.Expr.True :
- Boogie.Expr.Neq(Boogie.Expr.Literal(mod.CallGraph.GetSCCRepresentativePredecessorCount(overridingFunction)), etran.FunctionContextHeight());
+ // useViaContext: fh < FunctionContextHeight
+ Boogie.Expr useViaContext = !InVerificationScope(overridingFunction)
+ ? Boogie.Expr.True
+ : Boogie.Expr.Lt(Boogie.Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(overridingFunction)), etran.FunctionContextHeight());
+
+ // useViaCanCall: C.F#canCall(args)
+ Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(overridingFunction.tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool);
+ Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(argsCF, moreArgsCF));
+
+ if (layer != null) {
+ argsCF.Add(layer);
+ }
+
+ argsCF = Concat(argsCF, moreArgsCF);
+
+ // ante := useViaCanCall || (useViaContext && this != null && $Is(this, C))
+ ante = Bpl.Expr.Or(useViaCanCall, BplAnd(useViaContext, ante));
Boogie.Expr funcAppl;
{
@@ -1133,8 +1146,8 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti
// The axiom
Boogie.Expr ax = BplForall(f.tok, new List(), forallFormals, null, tr,
- Boogie.Expr.Imp(Boogie.Expr.And(ReceiverNotNull(bvThisExpr), isOfSubtype), synonyms));
- var activate = AxiomActivation(f, etran);
+ Boogie.Expr.Imp(ante, synonyms));
+ var activate = AxiomActivation(overridingFunction, etran);
string comment = "override axiom for " + f.FullSanitizedName + " in class " + overridingFunction.EnclosingClass.FullSanitizedName;
return new Boogie.Axiom(f.tok, Boogie.Expr.Imp(activate, ax), comment);
}
@@ -1192,13 +1205,11 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex
builder.Add(TrAssumeCmd(m.tok, etran.TrExpr(en.E)));
}
//generating trait post-conditions with class variables
+ FunctionCallSubstituter sub = null;
foreach (var en in m.OverriddenMethod.Ens) {
- Expression postcond = Substitute(en.E, null, substMap, typeMap);
- bool splitHappened; // we actually don't care
- foreach (var s in TrSplitExpr(postcond, etran, false, out splitHappened)) {
- if (s.IsChecked) {
- builder.Add(Assert(m.tok, s.E, new PODesc.EnsuresStronger()));
- }
+ sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)m.OverriddenMethod.EnclosingClass, (ClassDecl)m.EnclosingClass);
+ foreach (var s in TrSplitExpr(sub.Substitute(en.E), etran, false, out _).Where(s => s.IsChecked)) {
+ builder.Add(Assert(m.tok, s.E, new PODesc.EnsuresStronger()));
}
}
}
@@ -1211,18 +1222,14 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E
Contract.Requires(etran != null);
Contract.Requires(substMap != null);
//generating trait pre-conditions with class variables
+ FunctionCallSubstituter sub = null;
foreach (var req in m.OverriddenMethod.Req) {
- Expression precond = Substitute(req.E, null, substMap, typeMap);
- builder.Add(TrAssumeCmd(m.tok, etran.TrExpr(precond)));
+ sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)m.OverriddenMethod.EnclosingClass, (ClassDecl)m.EnclosingClass);
+ builder.Add(TrAssumeCmd(m.tok, etran.TrExpr(sub.Substitute(req.E))));
}
//generating class pre-conditions
- foreach (var req in m.Req) {
- bool splitHappened; // we actually don't care
- foreach (var s in TrSplitExpr(req.E, etran, false, out splitHappened)) {
- if (s.IsChecked) {
- builder.Add(Assert(m.tok, s.E, new PODesc.RequiresWeaker()));
- }
- }
+ foreach (var s in m.Req.SelectMany(req => TrSplitExpr(req.E, etran, false, out _).Where(s => s.IsChecked))) {
+ builder.Add(Assert(m.tok, s.E, new PODesc.RequiresWeaker()));
}
}
@@ -1381,7 +1388,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) {
// FREE PRECONDITIONS
if (kind == MethodTranslationKind.SpecWellformedness || kind == MethodTranslationKind.Implementation || kind == MethodTranslationKind.OverrideCheck) { // the other cases have no need for a free precondition
// free requires mh == ModuleContextHeight && fh == FunctionContextHeight;
- req.Add(Requires(m.tok, true, etran.HeightContext(kind == MethodTranslationKind.OverrideCheck ? m.OverriddenMethod : m), null, null));
+ req.Add(Requires(m.tok, true, etran.HeightContext(m), null, null));
if (m is TwoStateLemma) {
// free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap)
var a0 = Boogie.Expr.Eq(prevHeap, ordinaryEtran.HeapExpr);
diff --git a/Source/DafnyCore/Verifier/Translator.cs b/Source/DafnyCore/Verifier/Translator.cs
index 1fb4c2f1264..169864d8561 100644
--- a/Source/DafnyCore/Verifier/Translator.cs
+++ b/Source/DafnyCore/Verifier/Translator.cs
@@ -709,6 +709,7 @@ public Bpl.IdentifierExpr TrVar(IToken tok, IVariable var) {
private Bpl.Program DoTranslation(Program p, ModuleDefinition forModule) {
program = p;
+ this.forModule = forModule;
Type.EnableScopes();
EstablishModuleScope(p.BuiltIns.SystemModule, forModule);
@@ -2050,11 +2051,10 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis
//
// AXIOM_ACTIVATION
// means:
- // mh < ModuleContextHeight ||
- // (mh == ModuleContextHeight && fh <= FunctionContextHeight)
+ // fh <= FunctionContextHeight
//
// USE_VIA_CONTEXT
- // (mh != ModuleContextHeight || fh != FunctionContextHeight) &&
+ // fh < FunctionContextHeight &&
// GOOD_PARAMETERS
// where GOOD_PARAMETERS means:
// $IsGoodHeap($Heap) && this != null && formals-have-the-expected-types &&
@@ -2166,10 +2166,10 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis
foreach (AttributedExpression req in f.Req) {
pre = BplAnd(pre, etran.TrExpr(req.E));
}
- // useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight)
- var mod = f.EnclosingClass.EnclosingModuleDefinition;
- Bpl.Expr useViaContext = !InVerificationScope(f) ? Bpl.Expr.True :
- (Bpl.Expr)Bpl.Expr.Neq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight());
+ // useViaContext: fh < FunctionContextHeight
+ Expr useViaContext = !(InVerificationScope(f) || (f.EnclosingClass is TraitDecl))
+ ? Bpl.Expr.True
+ : Bpl.Expr.Lt(Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight());
// useViaCanCall: f#canCall(args)
Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool);
Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args));
@@ -2276,11 +2276,9 @@ Bpl.Expr AxiomActivation(Function f, ExpressionTranslator etran) {
Contract.Requires(f != null);
Contract.Requires(etran != null);
Contract.Requires(VisibleInScope(f));
- var module = f.EnclosingClass.EnclosingModuleDefinition;
-
if (InVerificationScope(f)) {
return
- Bpl.Expr.Le(Bpl.Expr.Literal(module.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight());
+ Bpl.Expr.Le(Bpl.Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight());
} else {
return Bpl.Expr.True;
}
@@ -2312,15 +2310,15 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) {
//
// AXIOM_ACTIVATION
// for visibility==ForeignModuleOnly, means:
- // mh < ModuleContextHeight
+ // true
// for visibility==IntraModuleOnly, means:
- // mh == ModuleContextHeight && fh <= FunctionContextHeight
+ // fh <= FunctionContextHeight
//
// USE_VIA_CONTEXT
// for visibility==ForeignModuleOnly, means:
// GOOD_PARAMETERS
// for visibility==IntraModuleOnly, means:
- // fh != FunctionContextHeight &&
+ // fh < FunctionContextHeight &&
// GOOD_PARAMETERS
// where GOOD_PARAMETERS means:
// $IsGoodHeap($Heap) && this != null && formals-have-the-expected-types &&
@@ -2525,12 +2523,11 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) {
return null;
}
- // useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight)
+ // useViaContext: fh < FunctionContextHeight
ModuleDefinition mod = f.EnclosingClass.EnclosingModuleDefinition;
Bpl.Expr useViaContext = !InVerificationScope(f)
- ? (Bpl.Expr)Bpl.Expr.True
- : Bpl.Expr.Neq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativePredecessorCount(f)),
- etran.FunctionContextHeight());
+ ? Bpl.Expr.True
+ : Bpl.Expr.Lt(Bpl.Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight());
// ante := (useViaContext && typeAnte && pre)
ante = BplAnd(useViaContext, BplAnd(ante, pre));
@@ -3172,6 +3169,7 @@ Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Type indexType, Bpl.Expr seq, bo
}
ModuleDefinition currentModule = null; // the module whose members are currently being translated
+ ModuleDefinition forModule = null; // the root module
ICallable codeContext = null; // the method/iterator whose implementation is currently being translated or the function whose specification is being checked for well-formedness
Bpl.LocalVariable yieldCountVariable = null; // non-null when an iterator body is being translated
bool inBodyInitContext = false; // true during the translation of the .BodyInit portion of a divided constructor body
@@ -3504,65 +3502,40 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder
builder.Add(TrAssumeCmd(f.tok, etran.TrExpr(en.E)));
}
- //generating assume J.F(ins) == C.F(ins)
- Bpl.FunctionCall funcIdC = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, TrType(f.ResultType)));
- Bpl.FunctionCall funcIdT = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.OverriddenFunction.tok, f.OverriddenFunction.FullSanitizedName, TrType(f.OverriddenFunction.ResultType)));
- List argsC = new List();
- List argsT = new List();
- // add type arguments
- argsT.AddRange(GetTypeArguments(f.OverriddenFunction, f).ConvertAll(TypeToTy));
- argsC.AddRange(GetTypeArguments(f, null).ConvertAll(TypeToTy));
- // add fuel arguments
- if (f.IsFuelAware()) {
- argsC.Add(etran.layerInterCluster.GetFunctionFuel(f));
- }
- if (f.OverriddenFunction.IsFuelAware()) {
- argsT.Add(etran.layerInterCluster.GetFunctionFuel(f));
- }
- // add heap arguments
- if (f is TwoStateFunction) {
- argsC.Add(etran.Old.HeapExpr);
- argsT.Add(etran.Old.HeapExpr);
- }
- if (AlwaysUseHeap || f.ReadsHeap) {
- argsC.Add(etran.HeapExpr);
- }
- if (AlwaysUseHeap || f.OverriddenFunction.ReadsHeap) {
- argsT.Add(etran.HeapExpr);
- }
- // add "ordinary" parameters (including "this", if any)
- var prefixCount = implInParams.Count - f.Formals.Count;
- for (var i = 0; i < implInParams.Count; i++) {
- Bpl.Expr cParam = new Bpl.IdentifierExpr(f.tok, implInParams[i]);
- Bpl.Expr tParam = new Bpl.IdentifierExpr(f.OverriddenFunction.tok, implInParams[i]);
- if (prefixCount <= i && ModeledAsBoxType(f.OverriddenFunction.Formals[i - prefixCount].Type)) {
- tParam = BoxIfNecessary(f.tok, tParam, f.Formals[i - prefixCount].Type);
- }
- argsC.Add(cParam);
- argsT.Add(tParam);
- }
- Bpl.Expr funcExpC = new Bpl.NAryExpr(f.tok, funcIdC, argsC);
- Bpl.Expr funcExpT = new Bpl.NAryExpr(f.OverriddenFunction.tok, funcIdT, argsT);
- var funcExpCPossiblyBoxed = funcExpC;
- if (ModeledAsBoxType(f.OverriddenFunction.ResultType)) {
- funcExpCPossiblyBoxed = BoxIfUnboxed(funcExpCPossiblyBoxed, f.ResultType);
- }
- builder.Add(TrAssumeCmd(f.tok, Bpl.Expr.Eq(funcExpCPossiblyBoxed, funcExpT)));
-
//generating assume C.F(ins) == out, if a result variable was given
if (resultVariable != null) {
+ var funcIdC = new FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, TrType(f.ResultType)));
+ var argsC = new List();
+
+ // add type arguments
+ argsC.AddRange(GetTypeArguments(f, null).ConvertAll(TypeToTy));
+
+ // add fuel arguments
+ if (f.IsFuelAware()) {
+ argsC.Add(etran.layerInterCluster.GetFunctionFuel(f));
+ }
+
+ // add heap arguments
+ if (f is TwoStateFunction) {
+ argsC.Add(etran.Old.HeapExpr);
+ }
+ if (AlwaysUseHeap || f.ReadsHeap) {
+ argsC.Add(etran.HeapExpr);
+ }
+
+ argsC.AddRange(implInParams.Select(var => new Bpl.IdentifierExpr(f.tok, var)));
+
+ var funcExpC = new Bpl.NAryExpr(f.tok, funcIdC, argsC);
var resultVar = new Bpl.IdentifierExpr(resultVariable.tok, resultVariable);
builder.Add(TrAssumeCmd(f.tok, Bpl.Expr.Eq(funcExpC, resultVar)));
}
//generating trait post-conditions with class variables
+ FunctionCallSubstituter sub = null;
foreach (var en in f.OverriddenFunction.Ens) {
- Expression postcond = Substitute(en.E, null, substMap, typeMap);
- bool splitHappened; // we don't actually care
- foreach (var s in TrSplitExpr(postcond, etran, false, out splitHappened)) {
- if (s.IsChecked) {
- builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(true)));
- }
+ sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)f.OverriddenFunction.EnclosingClass, (ClassDecl)f.EnclosingClass);
+ foreach (var s in TrSplitExpr(sub.Substitute(en.E), etran, false, out _).Where(s => s.IsChecked)) {
+ builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(true)));
}
}
}
@@ -3653,18 +3626,14 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde
Contract.Requires(etran != null);
Contract.Requires(substMap != null);
//generating trait pre-conditions with class variables
+ FunctionCallSubstituter sub = null;
foreach (var req in f.OverriddenFunction.Req) {
- Expression precond = Substitute(req.E, null, substMap, typeMap);
- builder.Add(TrAssumeCmd(f.tok, etran.TrExpr(precond)));
+ sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)f.OverriddenFunction.EnclosingClass, (ClassDecl)f.EnclosingClass);
+ builder.Add(TrAssumeCmd(f.tok, etran.TrExpr(sub.Substitute(req.E))));
}
//generating class pre-conditions
- foreach (var req in f.Req) {
- bool splitHappened; // we actually don't care
- foreach (var s in TrSplitExpr(req.E, etran, false, out splitHappened)) {
- if (s.IsChecked) {
- builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(false)));
- }
- }
+ foreach (var s in f.Req.SelectMany(req => TrSplitExpr(req.E, etran, false, out _).Where(s => s.IsChecked))) {
+ builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(false)));
}
}
diff --git a/Test/concurrency/01-InnerOuter.dfy b/Test/concurrency/01-InnerOuter.dfy
index 62ac4f891f8..ce833e2bcfd 100644
--- a/Test/concurrency/01-InnerOuter.dfy
+++ b/Test/concurrency/01-InnerOuter.dfy
@@ -84,7 +84,7 @@ class Inner extends O {
&& old(data) <= data
}
- twostate lemma adm() {}
+ twostate lemma adm() requires admPre() {}
constructor (ghost s: S, initial_data: int)
requires s.i()
diff --git a/Test/concurrency/01-InnerOuter.dfy.expect b/Test/concurrency/01-InnerOuter.dfy.expect
index 5ed0d97333e..b49c1470365 100644
--- a/Test/concurrency/01-InnerOuter.dfy.expect
+++ b/Test/concurrency/01-InnerOuter.dfy.expect
@@ -1,2 +1,2 @@
-Dafny program verifier finished with 31 verified, 0 errors
+Dafny program verifier finished with 32 verified, 0 errors
diff --git a/Test/concurrency/02-DoubleRead.dfy b/Test/concurrency/02-DoubleRead.dfy
index 11a3a86ab27..ae119a45d8d 100644
--- a/Test/concurrency/02-DoubleRead.dfy
+++ b/Test/concurrency/02-DoubleRead.dfy
@@ -111,7 +111,7 @@ class ArcAtomicIsize extends Object {
}
// Admissibility proof
- twostate lemma adm() {}
+ twostate lemma adm() requires admPre() {}
constructor (ghost universe: Universe, initial_data: int)
requires universe.i()
diff --git a/Test/concurrency/02-DoubleRead.dfy.expect b/Test/concurrency/02-DoubleRead.dfy.expect
index a61fd92d975..3e2e11e1866 100644
--- a/Test/concurrency/02-DoubleRead.dfy.expect
+++ b/Test/concurrency/02-DoubleRead.dfy.expect
@@ -1,2 +1,2 @@
-Dafny program verifier finished with 39 verified, 0 errors
+Dafny program verifier finished with 40 verified, 0 errors
diff --git a/Test/concurrency/03-SimpleCounter.dfy b/Test/concurrency/03-SimpleCounter.dfy
index d6a78609235..e0b01497246 100644
--- a/Test/concurrency/03-SimpleCounter.dfy
+++ b/Test/concurrency/03-SimpleCounter.dfy
@@ -112,7 +112,7 @@ class ArcAtomicIsize extends Object {
}
// Admissibility proof
- twostate lemma adm() {}
+ twostate lemma adm() requires admPre() {}
constructor (ghost universe: Universe, initial_data: int)
requires universe.i()
diff --git a/Test/concurrency/03-SimpleCounter.dfy.expect b/Test/concurrency/03-SimpleCounter.dfy.expect
index bba7f9e68b7..1b45cf86b6a 100644
--- a/Test/concurrency/03-SimpleCounter.dfy.expect
+++ b/Test/concurrency/03-SimpleCounter.dfy.expect
@@ -1,2 +1,2 @@
-Dafny program verifier finished with 41 verified, 0 errors
+Dafny program verifier finished with 42 verified, 0 errors
diff --git a/Test/concurrency/10-SequenceInvariant.dfy b/Test/concurrency/10-SequenceInvariant.dfy
index a9bff354a68..00b90d095ff 100644
--- a/Test/concurrency/10-SequenceInvariant.dfy
+++ b/Test/concurrency/10-SequenceInvariant.dfy
@@ -256,7 +256,7 @@ class Thread extends Object {
}
twostate lemma baseFieldsInvMonotonicity() requires old(baseFieldsInv()) && old(universe.content) <= universe.content && unchanged(this) ensures baseFieldsInv() {}
- predicate localInv() reads * {
+ predicate localInv() reads * ensures localInv() ==> objectGlobalBaseInv() {
&& objectGlobalBaseInv()
}
predicate inv() reads * ensures inv() ==> localInv() {
@@ -322,7 +322,7 @@ trait OwnedObject extends Object {
assert owner in universe.content;
}
- predicate localInv() reads * {
+ predicate localInv() reads * ensures localInv() ==> objectGlobalBaseInv() {
&& objectGlobalBaseInv()
&& localUserInv()
}
diff --git a/Test/concurrency/11-MutexGuard2.dfy b/Test/concurrency/11-MutexGuard2.dfy
index ac2aca3197a..12f8f44288c 100644
--- a/Test/concurrency/11-MutexGuard2.dfy
+++ b/Test/concurrency/11-MutexGuard2.dfy
@@ -261,7 +261,7 @@ class Thread extends Object {
}
twostate lemma baseFieldsInvMonotonicity() requires old(baseFieldsInv()) && old(universe.content) <= universe.content && unchanged(this) ensures baseFieldsInv() {}
- predicate localInv() reads * {
+ predicate localInv() reads * ensures localInv() ==> objectGlobalBaseInv() {
&& objectGlobalBaseInv()
}
predicate inv() reads * ensures inv() ==> localInv() {
@@ -332,7 +332,7 @@ trait OwnedObject extends Object {
&& unchangedNonvolatileUserFields()
}
- predicate localInv() reads * {
+ predicate localInv() reads * ensures localInv() ==> objectGlobalBaseInv() {
&& objectGlobalBaseInv()
&& localUserInv()
}
diff --git a/Test/concurrency/12-MutexLifetime-short.dfy b/Test/concurrency/12-MutexLifetime-short.dfy
index 5598fa22a50..b2f99b05827 100644
--- a/Test/concurrency/12-MutexLifetime-short.dfy
+++ b/Test/concurrency/12-MutexLifetime-short.dfy
@@ -365,7 +365,7 @@ class Thread extends Object {
}
twostate lemma baseFieldsInvMonotonicity() requires old(baseFieldsInv()) && old(universe.content) <= universe.content && unchanged(this) ensures baseFieldsInv() {}
- predicate localInv() reads * {
+ predicate localInv() reads * ensures localInv() ==> objectGlobalBaseInv() {
&& objectGlobalBaseInv()
}
predicate inv() reads * ensures inv() ==> localInv() {
@@ -439,7 +439,7 @@ trait OwnedObject extends Object {
&& unchangedNonvolatileUserFields()
}
- predicate localInv() reads * {
+ predicate localInv() reads * ensures localInv() ==> objectGlobalBaseInv() {
&& objectGlobalBaseInv()
&& this in lifetime.elements
&& (lifetime.alive() ==> owner != null)
diff --git a/Test/dafny4/git-issue245.dfy b/Test/dafny4/git-issue245.dfy
index 187cef8bc42..a5458a2cd7c 100644
--- a/Test/dafny4/git-issue245.dfy
+++ b/Test/dafny4/git-issue245.dfy
@@ -37,23 +37,23 @@ class C extends T {
{ 5 + y }
}
-// class D is like C, but in each case, the spec is NOT weaker than that in T
+// class D is like C, but in each case, the spec is NOT stronger than that in T
class D extends T {
// d does not name the result in either trait or class
function method d(y: nat): nat
- ensures d(y) < 20 + y // error: specification is not weaker
+ ensures d(y) < 20 + y // error: specification is not stronger
{ 11 + y }
// f names the result in both trait and class
function method f(y: nat): (result: nat)
- ensures result < 20 + y // error: specification is not weaker
+ ensures result < 20 + y // error: specification is not stronger
{ 11 + y }
// g names the result in just the class
function method g(y: nat): (result: nat)
- ensures result < 20 + y // error: specification is not weaker
+ ensures result < 20 + y // error: specification is not stronger
{ 11 + y }
// h names the result in just the trait
function method h(y: nat): nat
- ensures h(y) < 20 + y // error: specification is not weaker
+ ensures h(y) < 20 + y // error: specification is not stronger
{ 11 + y }
}
@@ -62,19 +62,19 @@ class D extends T {
class E extends T {
// d does not name the result in either trait or class
function method d(y: nat): nat
- ensures d(y) < 20 + y
+ ensures d(y) < 20 + y // error: specification is not stronger
{ 5 + y }
// f names the result in both trait and class
function method f(y: nat): (result: nat)
- ensures result < 20 + y
+ ensures result < 20 + y // error: specification is not stronger
{ 5 + y }
// g names the result in just the class
function method g(y: nat): (result: nat)
- ensures result < 20 + y
+ ensures result < 20 + y // error: specification is not stronger
{ 5 + y }
// h names the result in just the trait
function method h(y: nat): nat
- ensures h(y) < 20 + y
+ ensures h(y) < 20 + y // error: specification is not stronger
{ 5 + y }
}
diff --git a/Test/dafny4/git-issue245.dfy.expect b/Test/dafny4/git-issue245.dfy.expect
index f154a354f05..52dc0dd66b2 100644
--- a/Test/dafny4/git-issue245.dfy.expect
+++ b/Test/dafny4/git-issue245.dfy.expect
@@ -2,6 +2,10 @@ git-issue245.dfy(43,18): Error: the function must provide an equal or more detai
git-issue245.dfy(47,18): Error: the function must provide an equal or more detailed postcondition than in its parent trait
git-issue245.dfy(51,18): Error: the function must provide an equal or more detailed postcondition than in its parent trait
git-issue245.dfy(55,18): Error: the function must provide an equal or more detailed postcondition than in its parent trait
+git-issue245.dfy(64,18): Error: the function must provide an equal or more detailed postcondition than in its parent trait
+git-issue245.dfy(68,18): Error: the function must provide an equal or more detailed postcondition than in its parent trait
+git-issue245.dfy(72,18): Error: the function must provide an equal or more detailed postcondition than in its parent trait
+git-issue245.dfy(76,18): Error: the function must provide an equal or more detailed postcondition than in its parent trait
git-issue245.dfy(84,18): Error: A postcondition might not hold on this return path.
git-issue245.dfy(85,17): Related location: This is the postcondition that might not hold.
git-issue245.dfy(88,18): Error: A postcondition might not hold on this return path.
@@ -11,4 +15,4 @@ git-issue245.dfy(93,19): Related location: This is the postcondition that might
git-issue245.dfy(96,18): Error: A postcondition might not hold on this return path.
git-issue245.dfy(97,17): Related location: This is the postcondition that might not hold.
-Dafny program verifier finished with 28 verified, 8 errors
+Dafny program verifier finished with 24 verified, 12 errors
diff --git a/Test/git-issues/git-issue-2500.dfy b/Test/git-issues/git-issue-2500.dfy
new file mode 100644
index 00000000000..6764811860f
--- /dev/null
+++ b/Test/git-issues/git-issue-2500.dfy
@@ -0,0 +1,81 @@
+// RUN: %exits-with 4 %dafny /compile:0 "%s" > "%t"
+// RUN: %diff "%s.expect" "%t"
+
+module M {
+ trait {:termination false} Top {
+ predicate ActuallyTrue() ensures ActuallyTrue()
+ }
+ trait {:termination false} Tr extends Top {
+ predicate True(): (ret: bool) ensures ret
+ predicate True'() ensures True'()
+ predicate ActuallyTrue'() ensures ActuallyTrue()
+ predicate ActuallyTrue''() ensures var alsoThis := this; alsoThis.ActuallyTrue()
+ predicate Other(x:nat, free: Tr)
+ ensures x != 0 && free.Other(x-1, free) ==> Other(x-1, free)
+ }
+ method X(tr: Tr) requires tr.ActuallyTrue() {}
+}
+
+class Cl extends M.Tr {
+ constructor() {}
+ predicate True(): (ret: bool)
+ // Missing: ensures ret
+ { false }
+ predicate True'()
+ // Missing: ensures True'()
+ { false }
+ predicate ActuallyTrue()
+ ensures ActuallyTrue()
+ { true }
+ predicate ActuallyTrue'()
+ ensures ActuallyTrue()
+ { true }
+ predicate ActuallyTrue''()
+ // This is logically correct, but the disguised receiver in the Tr spec prevents the override check from passing.
+ ensures ActuallyTrue''()
+ { true }
+ predicate Other(x: nat, free: M.Tr)
+ // Different receiver
+ ensures x != 0 && Other(x-1, free) ==> Other(x-1, free)
+ { false }
+}
+
+method Meh(tr: M.Tr)
+ requires tr.ActuallyTrue'()
+{
+ M.X(tr);
+}
+
+module SSCinClass {
+ trait Trait {
+ predicate Even(i: nat)
+ predicate Odd(i: nat)
+ ensures i == 0 || (i % 2 == 1) == Even(i-1)
+ }
+
+ class Class extends Trait {
+ predicate Even(i: nat)
+ { i == 0 || Odd(i-1) }
+ predicate Odd(i: nat)
+ ensures i == 0 || (i % 2 == 1) == Even(i-1)
+ { i != 0 && Even(i-1) }
+ }
+}
+
+module SSCinBoth {
+ trait Trait {
+ predicate Even(i: nat)
+ ensures i == 0 || (i % 2 == 0) == Odd(i-1)
+ predicate Odd(i: nat)
+ ensures i == 0 || (i % 2 == 1) == Even(i-1)
+ }
+
+ class Class extends Trait {
+ predicate Even(i: nat)
+ ensures i == 0 || (i % 2 == 0) == Odd(i-1)
+ { i == 0 || Odd(i-1) }
+ predicate Odd(i: nat)
+ ensures i == 0 || (i % 2 == 1) == Even(i-1)
+ { i != 0 && Even(i-1) }
+ }
+}
diff --git a/Test/git-issues/git-issue-2500.dfy.expect b/Test/git-issues/git-issue-2500.dfy.expect
new file mode 100644
index 00000000000..64fcebbc922
--- /dev/null
+++ b/Test/git-issues/git-issue-2500.dfy.expect
@@ -0,0 +1,6 @@
+git-issue-2500.dfy(21,12): Error: the function must provide an equal or more detailed postcondition than in its parent trait
+git-issue-2500.dfy(24,12): Error: the function must provide an equal or more detailed postcondition than in its parent trait
+git-issue-2500.dfy(33,12): Error: the function must provide an equal or more detailed postcondition than in its parent trait
+git-issue-2500.dfy(37,12): Error: the function must provide an equal or more detailed postcondition than in its parent trait
+
+Dafny program verifier finished with 26 verified, 4 errors
diff --git a/docs/dev/news/3479.fix b/docs/dev/news/3479.fix
new file mode 100644
index 00000000000..96e3176bae2
--- /dev/null
+++ b/docs/dev/news/3479.fix
@@ -0,0 +1 @@
+To ensure that a `class` correctly implements a `trait`, we perform an override check. This check was previously faulty across `module`s, but works unconditionally now.