-
Notifications
You must be signed in to change notification settings - Fork 38
Fix issue #841 #919
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?
Fix issue #841 #919
Changes from all commits
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,17 +7,20 @@ | |||||||||
| package viper.gobra.frontend.info.implementation.typing.ghost | ||||||||||
|
|
||||||||||
| import org.bitbucket.inkytonik.kiama.util.Messaging.{Messages, error, noMessages} | ||||||||||
| import viper.gobra.ast.frontend.{PBlock, PCodeRootWithResult, PExplicitGhostMember, PFPredicateDecl, PFunctionDecl, PFunctionSpec, PGhostMember, PIdnUse, PImplementationProof, PMember, PMPredicateDecl, PMethodDecl, PMethodImplementationProof, PParameter, PReturn, PVariadicType, PWithBody} | ||||||||||
| import viper.gobra.ast.frontend._ | ||||||||||
| import viper.gobra.frontend.info.base.SymbolTable.{MPredicateSpec, MethodImpl, MethodSpec} | ||||||||||
| import viper.gobra.frontend.info.base.Type.{InterfaceT, Type, UnknownType} | ||||||||||
| import viper.gobra.frontend.info.implementation.TypeInfoImpl | ||||||||||
| import viper.gobra.frontend.info.implementation.typing.BaseTyping | ||||||||||
| import viper.gobra.util.Violation | ||||||||||
|
|
||||||||||
| trait GhostMemberTyping extends BaseTyping { this: TypeInfoImpl => | ||||||||||
|
|
||||||||||
| private[typing] def wellDefGhostMember(member: PGhostMember): Messages = member match { | ||||||||||
| case PExplicitGhostMember(_) => noMessages | ||||||||||
| case PExplicitGhostMember(m) => m match { | ||||||||||
| case f: PFunctionDecl => wellFoundedIfNeeded(f.spec) | ||||||||||
| case m: PMethodDecl => wellFoundedIfNeeded(m.spec) | ||||||||||
| case _ => noMessages | ||||||||||
| } | ||||||||||
|
|
||||||||||
| case PFPredicateDecl(_, args, body) => | ||||||||||
| body.fold(noMessages)(assignableToSpec) ++ nonVariadicArguments(args) | ||||||||||
|
|
@@ -28,28 +31,38 @@ trait GhostMemberTyping extends BaseTyping { this: TypeInfoImpl => | |||||||||
| nonVariadicArguments(args) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private[typing] def wellFoundedIfNeeded(member: PMember): Messages = { | ||||||||||
| val spec = member match { | ||||||||||
| case m: PMethodDecl => m.spec | ||||||||||
| case f: PFunctionDecl => f.spec | ||||||||||
| case _ => Violation.violation("Unexpected member type") | ||||||||||
| // spec must come from a ghost or pure function | ||||||||||
| private[typing] def wellFoundedIfNeeded(spec: PFunctionSpec): Messages = { | ||||||||||
| val needsMeasure = !config.disableCheckTerminationPureFns | ||||||||||
| if (needsMeasure) { | ||||||||||
| isValidMeasureForGhostOrPure(spec) | ||||||||||
| } else noMessages | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private def isValidMeasureForGhostOrPure(spec: PFunctionSpec): Messages = { | ||||||||||
| // TODO: eventually, we should deprecate the flag `config.disableCheckTerminationPureFns` | ||||||||||
| val hasTerminationMeasureErrors = | ||||||||||
| error(spec, s"Pure and ghost functions and methods must have termination measures.", | ||||||||||
| spec.terminationMeasures.isEmpty) | ||||||||||
| // (João) Conditional termination measures are a very rarely used feature. They allow defining termination measures | ||||||||||
| // case-per-case. However, for pure or ghost functions and methods, we need to show that all conditions provided | ||||||||||
|
Comment on lines
+47
to
+48
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.
Suggested change
|
||||||||||
| // do cover all possible inputs, otherwise the function is not guaranteed to terminate. Implementing such a | ||||||||||
| // check would not be hard, but it would be more cumbersome (and in general, less efficient) than this syntactic check. | ||||||||||
| // If this feature is requested, I am happy to implement the more complete check, but for now, I will stick to | ||||||||||
| // this syntactic check. | ||||||||||
| val hasConditionalTerminationMeasureErrors = { | ||||||||||
| val conditionalMeasure = spec.terminationMeasures.find(_.isConditional) | ||||||||||
| conditionalMeasure match { | ||||||||||
| case Some(n) => error(n, "Conditional termination measures are not allowed in pure members.") | ||||||||||
|
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.
Suggested change
|
||||||||||
| case None => noMessages | ||||||||||
| } | ||||||||||
| } | ||||||||||
| val hasMeasureIfNeeded = | ||||||||||
| if (spec.isPure || isEnclosingGhost(member)) | ||||||||||
| config.disableCheckTerminationPureFns || spec.terminationMeasures.nonEmpty | ||||||||||
| else | ||||||||||
| true | ||||||||||
| val needsMeasureError = | ||||||||||
| error(member, "All pure or ghost functions and methods must have termination measures, but none was found for this member.", !hasMeasureIfNeeded) | ||||||||||
| needsMeasureError | ||||||||||
| hasTerminationMeasureErrors ++ hasConditionalTerminationMeasureErrors | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private[typing] def wellDefIfPureMethod(member: PMethodDecl): Messages = { | ||||||||||
| if (member.spec.isPure) { | ||||||||||
|
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. this if condition is redundant |
||||||||||
| isSingleResultArg(member) ++ | ||||||||||
| isSinglePureReturnExpr(member) ++ | ||||||||||
| isPurePostcondition(member.spec) ++ | ||||||||||
| nonVariadicArguments(member.args) | ||||||||||
| wellDefIfPureSpec(member.spec, member.args, member.result) ++ isSinglePureReturnExpr(member) | ||||||||||
| } else noMessages | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
@@ -61,15 +74,23 @@ trait GhostMemberTyping extends BaseTyping { this: TypeInfoImpl => | |||||||||
|
|
||||||||||
| private[typing] def wellDefIfPureFunction(member: PFunctionDecl): Messages = { | ||||||||||
| if (member.spec.isPure) { | ||||||||||
|
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. this if condition is redundant |
||||||||||
| isSingleResultArg(member) ++ | ||||||||||
| isSinglePureReturnExpr(member) ++ | ||||||||||
| isPurePostcondition(member.spec) ++ | ||||||||||
| nonVariadicArguments(member.args) | ||||||||||
| wellDefIfPureSpec(member.spec, member.args, member.result) ++ isSinglePureReturnExpr(member) | ||||||||||
| } else noMessages | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private def isSingleResultArg(member: PCodeRootWithResult): Messages = { | ||||||||||
| error(member, "For now, pure methods and pure functions must have exactly one result argument", member.result.outs.size != 1) | ||||||||||
| // This implements checks that are common to pure functions and pure methods. Checks that are specific to either | ||||||||||
| // (e.g., checks that may depend on the receiver of a method) should be implemented elsewhere. | ||||||||||
| private[typing] def wellDefIfPureSpec(spec: PFunctionSpec, args: Vector[PParameter], result: PResult) : Messages = { | ||||||||||
| if (spec.isPure) { | ||||||||||
| isSingleResultArg(result) ++ | ||||||||||
| isPurePostcondition(spec) ++ | ||||||||||
| nonVariadicArguments(args) ++ | ||||||||||
| wellFoundedIfNeeded(spec) | ||||||||||
| } else noMessages | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private def isSingleResultArg(result: PResult): Messages = { | ||||||||||
| error(result, "For now, pure methods and pure functions must have exactly one result argument", result.outs.size != 1) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private def isSinglePureReturnExpr(member: PWithBody): Messages = { | ||||||||||
|
|
@@ -93,6 +114,8 @@ trait GhostMemberTyping extends BaseTyping { this: TypeInfoImpl => | |||||||||
| p: PParameter => error(p, s"Pure members cannot have variadic arguments, but got $p", p.typ.isInstanceOf[PVariadicType]) | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
||||||||||
|
|
||||||||||
| override lazy val localImplementationProofs: Vector[(Type, InterfaceT, Vector[String], Vector[String])] = { | ||||||||||
| val implementationProofs = tree.root.programs.flatMap(_.declarations.collect{ case m: PImplementationProof => m}) | ||||||||||
| implementationProofs.flatMap { ip => | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,18 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Any copyright is dedicated to the Public Domain. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // http://creativecommons.org/publicdomain/zero/1.0/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package issue000841 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type J interface { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Type error, pure function does not have termination measures. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //:: ExpectedOutput(type_error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Type error, pure function cannot have variadic parameters. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //:: ExpectedOutput(type_error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| M(a ...int) int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+7
to
+12
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'd split the errors into separate interface functions. Furthermore, I find it a bit funny that one error occurs at the
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pure | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //:: ExpectedOutput(type_error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| decreases _ if b | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+17
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.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func f(b bool) bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
three comments:
wellFoundedIf...functions. Alternatively, please add a require stmt that checks this assumptionNeededis supposed to do. What aboutwellFoundedIfGhostOrPure?