Conversation
This reverts commit 0b571fe.
ArquintL
left a comment
There was a problem hiding this comment.
Looks overall good to me but I've left some requests for small changes
| // (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 |
There was a problem hiding this comment.
| // (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 | |
| // (João) Conditional termination measures are a very rarely used feature. They allow defining termination measures | |
| // case-by-case. However, for pure or ghost functions and methods, we need to show that all conditions provided |
| val hasConditionalTerminationMeasureErrors = { | ||
| val conditionalMeasure = spec.terminationMeasures.find(_.isConditional) | ||
| conditionalMeasure match { | ||
| case Some(n) => error(n, "Conditional termination measures are not allowed in pure members.") |
There was a problem hiding this comment.
| case Some(n) => error(n, "Conditional termination measures are not allowed in pure members.") | |
| case Some(n) => error(n, "Conditional termination measures are not allowed for pure or ghost functions and methods.") |
| } | ||
|
|
||
| private[typing] def wellDefIfPureMethod(member: PMethodDecl): Messages = { | ||
| if (member.spec.isPure) { |
There was a problem hiding this comment.
this if condition is redundant
| @@ -61,15 +74,23 @@ trait GhostMemberTyping extends BaseTyping { this: TypeInfoImpl => | |||
|
|
|||
| private[typing] def wellDefIfPureFunction(member: PFunctionDecl): Messages = { | |||
| if (member.spec.isPure) { | |||
There was a problem hiding this comment.
this if condition is redundant
| // spec must come from a ghost or pure function | ||
| private[typing] def wellFoundedIfNeeded(spec: PFunctionSpec): Messages = { |
There was a problem hiding this comment.
three comments:
- can we change the behavior such that this function can be called for any spec and internally checks whether it's a spec for a ghost or pure function / method? That's the intuition I have for these
wellFoundedIf...functions. Alternatively, please add a require stmt that checks this assumption - Maybe I'm missing something but isn't this function only called for explicitly ghost functions and methods? To me, it seems like we do not call this function for actual pure functions and methods
- can we rename this function? Without looking at its implementation, I do not have any clue what
Neededis supposed to do. What aboutwellFoundedIfGhostOrPure?
| //:: ExpectedOutput(type_error) | ||
| decreases _ if b |
There was a problem hiding this comment.
| //:: ExpectedOutput(type_error) | |
| decreases _ if b | |
| //:: ExpectedOutput(type_error) | |
| decreases _ if b // we syntactically disallow conditional termination measures for members that must terminate |
| // 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 |
There was a problem hiding this comment.
I'd split the errors into separate interface functions. Furthermore, I find it a bit funny that one error occurs at the pure keyword whereas the other errors occur at the parameter declaration site but that's just an artifact of how we type-check. We could consider making the following declarations one-liners such that the testcases are not overly restrictive (should we ever indicate missing termination measures, e.g., at the function name
| // 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 | |
| // Type error, pure function does not have termination measures. | |
| //:: ExpectedOutput(type_error) | |
| pure | |
| M1(a int) int | |
| // Type error, we do not permit conditional termination measures for members that must terminate | |
| //:: ExpectedOutput(type_error) | |
| decreases if a == 42 | |
| pure | |
| M2(a int) int | |
| decreases | |
| pure | |
| // Type error, pure function cannot have variadic parameters. | |
| //:: ExpectedOutput(type_error) | |
| M3(a ...int) int | |
| decreases | |
| pure | |
| // Type error, pure function must have exactly one return parameter | |
| //:: ExpectedOutput(type_error) | |
| M4(a int) |
Rebase the fix for issue #841 onto master, whose PR #1019 already introduced the general rejection of conditional termination measures for ghost and pure members (including interface method signatures). The remaining, still-open part of issue #841 is that pure interface method signatures were not required to have termination measures and their signatures were not validated. This resolves the merge by: - Extracting the signature-level pure checks into a shared 'wellDefIfPureSpec', now used by pure functions, pure methods, and pure interface method sigs. - Requiring pure interface method sigs to have a termination measure. - Keeping master's conditional/wildcard-measure and mayInit handling. - Adding 'decreases' to the pure ByteOrder interface methods in the stdlib stub. - Restructuring the regression test so each case triggers exactly one error.
…ddedInterfaces-fail3 The requirement that pure members carry a termination measure is gated behind 'disableCheckTerminationPureFns', which the regression test suite disables, so that case cannot be exercised here. The test now covers the checks that always apply to pure interface method signatures: exactly one result, non-variadic parameters, and rejection of conditional termination measures. embeddedInterfaces-fail3 declared 'pure g()' with no result, which is now rejected at the interface itself (issue #841). Give it a result so the test keeps exercising its intended error: a non-pure method implementing a pure interface method.
|
I've rebased this branch on top of that, which changes the picture for most of your comments:
The remaining unique change is the interface part of #841: One thing worth flagging: because the regression suite forces CI is green. PTAL when you get a chance. Generated by Claude Code |
You could add a testcase that uses an in-file config to set |
…ace methods The regression-test suite runs with disableCheckTerminationPureFns = true, so the requirement that a pure interface method carry a termination measure cannot be exercised there. The type-checking unit tests, however, use the default Config() in which the check is enabled, so we add two tests: a pure interface method without a termination measure is rejected, and one with a measure is accepted. Addresses ArquintL's review comment on issue #841.
| // (e.g., `mayInit`, or the requirement to have a termination measure) are added by the callers. | ||
| // Precondition: `spec.isPure`. | ||
| private[typing] def wellDefIfPureSpec(node: PCodeRootWithResult, args: Vector[PParameter], spec: PFunctionSpec): Messages = { | ||
| isSingleResultArg(node) ++ |
There was a problem hiding this comment.
| isSingleResultArg(node) ++ | |
| requires(spec.isPure) | |
| isSingleResultArg(node) ++ |
| isSingleResultArg(node) ++ | ||
| isPurePostcondition(spec) ++ | ||
| pureMembersCannotHavePreserves(spec) ++ | ||
| nonVariadicArguments(args) |
There was a problem hiding this comment.
why don't we check here the presence of termination measures in a uniform way?
| sigsWithWildcardMeasuresErrors ++ | ||
| sigsWithConditionalMeasuresErrors ++ | ||
| pureSigErrors ++ |
There was a problem hiding this comment.
I really don't want to blame anyone here as I guess things happened incrementally, but I'm not happy with this design:
wellDefIfPureSpecis supposed to make the checks uniform for pure members...- but then
pureSigErrorschecks for the presence of termination measures... - without however checking whether the present measures are meaningful (non-wildecard & non-conditional)
| decreases | ||
| pure | ||
| M1(a int) |
There was a problem hiding this comment.
| decreases | |
| pure | |
| M1(a int) | |
| decreases pure M1(a int) |
| pure | ||
| M2(a int) int |
There was a problem hiding this comment.
| pure | |
| M2(a int) int | |
| pure M2(a int) int |
| pure | ||
| //:: ExpectedOutput(type_error) | ||
| M3(a ...int) int |
There was a problem hiding this comment.
| pure | |
| //:: ExpectedOutput(type_error) | |
| M3(a ...int) int | |
| //:: ExpectedOutput(type_error) | |
| pure M3(a ...int) int |
| pure | ||
| //:: ExpectedOutput(type_error) | ||
| decreases _ if b // we syntactically disallow conditional termination measures for members that must terminate | ||
| func f(b bool) bool |
There was a problem hiding this comment.
| pure | |
| //:: ExpectedOutput(type_error) | |
| decreases _ if b // we syntactically disallow conditional termination measures for members that must terminate | |
| func f(b bool) bool | |
| //:: ExpectedOutput(type_error) | |
| decreases _ if b // we syntactically disallow conditional termination measures for members that must terminate | |
| pure func f(b bool) bool |
| test("Typing: a pure interface method without a termination measure is not well-defined") { | ||
| assert (!frontend.isWellDef(pureInterfaceType(Vector.empty)).valid) | ||
| } | ||
|
|
||
| test("Typing: a pure interface method with a termination measure is well-defined") { | ||
| val measure = PTupleTerminationMeasure(Vector.empty, None) | ||
| assert (frontend.isWellDef(pureInterfaceType(Vector(measure))).valid) | ||
| } |
There was a problem hiding this comment.
haha not quite what I had in mind but I guess it tests the same ^^
|
Oops, I saw that Claude went ahead and commented on this thread and marked this for review without me expecting or requesting any of these actions. Sorry that this led you to spend your time on something that was not ready for review @ArquintL |
Address review feedback on the scattered measure checks for pure interface methods: - wellDefIfPureSpec now validates the termination measure of every pure member (function, method, and interface method signature): it must be present (unless disableCheckTerminationPureFns) and non-conditional. It also asserts its precondition that the spec is pure. - wellFoundedIfNeeded / noConditionalMeasureIfGhostOrPure are renamed to wellFoundedIfGhost / noConditionalMeasureIfGhost and restricted to ghost (non-pure) members, since pure members are now handled uniformly; this avoids double-reporting. - The interface handling in wellDefType delegates pure-signature checks entirely to wellDefIfPureSpec and only rejects conditional measures for ghost (non-pure) signatures; wildcard measures remain rejected for all interface signatures. - Restructure the 000841 regression test into one-liner method signatures. Removes the TypeTypingUnitTests cases added earlier: the anonymous-interface harness does not exercise interface method-signature checks, so they did not test the intended behaviour.
'decreases pure M1(a int)' does not parse: the decreases clause consumes the following tokens as the measure and rejects the 'pure' keyword. Put 'decreases' on its own line; 'pure' directly before the method name parses fine.
This PR fixes issue #841: pure method signatures declared in an interface are now held to the same requirements as pure function/method implementations.
Since this PR was first opened,
mastermerged #1019, which already generalises the rejection of conditional termination measures to ghost and pure members (including interface method signatures) and requires termination measures for pure/ghost implementations. This branch has been rebased on top of that, so its remaining, unique contribution is focused on interfaces:wellDefIfPureSpec(exactly one result, non-variadic parameters, pure postconditions, nopreservesclauses).disableCheckTerminationPureFnsexactly like pure functions and methods.decreasesto the pure methods of theByteOrderstdlib stub, and restructures the000841regression test.Note: because the regression-test suite runs with
disableCheckTerminationPureFns = true, the "must have a termination measure" case is not exercised by the suite (same as for pure functions); the000841test covers the always-checked rules (single result, non-variadic parameters, rejection of conditional measures).