From d731efa64234015f6f785c31988bbc9314f0b51b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 2 Sep 2022 03:05:08 -0700 Subject: [PATCH] feat: Generate a warning when 'old' has no effect (#2610) Fixes #1989 If the argument to `old` has no dereferences of the mutable heap, generate a warning saying that the `old` has no effect. This has been a common source of errors and confusion. --- RELEASE_NOTES.md | 2 +- Source/Dafny/AST/DafnyAst.cs | 20 +- Source/Dafny/AST/FreeVariablesUtil.cs | 18 +- Source/Dafny/Resolver.cs | 16 +- Source/Dafny/Rewriter.cs | 10 + Source/Dafny/Triggers/TriggerUtils.cs | 2 +- Source/Dafny/UselessOldLinter.cs | 51 ++++ Source/Dafny/Verifier/Translator.cs | 6 +- Test/VSComp2010/Problem4-Queens.dfy | 1 - Test/allocated1/dafny0/Simple.dfy.expect | 1 + Test/allocated1/dafny0/SmallTests.dfy.expect | 2 + .../dafny0/Twostate-Verification.dfy.expect | 1 + Test/concurrency/12-MutexLifetime-long.dfy | 7 +- Test/concurrency/12-MutexLifetime-short.dfy | 7 +- .../12-MutexLifetime-short.dfy.expect | 2 +- Test/dafny0/LabeledAsserts.dfy.expect | 2 + Test/dafny0/LabelsOldAt.dfy.expect | 1 + Test/dafny0/OpaqueTypeWithMembers.dfy | 6 +- Test/dafny0/OpaqueTypeWithMembers.dfy.expect | 3 + Test/dafny0/Simple.dfy | 2 +- Test/dafny0/Simple.dfy.expect | 1 + Test/dafny0/SmallTests.dfy.expect | 2 + Test/dafny0/Twostate-Verification.dfy | 46 ++-- Test/dafny0/Twostate-Verification.dfy.expect | 23 ++ Test/dafny0/TypeMembers.dfy | 2 +- Test/dafny0/TypeMembers.dfy.expect | 1 + Test/git-issues/git-issue-1163.dfy | 2 +- Test/git-issues/git-issue-1163.dfy.expect | 1 + Test/git-issues/git-issue-1989.dfy | 238 ++++++++++++++++++ Test/git-issues/git-issue-1989.dfy.expect | 31 +++ Test/refman/Example-Old.dfy.expect | 3 + Test/refman/Example-Old3.dfy.expect | 1 + Test/traits/TraitOverride2.dfy | 4 - Test/traits/TraitOverride2.dfy.expect | 2 +- ...ping-is-hard-to-decide-modulo-equality.dfy | 2 +- .../old-is-a-special-case-for-triggers.dfy | 29 ++- ...-is-a-special-case-for-triggers.dfy.expect | 30 ++- 37 files changed, 508 insertions(+), 70 deletions(-) create mode 100644 Source/Dafny/UselessOldLinter.cs create mode 100644 Test/git-issues/git-issue-1989.dfy create mode 100644 Test/git-issues/git-issue-1989.dfy.expect diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d6bbcea573d..a5a276b6120 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,7 @@ - fix: Missing related position in failing precondition (https://github.com/dafny-lang/dafny/pull/2658) - fix: No more IDE crashing on the elephant operator (https://github.com/dafny-lang/dafny/pull/2668) +- feat: generate warning when 'old' has no effect # 3.8.1 @@ -15,7 +16,6 @@ - fix: Better messages on hovering failing postconditions in IDE (https://github.com/dafny-lang/dafny/pull/2654) - fix: Better error reporting on counter-examples if an option is not provided (https://github.com/dafny-lang/dafny/pull/2650) - # 3.8.0 - fix: Use the right bitvector comparison in decrease checks diff --git a/Source/Dafny/AST/DafnyAst.cs b/Source/Dafny/AST/DafnyAst.cs index 7a609536d6c..14aee1ffdc1 100644 --- a/Source/Dafny/AST/DafnyAst.cs +++ b/Source/Dafny/AST/DafnyAst.cs @@ -6723,6 +6723,24 @@ public Statement(IToken tok, IToken endTok) Contract.Requires(endTok != null); } + /// + /// Returns the non-null expressions of this statement proper (that is, do not include the expressions of substatements). + /// Filters all sub expressions that are not part of specifications + /// + public IEnumerable SubExpressionsIncludingTransitiveSubStatements { + get { + foreach (var e in SubExpressions) { + yield return e; + } + + foreach (var s in SubStatements) { + foreach (var e in s.SubExpressionsIncludingTransitiveSubStatements) { + yield return e; + } + } + } + } + /// /// Returns the non-null substatements of the Statements. /// @@ -8759,7 +8777,7 @@ public TryRecoverStatement(Statement tryBody, IVariable haltMessageVar, Statemen // ------------------------------------------------------------------------------------------------------ public abstract class TokenWrapper : IToken { - protected readonly IToken WrappedToken; + public readonly IToken WrappedToken; protected TokenWrapper(IToken wrappedToken) { Contract.Requires(wrappedToken != null); WrappedToken = wrappedToken; diff --git a/Source/Dafny/AST/FreeVariablesUtil.cs b/Source/Dafny/AST/FreeVariablesUtil.cs index bc0a4ef9229..6f8fd670124 100644 --- a/Source/Dafny/AST/FreeVariablesUtil.cs +++ b/Source/Dafny/AST/FreeVariablesUtil.cs @@ -35,17 +35,20 @@ public static void ComputeFreeVariables(Expression expr, ISet fvs) { bool dontCare0 = false, dontCare1 = false; Type dontCareT = null; var dontCareHeapAt = new HashSet