-
Notifications
You must be signed in to change notification settings - Fork 262
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
fix: Cross-modular override check #3223
Changes from 16 commits
1f0d29b
b99bff8
fbedb7a
342be57
d37cf04
d1eb5a8
117585e
922049d
1dc1ed4
4c46f74
0a3d964
2ee54f7
0c3cb41
54e1cfd
2e67b54
923f38a
7d8fe8f
d9ae00a
ee6c02d
a86643c
01b2f4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -258,12 +258,11 @@ public Boogie.IdentifierExpr FunctionContextHeight() { | |
return new Boogie.IdentifierExpr(Token.NoToken, "$FunctionContextHeight", Boogie.Type.Int); | ||
} | ||
|
||
public Boogie.Expr HeightContext(ICallable m) { | ||
public Boogie.Expr HeightContext(ICallable m, bool reducedScope = false) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: consider using "intermediateScope" here instead, or renaming the parameter, for consistency. |
||
Contract.Requires(m != null); | ||
// free requires fh == FunctionContextHeight; | ||
var module = m.EnclosingModule; | ||
Boogie.Expr context = | ||
Boogie.Expr.Eq(Boogie.Expr.Literal(module.CallGraph.GetSCCRepresentativePredecessorCount(m)), FunctionContextHeight()); | ||
var visibilityLevel = m.EnclosingModule.CallGraph.GetSCCRepresentativePredecessorCount(m); | ||
Boogie.Expr context = Boogie.Expr.Eq(MkFunctionHeight(visibilityLevel, reducedScope), FunctionContextHeight()); | ||
return context; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2054,7 +2054,7 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis | |
// (mh == ModuleContextHeight && 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 && | ||
|
@@ -2157,10 +2157,9 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis | |
foreach (AttributedExpression req in f.Req) { | ||
pre = BplAnd(pre, etran.TrExpr(Substitute(req.E, null, substMap))); | ||
} | ||
// 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 | ||
var visibilityLevel = f.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCCRepresentativePredecessorCount(f); | ||
Expr useViaContext = !InVerificationScope(f) ? Bpl.Expr.True : Expr.Lt(MkFunctionHeight(visibilityLevel), etran.FunctionContextHeight()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good. I like this |
||
// 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)); | ||
|
@@ -2255,15 +2254,14 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis | |
return (olderParameterCount, olderCondition); | ||
} | ||
|
||
Bpl.Expr AxiomActivation(Function f, ExpressionTranslator etran) { | ||
Bpl.Expr AxiomActivation(Function f, ExpressionTranslator etran, bool requiresFullScope = false) { | ||
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()); | ||
var visibilityLevel = f.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCCRepresentativePredecessorCount(f); | ||
return Expr.Le(MkFunctionHeight(visibilityLevel, !requiresFullScope), etran.FunctionContextHeight()); | ||
} else { | ||
return Bpl.Expr.True; | ||
} | ||
|
@@ -2303,7 +2301,7 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List<Formal> lits) { | |
// 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 && | ||
|
@@ -2508,12 +2506,11 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List<Formal> lits) { | |
return null; | ||
} | ||
|
||
// useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight) | ||
ModuleDefinition mod = f.EnclosingClass.EnclosingModuleDefinition; | ||
// useViaContext: fh < FunctionContextHeight | ||
var visibilityLevel = f.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCCRepresentativePredecessorCount(f); | ||
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(MkFunctionHeight(visibilityLevel), etran.FunctionContextHeight()); | ||
// ante := (useViaContext && typeAnte && pre) | ||
ante = BplAnd(useViaContext, BplAnd(ante, pre)); | ||
|
||
|
@@ -3487,65 +3484,42 @@ 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<Bpl.Expr> argsC = new List<Bpl.Expr>(); | ||
List<Bpl.Expr> argsT = new List<Bpl.Expr>(); | ||
// 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<Bpl.Expr>(); | ||
|
||
// 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 | ||
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))); | ||
} | ||
// 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. | ||
var sub = new FunctionCallSubstituter(null, substMap, typeMap, f.OverriddenFunction, f); | ||
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))); | ||
} | ||
} | ||
} | ||
|
@@ -4769,7 +4743,7 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { | |
Contract.Requires(predef != null); | ||
Contract.Ensures(Contract.Result<Bpl.Expr>() != null); | ||
|
||
if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr || expr is WildcardExpr || expr is BoogieWrapper) { | ||
if (expr is LiteralExpr or ThisExpr or IdentifierExpr or WildcardExpr or BoogieWrapper) { | ||
return Bpl.Expr.True; | ||
} else if (expr is DisplayExpression) { | ||
DisplayExpression e = (DisplayExpression)expr; | ||
|
@@ -10756,6 +10730,14 @@ List<Variable> MkTyParamFormals(List<TypeParameter> args, bool includeWhereClaus | |
return null; | ||
} | ||
|
||
// We use the $FunctionContextHeight to restrict the applicability of certain axioms. The entity at the end of a | ||
// dependency chain has the highest number. To get more granular control over the visibility, we extend every | ||
// visibility level by a precursory intermediate level. This additional level is helpful for proofs that would be | ||
// disturbed by axioms that are visible the the final level. | ||
static Bpl.Expr MkFunctionHeight(int visibilityLevel, bool intermediateScope = false) { | ||
return Expr.Literal(visibilityLevel * 2 + (intermediateScope ? 0 : 1)); | ||
} | ||
|
||
public static void MapM<A>(IEnumerable<A> xs, Action<A> K) { | ||
Contract.Requires(xs != null); | ||
Contract.Requires(K != null); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still a TODO or have you fixed it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither? I couldn't find a situation where it affects the way I use it, Boogie has more shallow types, but there may be others, where getting this right is crucial.