From 2e6a08c2882d10caa5f6bbfef09c5b1bdd31752f Mon Sep 17 00:00:00 2001 From: Alex Chew Date: Fri, 19 Apr 2024 17:29:20 -0700 Subject: [PATCH] Add first pass of proof obligation description expressions (#5317) ### Description Adds a first pass of proof obligation description expressions, towards resolving https://github.com/dafny-lang/dafny/issues/2987. It also adds a new hidden option, `--show-proof-obligation-expressions`, which adds to each (supported) failed proof obligation error message an equivalent Dafny assertion. This option is used to automate tests for the newly added proof obligation description expressions, but this PR does not backfill tests for existing PODesc expressions. By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license. --------- Co-authored-by: Aaron Tomb --- Source/DafnyCore/DafnyOptions.cs | 6 + .../DafnyCore/Generic/ReporterExtensions.cs | 3 +- Source/DafnyCore/Options/CommonOptionBag.cs | 13 +- Source/DafnyCore/Options/DafnyCommands.cs | 3 +- Source/DafnyCore/Pipeline/Compilation.cs | 38 +++ .../BoogieGenerator.ExpressionWellformed.cs | 56 ++-- .../Verifier/BoogieGenerator.Functions.cs | 2 +- .../Verifier/BoogieGenerator.Methods.cs | 7 +- .../Verifier/BoogieGenerator.TrStatement.cs | 41 ++- .../Verifier/BoogieGenerator.Types.cs | 68 +++-- Source/DafnyCore/Verifier/BoogieGenerator.cs | 2 +- .../Verifier/ProofObligationDescription.cs | 271 ++++++++++++++++-- .../array-init-empty.dfy | 6 + .../array-init-empty.dfy.expect | 4 + .../array-init-size-valid.dfy | 6 + .../array-init-size-valid.dfy.expect | 4 + .../char-overflow-non-unicode.dfy | 6 + .../char-overflow-non-unicode.dfy.expect | 5 + .../char-overflow-unicode.dfy | 6 + .../char-overflow-unicode.dfy.expect | 4 + .../char-underflow-non-unicode.dfy | 6 + .../char-underflow-non-unicode.dfy.expect | 5 + .../char-underflow-unicode.dfy | 6 + .../char-underflow-unicode.dfy.expect | 4 + .../proof-obligation-desc/conversion-fit.dfy | 7 + .../conversion-fit.dfy.expect | 4 + .../conversion-is-natural.dfy | 7 + .../conversion-is-natural.dfy.expect | 4 + .../conversion-positive.dfy | 7 + .../conversion-positive.dfy.expect | 4 + .../conversion-satisfies-constraints.dfy | 9 + ...onversion-satisfies-constraints.dfy.expect | 4 + .../for-range-assignable.dfy | 7 + .../for-range-assignable.dfy.expect | 4 + .../for-range-bounds-valid.dfy | 7 + .../for-range-bounds-valid.dfy.expect | 4 + .../forall-postcondition.dfy | 7 + .../forall-postcondition.dfy.expect | 5 + .../proof-obligation-desc/is-allocated.dfy | 12 + .../is-allocated.dfy.expect | 4 + .../proof-obligation-desc/is-integer.dfy | 7 + .../is-integer.dfy.expect | 4 + .../proof-obligation-desc/non-negative.dfy | 7 + .../non-negative.dfy.expect | 4 + .../proof-obligation-desc/non-null.dfy | 7 + .../proof-obligation-desc/non-null.dfy.expect | 4 + .../ordinal-subtraction-is-natural.dfy | 8 + .../ordinal-subtraction-is-natural.dfy.expect | 4 + .../ordinal-subtraction-underflow.dfy | 8 + .../ordinal-subtraction-underflow.dfy.expect | 4 + .../shift-lower-bound.dfy | 6 + .../shift-lower-bound.dfy.expect | 4 + .../shift-upper-bound.dfy | 6 + .../shift-upper-bound.dfy.expect | 4 + .../proof-obligation-desc/witness-check.dfy | 4 + .../witness-check.dfy.expect | 4 + 56 files changed, 666 insertions(+), 87 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/array-init-empty.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/array-init-empty.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/array-init-size-valid.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/array-init-size-valid.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/char-overflow-non-unicode.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/char-overflow-non-unicode.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/char-overflow-unicode.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/char-overflow-unicode.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/char-underflow-non-unicode.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/char-underflow-non-unicode.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/char-underflow-unicode.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/char-underflow-unicode.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/conversion-fit.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/conversion-fit.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/conversion-is-natural.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/conversion-is-natural.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/conversion-positive.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/conversion-positive.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/conversion-satisfies-constraints.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/conversion-satisfies-constraints.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/for-range-assignable.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/for-range-assignable.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/for-range-bounds-valid.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/for-range-bounds-valid.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/forall-postcondition.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/forall-postcondition.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/is-allocated.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/is-allocated.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/is-integer.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/is-integer.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/non-negative.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/non-negative.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/non-null.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/non-null.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/ordinal-subtraction-is-natural.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/ordinal-subtraction-is-natural.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/ordinal-subtraction-underflow.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/ordinal-subtraction-underflow.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/shift-lower-bound.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/shift-lower-bound.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/shift-upper-bound.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/shift-upper-bound.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/witness-check.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/proof-obligation-desc/witness-check.dfy.expect diff --git a/Source/DafnyCore/DafnyOptions.cs b/Source/DafnyCore/DafnyOptions.cs index ad51aac125..f5855a8962 100644 --- a/Source/DafnyCore/DafnyOptions.cs +++ b/Source/DafnyCore/DafnyOptions.cs @@ -380,6 +380,8 @@ public enum IncludesModes { [CanBeNull] private TestGenerationOptions testGenOptions = null; public bool ExtractCounterexample = false; + public bool ShowProofObligationExpressions = false; + public bool AuditProgram = false; public static string DefaultZ3Version = "4.12.1"; @@ -796,6 +798,10 @@ protected bool ParseDafnySpecificOption(string name, Bpl.CommandLineParseState p EnhancedErrorMessages = 1; return true; + case "showProofObligationExpressions": + ShowProofObligationExpressions = true; + return true; + case "testContracts": if (ps.ConfirmArgumentCount(1)) { if (args[ps.i].Equals("Externs")) { diff --git a/Source/DafnyCore/Generic/ReporterExtensions.cs b/Source/DafnyCore/Generic/ReporterExtensions.cs index 2c7db25491..579cea5939 100644 --- a/Source/DafnyCore/Generic/ReporterExtensions.cs +++ b/Source/DafnyCore/Generic/ReporterExtensions.cs @@ -11,7 +11,7 @@ public static void ReportBoogieError(this ErrorReporter reporter, ErrorInformati var usingSnippets = reporter.Options.Get(Snippets.ShowSnippets); var relatedInformation = new List(); foreach (var auxiliaryInformation in error.Aux) { - if (auxiliaryInformation.Category == RelatedMessageCategory) { + if (auxiliaryInformation.Category == RelatedMessageCategory || auxiliaryInformation.Category == AssertedExprCategory) { error.Msg += "\n" + auxiliaryInformation.FullMsg; } else if (auxiliaryInformation.Category == RelatedLocationCategory) { relatedInformation.AddRange(CreateDiagnosticRelatedInformationFor(BoogieGenerator.ToDafnyToken(true, auxiliaryInformation.Tok), auxiliaryInformation.Msg, usingSnippets)); @@ -46,6 +46,7 @@ public static void ReportBoogieError(this ErrorReporter reporter, ErrorInformati private const string RelatedLocationCategory = "Related location"; public const string RelatedLocationMessage = RelatedLocationCategory; private const string RelatedMessageCategory = "Related message"; + public const string AssertedExprCategory = "Asserted expression"; public static readonly string PostConditionFailingMessage = new ProofObligationDescription.EnsuresDescription(null, null).FailureDescription; private static string FormatRelated(string related) { return $"Could not prove: {related}"; diff --git a/Source/DafnyCore/Options/CommonOptionBag.cs b/Source/DafnyCore/Options/CommonOptionBag.cs index f461a222ec..ef7563b8a0 100644 --- a/Source/DafnyCore/Options/CommonOptionBag.cs +++ b/Source/DafnyCore/Options/CommonOptionBag.cs @@ -359,6 +359,12 @@ Allow Dafny code to depend on the standard libraries included with the Dafny dis If verification fails, report a detailed counterexample for the first failing assertion (experimental).".TrimStart()) { }; + public static readonly Option ShowProofObligationExpressions = new("--show-proof-obligation-expressions", () => false, + @" +(Experimental) Show Dafny expressions corresponding to unverified proof obligations.".TrimStart()) { + IsHidden = true + }; + static CommonOptionBag() { DafnyOptions.RegisterLegacyBinding(WarnAsErrors, (options, value) => { if (!options.Get(AllowWarnings) && !options.Get(WarnAsErrors)) { @@ -571,6 +577,10 @@ void ParsePrintMode(Option option, Boogie.CommandLineParseState ps, options.EnhancedErrorMessages = 1; }); + DafnyOptions.RegisterLegacyBinding(ShowProofObligationExpressions, (options, value) => { + options.ShowProofObligationExpressions = value; + }); + DooFile.RegisterLibraryChecks( new Dictionary() { { UnicodeCharacters, DooFile.CheckOptionMatches }, @@ -635,7 +645,8 @@ void ParsePrintMode(Option option, Boogie.CommandLineParseState ps, AddCompileSuffix, SystemModule, ExecutionCoverageReport, - ExtractCounterexample + ExtractCounterexample, + ShowProofObligationExpressions ); } diff --git a/Source/DafnyCore/Options/DafnyCommands.cs b/Source/DafnyCore/Options/DafnyCommands.cs index a0e8f66cb5..d482ab458f 100644 --- a/Source/DafnyCore/Options/DafnyCommands.cs +++ b/Source/DafnyCore/Options/DafnyCommands.cs @@ -48,7 +48,8 @@ static DafnyCommands() { CommonOptionBag.NoTimeStampForCoverageReport, CommonOptionBag.VerificationCoverageReport, CommonOptionBag.ExtractCounterexample, - CommonOptionBag.ManualTriggerOption + CommonOptionBag.ManualTriggerOption, + CommonOptionBag.ShowProofObligationExpressions }.ToList(); public static IReadOnlyList