-
Notifications
You must be signed in to change notification settings - Fork 37
atomic methods and invariants
#983
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
base: master
Are you sure you want to change the base?
Changes from 24 commits
b88b89b
0ec1a01
cdb92b1
a2d5080
39198a3
62e7a86
979d876
9db8f6f
30d9b0f
8aa52cb
61a4218
4d950a2
9e4af81
127928c
ada03c9
97db219
db94648
5b9253b
8403fd5
b7be63e
7cac033
dec8198
aed4e4a
627147e
de617aa
483cc27
4c473ab
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 |
|---|---|---|
|
|
@@ -7,7 +7,7 @@ | |
| package viper.gobra.frontend | ||
|
|
||
| import com.typesafe.scalalogging.LazyLogging | ||
| import viper.gobra.ast.frontend.{PExpression, AstPattern => ap, _} | ||
| import viper.gobra.ast.frontend.{AstPattern => ap, _} | ||
| import viper.gobra.ast.{internal => in} | ||
| import viper.gobra.frontend.PackageResolver.RegularImport | ||
| import viper.gobra.frontend.Source.TransformableSource | ||
|
|
@@ -16,7 +16,7 @@ import viper.gobra.frontend.info.base.Type._ | |
| import viper.gobra.frontend.info.base.{BuiltInMemberTag, Type, SymbolTable => st} | ||
| import viper.gobra.frontend.info.implementation.resolution.MemberPath | ||
| import viper.gobra.frontend.info.{ExternalTypeInfo, TypeInfo} | ||
| import viper.gobra.reporting.Source.{AutoImplProofAnnotation, ImportPreNotEstablished, MainPreNotEstablished} | ||
| import viper.gobra.reporting.Source.{AutoImplProofAnnotation, ImportPreNotEstablished, InvariantMightBeOpenAnnotation, InvariantNotRestoredAnnotation, IsInvariantAnnotation, MainPreNotEstablished} | ||
| import viper.gobra.reporting.{DesugaredMessage, Source} | ||
| import viper.gobra.theory.Addressability | ||
| import viper.gobra.translator.Names | ||
|
|
@@ -526,6 +526,13 @@ object Desugar extends LazyLogging { | |
| in.GlobalVarProxy(v.id.name, name)(meta(v.decl, v.context.getTypeInfo)) | ||
| } | ||
|
|
||
| def openInvsVar : in.LocalVar = | ||
| in.LocalVar( | ||
| "openInvariants", | ||
|
ArquintL marked this conversation as resolved.
|
||
| in.SetT( | ||
| in.PredT(Vector.empty, Addressability.exclusiveVariable), | ||
| Addressability.exclusiveVariable))(Source.Parser.Internal) | ||
|
ArquintL marked this conversation as resolved.
|
||
|
|
||
| def constDeclD(block: PConstDecl): Vector[in.GlobalConstDecl] = block.specs.flatMap(constSpecD) | ||
|
|
||
| def constSpecD(decl: PConstSpec): Vector[in.GlobalConstDecl] = decl.left.flatMap(l => info.regular(l) match { | ||
|
|
@@ -692,7 +699,7 @@ object Desugar extends LazyLogging { | |
| val capturedWithAliases = (captured.map { v => in.Ref(localVarD(outerCtx, info)(v))(meta(v, info)) } zip capturedPar) | ||
|
|
||
| val bodyOpt = decl.body.map{ case (_, s) => | ||
| val vars = argSubs.flatten ++ capturedSubs ++ returnSubs.flatten | ||
| val vars = argSubs.flatten ++ capturedSubs ++ returnSubs.flatten ++ Vector(openInvsVar) | ||
| val varsInit = vars map (v => in.Initialization(v)(v.info)) | ||
| val body = varsInit ++ argInits ++ capturedInits ++ Vector(blockD(ctx, info)(s)) | ||
| in.MethodBody(vars, in.MethodBodySeqn(body)(fsrc), resultAssignments)(fsrc) | ||
|
|
@@ -890,7 +897,7 @@ object Desugar extends LazyLogging { | |
|
|
||
|
|
||
| val bodyOpt = decl.body.map{ case (_, s) => | ||
| val vars = recvSub.toVector ++ argSubs.flatten ++ returnSubs.flatten | ||
| val vars = recvSub.toVector ++ argSubs.flatten ++ returnSubs.flatten ++ Vector(openInvsVar) | ||
| val varsInit = vars map (v => in.Initialization(v)(v.info)) | ||
| val body = varsInit ++ recvInits ++ argInits ++ Vector(blockD(ctx, info)(s)) | ||
| in.MethodBody(vars, in.MethodBodySeqn(body)(fsrc), resultAssignments)(fsrc) | ||
|
|
@@ -1587,6 +1594,19 @@ object Desugar extends LazyLogging { | |
| case n@PForStmt(pre, cond, post, spec, body) => | ||
| unit(block( // is a block because 'pre' might define new variables | ||
| for { | ||
| oldOpenInvs <- freshDeclaredExclusiveVar( | ||
| in.SetT(in.PredT(Vector.empty, Addressability.Exclusive), Addressability.Exclusive), n, info | ||
| )(src) | ||
| initOldOpenInvs = in.SingleAss(in.Assignee.Var(oldOpenInvs), openInvsVar)(src) | ||
| // This invariant allows for loops that open critical regions without havocking the value | ||
| // of openInvs, which would prevent opening invariants in the body of the loop or after the loop. | ||
| // This invariant does not impose any restrictions on the user, given that the syntax of critical | ||
| // regions guarantees that no invariant may be open for longer than one loop iteration. | ||
| // TODO: do the same for range loops | ||
| openInvsDoesNotChange: in.Assertion = in.ExprAssertion( | ||
| in.EqCmp(oldOpenInvs, openInvsVar)(src) | ||
| )(src) | ||
|
|
||
| dPre <- maybeStmtD(ctx)(pre)(src) | ||
| (dCondPre, dCond) <- prelude(exprD(ctx, info)(cond)) | ||
| (dInvPre, dInv) <- prelude(sequence(spec.invariants map assertionD(ctx, info))) | ||
|
|
@@ -1605,8 +1625,8 @@ object Desugar extends LazyLogging { | |
| dPost <- maybeStmtD(ctx)(post)(src) | ||
|
|
||
| wh = in.Seqn( | ||
| Vector(dPre) ++ dCondPre ++ dInvPre ++ dTerPre ++ Vector( | ||
| in.While(dCond, dInv, dTer, in.Block(Vector(continueLoopLabelProxy), | ||
| Vector(initOldOpenInvs, dPre) ++ dCondPre ++ dInvPre ++ dTerPre ++ Vector( | ||
| in.While(dCond, openInvsDoesNotChange +: dInv, dTer, in.Block(Vector(continueLoopLabelProxy), | ||
| Vector(dBody, continueLoopLabel, dPost) ++ dCondPre ++ dInvPre ++ dTerPre | ||
| )(src))(src), breakLoopLabel | ||
| ) | ||
|
|
@@ -1903,6 +1923,91 @@ object Desugar extends LazyLogging { | |
| } yield in.Outline(name, pres, posts, terminationMeasures, annotations, dummyBody, trusted = true)(src) | ||
| } | ||
|
|
||
| case n@PCritical(expr, stmts) => | ||
| val exprSrc: Meta = meta(expr, info) | ||
| val exprSrcIsInv = meta(expr, info).createAnnotatedInfo(IsInvariantAnnotation()) | ||
| val exprSrcIsOpen = meta(expr, info).createAnnotatedInfo(InvariantMightBeOpenAnnotation()) | ||
| val exprSrcNotRestored = meta(expr, info).createAnnotatedInfo(InvariantNotRestoredAnnotation()) | ||
|
|
||
| for { | ||
| e <- goE(expr) | ||
|
|
||
| // check if the invariant is an invariant | ||
| isInv = in.Assert( | ||
| in.ExprAssertion( | ||
| in.PureFunctionCall( | ||
| functionProxy(InvariantFunctionTag, Vector(e.typ))(exprSrcIsInv), | ||
| Vector(e), | ||
| in.BoolT(Addressability.Exclusive), | ||
| false | ||
| )(exprSrcIsInv) | ||
| )(exprSrcIsInv) | ||
| )(exprSrcIsInv) | ||
| _ <- write(isInv) | ||
|
|
||
| // check the invariant is not open yet | ||
| checkIsOpen = in.Assert( | ||
| in.ExprAssertion( | ||
| in.Negation( | ||
| in.Contains( | ||
| e, | ||
| openInvsVar | ||
| )(exprSrcIsOpen) | ||
| )(exprSrcIsOpen) | ||
| )(exprSrcIsOpen) | ||
| )(exprSrcIsOpen) | ||
| _ <- write(checkIsOpen) | ||
|
ArquintL marked this conversation as resolved.
|
||
|
|
||
| // mark invariant as open | ||
| markOpen = in.SingleAss( | ||
| in.Assignee.Var(openInvsVar), | ||
| in.Union( | ||
| openInvsVar, | ||
| in.SetLit( | ||
| in.PredT(Vector.empty, Addressability.Exclusive), | ||
| Vector(e) | ||
| )(exprSrc), | ||
| in.PredT(Vector.empty, Addressability.Exclusive) | ||
| )(exprSrc) | ||
| )(exprSrc) | ||
| _ <- write(markOpen) | ||
|
|
||
| // inhale the invariant | ||
| inhaleInv = in.Inhale( | ||
| in.Access( | ||
| in.Accessible.PredExpr(in.PredExprInstance(e, Vector.empty)(exprSrc)), | ||
| in.FullPerm(exprSrc) | ||
| )(exprSrc) | ||
| )(exprSrc) | ||
| _ <- write(inhaleInv) | ||
|
|
||
| // stmts | ||
|
Member
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. I hope the checks disallow gotos in here
Contributor
Author
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. We do, the only actual statements we allow are calls to atomic functions whose parameters have been evaluated and assignments to local exclusive variables whose rhs are calls to atomic functions
Member
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. Could you please document all these side-conditions at the place where we type-check |
||
| stmtsD <- sequence(stmts.map(goS)) | ||
| _ <- write(stmtsD : _*) | ||
|
|
||
| // exhale the invariant | ||
| exhaleInv = in.Exhale( | ||
| in.Access( | ||
| in.Accessible.PredExpr(in.PredExprInstance(e, Vector.empty)(exprSrcNotRestored)), | ||
| in.FullPerm(exprSrcNotRestored) | ||
| )(exprSrcNotRestored) | ||
| )(exprSrcNotRestored) | ||
|
|
||
|
jcp19 marked this conversation as resolved.
|
||
| // mark invariant as closed | ||
| markClosed = in.SingleAss( | ||
| in.Assignee.Var(openInvsVar), | ||
| in.SetMinus( | ||
| openInvsVar, | ||
| in.SetLit( | ||
| in.PredT(Vector.empty, Addressability.Exclusive), | ||
| Vector(e) | ||
| )(exprSrc), | ||
| in.PredT(Vector.empty, Addressability.Exclusive) | ||
| )(exprSrc) | ||
| )(exprSrc) | ||
| _ <- write(markClosed) | ||
|
|
||
| } yield exhaleInv | ||
|
Member
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. Wouldn't it be cleaner do define an encoding for the critical stmt instead of making the desugarer even larger?
Contributor
Author
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. What do you mean exactly? Introducing a
Member
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. Yes exactly |
||
|
|
||
| case n@PContinue(label) => unit(in.Continue(label.map(x => x.name), nm.fetchContinueLabel(n, info))(src)) | ||
|
|
||
|
|
@@ -3704,7 +3809,7 @@ object Desugar extends LazyLogging { | |
| backendAnnotations = Vector.empty, | ||
| body = Some( | ||
| in.MethodBody( | ||
| decls = Vector(), | ||
| decls = Vector(openInvsVar), | ||
| postprocessing = Vector(), | ||
| seqn = in.MethodBodySeqn{ | ||
| // Init all global variables declared in the file (not all declarations in the package!). | ||
|
|
@@ -4823,7 +4928,7 @@ object Desugar extends LazyLogging { | |
| unit(in.MPredicateAccess(implicitThisD(recvType)(src), proxy, convertArgs(dArgs))(src)) | ||
|
|
||
| case b: ap.BuiltInPredicate => | ||
| val fproxy = fpredicateProxy(b.id, info) | ||
| val fproxy = fpredicateProxy(b.symb.tag, convertArgs(dArgs).map(_.typ))(src) | ||
| unit(in.FPredicateAccess(fproxy, convertArgs(dArgs))(src)) | ||
|
|
||
| case b: ap.BuiltInReceivedPredicate => | ||
|
|
||
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.
Is there a reason against using the same order as on L. 183?
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.
well, the two orders never matched anyway, but I don't mind changing that
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.
That's what I noticed too. Double checking whether we assigned all of them just became increasingly difficult ^^