Open MikaelMayer opened 11 months ago
Refactoring the postcondition to a lemma solves the problem
Update: The discrepancy from the CLI and VSCode comes from a flag that I'm passing to the language server that I had forgotten about: --disable-nonlinear-arithmetic
.
So just by removing this flag, the code above passes in VSCode.
Now I can also reproduce the issue on the command line:
& "C:\Program Files\dotnet\dotnet.EXE" "...\.vscode\extensions\dafny-lang.ide-vscode-3.2.1\out\resources\nightly-2023-11-12-906db28\github\dafny\Dafny.dll" "verify" "--disable-nonlinear-arithmetic" "...\dafny\Source\IntegrationTests\TestFiles\LitTests\LitTest\libraries\src\Parsers\parserDebug.dfy"
Advisory: CornerCase (correctness) SKIPPED because of internal error: unexpected prover output: System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Boogie.TypeErasure.OpTypeEraserArguments.OriginalOpTypes(VCExprNAry node)
at Microsoft.Boogie.TypeErasure.OpTypeEraserArguments.VisitBoogieFunctionOp(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprBoogieFunctionOp.Accept[Result,Arg](VCExprNAry expr, IVCExprOpVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprOpVisitor`2 visitor, Arg arg) at Microsoft.Boogie.TypeErasure.TypeEraser.Visit(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.MutatingVCExprVisitor`1.MutateList(List`1 exprs, Arg arg)
at Microsoft.Boogie.VCExprAST.MutatingVCExprVisitor`1.MutateTriggers(List`1 triggers, Arg arg)
at Microsoft.Boogie.TypeErasure.TypeEraserArguments.HandleQuantifier(VCExprQuantifier node, List`1 newBoundVars, VariableBindings bindings)
at Microsoft.Boogie.TypeErasure.TypeEraserArguments.Visit(VCExprQuantifier node, VariableBindings oldBindings)
at Microsoft.Boogie.VCExprAST.VCExprQuantifier.Accept[Result,Arg](IVCExprVisitor`2 visitor, Arg
arg)
at Microsoft.Boogie.VCExprAST.MutatingVCExprVisitor`1.Visit(VCExprNAry node, Arg arg)
at Microsoft.Boogie.TypeErasure.TypeEraser.Visit(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.MutatingVCExprVisitor`1.Mutate(VCExpr expr, Arg arg)
at Microsoft.Boogie.TypeErasure.OpTypeEraser.VisitImpliesOp(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprOp.Accept[Result,Arg](VCExprNAry expr, IVCExprOpVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprOpVisitor`2 visitor, Arg arg) at Microsoft.Boogie.TypeErasure.TypeEraser.Visit(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.MutatingVCExprVisitor`1.Mutate(VCExpr expr, Arg arg)
at Microsoft.Boogie.TypeErasure.OpTypeEraser.VisitImpliesOp(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprOp.Accept[Result,Arg](VCExprNAry expr, IVCExprOpVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprOpVisitor`2 visitor, Arg arg) at Microsoft.Boogie.TypeErasure.TypeEraser.Visit(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.MutatingVCExprVisitor`1.Mutate(VCExpr expr, Arg arg)
at Microsoft.Boogie.TypeErasure.TypeEraser.Visit(VCExprLet node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprLet.Accept[Result,Arg](IVCExprVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.MutatingVCExprVisitor`1.Mutate(VCExpr expr, Arg arg)
at Microsoft.Boogie.TypeErasure.OpTypeEraser.VisitImpliesOp(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprOp.Accept[Result,Arg](VCExprNAry expr, IVCExprOpVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprOpVisitor`2 visitor, Arg arg) at Microsoft.Boogie.TypeErasure.TypeEraser.Visit(VCExprNAry node, VariableBindings bindings)
at Microsoft.Boogie.VCExprAST.VCExprNAry.Accept[Result,Arg](IVCExprVisitor`2 visitor, Arg arg)
at Microsoft.Boogie.VCExprAST.MutatingVCExprVisitor`1.Mutate(VCExpr expr, Arg arg)
at Microsoft.Boogie.TypeErasure.TypeEraser.Erase(VCExpr expr, Int32 polarity)
at Microsoft.Boogie.SMTLib.SMTLibProcessTheoremProver.VCExpr2String(VCExpr expr, Int32 polarity) at Microsoft.Boogie.SMTLib.SMTLibInteractiveTheoremProver.Check(String descriptiveName, VCExpr vc, ErrorHandler handler, Int32 errorLimit, CancellationToken cancellationToken)
at Microsoft.Boogie.Checker.Check(String descriptiveName, VCExpr vc, CancellationToken cancellationToken)
Unhandled exception: System.ArgumentNullException: Value cannot be null. (Parameter 'source')
at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
at System.Linq.Enumerable.Select[TSource,TResult](IEnumerable`1 source, Func`2 selector)
at Microsoft.Dafny.DafnyConsolePrinter.DistillVerificationResult(VerificationResult verificationResult)
at Microsoft.Dafny.DafnyConsolePrinter.ReportEndVerifyImplementation(Implementation implementation, VerificationResult result)
at Microsoft.Boogie.ExecutionEngine.<>c__DisplayClass48_0.<VerifyImplementation>b__0(IVerificationStatus status)
at System.Reactive.Linq.ObservableImpl.Do`1.OnNext._.OnNext(TSource value) in /_/Rx.NET/Source/src/System.Reactive/Linq/Observable/Do.cs:line 38
--- End of stack trace from previous location ---
at Microsoft.Boogie.ExecutionEngine.<>c__DisplayClass41_0.<<VerifyEachImplementation>b__1>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Boogie.ExecutionEngine.<>c__DisplayClass41_0.<<VerifyEachImplementation>b__1>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Boogie.ExecutionEngine.VerifyEachImplementation(TextWriter output, ProcessedProgram processedProgram, PipelineStatistics stats, String programId, ErrorReporterDelegate er, String requestId, Implementation[] stablePrioritizedImpls)
at Microsoft.Boogie.ExecutionEngine.InferAndVerify(TextWriter output, Program program, PipelineStatistics stats, String programId, ErrorReporterDelegate er, String requestId)
at Microsoft.Dafny.DafnyMain.BoogiePipelineWithRerun(DafnyOptions options, TextWriter output, ExecutionEngine engine, Program program, String bplFileName, String programId)
at Microsoft.Dafny.DafnyMain.BoogieOnce(DafnyOptions options, TextWriter output, ExecutionEngine engine, String baseFile, String moduleName, Program boogieProgram, String programId)
at Microsoft.Dafny.CompilerDriver.BoogieOnceWithTimerAsync(TextWriter output, DafnyOptions options, String baseName, String programId, String moduleName, Program program)
at Microsoft.Dafny.CompilerDriver.<>c__DisplayClass8_0.<<BoogieAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Dafny.CompilerDriver.<>c__DisplayClass8_0.<<BoogieAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Dafny.CompilerDriver.BoogieAsync(DafnyOptions options, String baseName, IEnumerable`1 boogiePrograms, String programId)
at Microsoft.Dafny.CompilerDriver.ProcessFilesAsync(IReadOnlyList`1 dafnyFiles, ReadOnlyCollection`1 otherFileNames, DafnyOptions options, ProofDependencyManager depManager, Boolean lookForSnapshots, String programId)
at Microsoft.Dafny.CompilerDriver.RunCompiler(DafnyOptions options)
at Microsoft.Dafny.DafnyCli.<>c__DisplayClass10_0.<<SetHandlerUsingDafnyOptionsContinuation>g__Handle|0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Invocation.AnonymousCommandHandler.InvokeAsync(InvocationContext context)
at System.CommandLine.Invocation.InvocationPipeline.<>c__DisplayClass4_0.<<BuildInvocationChain>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass17_0.<<UseParseErrorReporting>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass12_0.<<UseHelp>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Dafny.DafnyCli.<>c__DisplayClass21_0.<<AddDeveloperHelp>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass22_0.<<UseVersionOption>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass19_0.<<UseTypoCorrections>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<UseSuggestDirective>b__18_0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass16_0.<<UseParseDirective>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c.<<RegisterWithDotnetSuggest>b__5_0>d.MoveNext()
--- End of stack trace from previous location ---
at System.CommandLine.Builder.CommandLineBuilderExtensions.<>c__DisplayClass8_0.<<UseExceptionHandler>b__0>d.MoveNext()
First investigations show that it's cause by Boogie interpreting "1 <= r" as a trigger because "<=" is now uninterpreted. And since the variable r is local, it can't find a definition while trying to translate triggers to Z3
The first fault in the code is in Boogie apparently:
public override Expr VisitNAryExpr(NAryExpr node)
{
//Contract.Requires(node != null);
Contract.Ensures(Contract.Result<Expr>() != null);
FunctionCall fn = node.Fun as FunctionCall;
if (fn != null && cce.NonNull(fn.Func).NeverTrigger)
{
parent.Triggers = new Trigger(fn.Func.tok, false, new List<Expr> {node}, parent.Triggers);
}
return base.VisitNAryExpr(node);
}
The line:
parent.Triggers = new Trigger(fn.Func.tok, false, new List<Expr> {node}, parent.Triggers);
get executed with parent == forall $ih#ntimes0#0 :: ....
but the node itself is 1 <= r#h
which is a local variable and apparently a "NeverTrigger" that still gets collected.
Note that even without --disable-nonlinear-arithmetic in vSCode, I still have a similar problem in my original code.
Nice sleuthing!
The crash you're seeing is a bug in Boogie. Here is a smaller repro:
function {:never_pattern true} P(x: int): bool;
procedure Test() {
assert (forall xyz: int ::
(var r := xyz; P(r)));
}
which for me produces:
d.bpl(50,21): Error: invalid type for argument 0 in application of P: bool (expected: int)
d.bpl(50,21): Error: internal error, shallow-type assignment was done incorrectly, r:bool != int
Process terminated. Assertion failed.
at Microsoft.Boogie.IdentifierExpr.Typecheck(TypecheckingContext tc) in BOOGIE/Source/Core/AST/AbsyExpr.cs:line 1343
at Microsoft.Boogie.NAryExpr.Typecheck(TypecheckingContext tc) in BOOGIE/Source/Core/AST/AbsyExpr.cs:line 3328
at Microsoft.Boogie.LetExpr.Typecheck(TypecheckingContext tc) in BOOGIE/Source/Core/AST/AbsyQuant.cs:line 1313
at Microsoft.Boogie.QuantifierExpr.Typecheck(TypecheckingContext tc) in BOOGIE/Source/Core/AST/AbsyQuant.cs:line 1077
at Microsoft.Boogie.AssertCmd.Typecheck(TypecheckingContext tc) in BOOGIE/Source/Core/AST/AbsyCmd.cs:line 4368
at Microsoft.Boogie.Block.Typecheck(TypecheckingContext tc) in BOOGIE/Source/Core/AST/AbsyCmd.cs:line 1381
at Microsoft.Boogie.Implementation.Typecheck(TypecheckingContext tc) in BOOGIE/Source/Core/AST/Absy.cs:line 4068
at Microsoft.Boogie.Program.Typecheck(TypecheckingContext tc) in BOOGIE/Source/Core/AST/Program.cs:line 152
at Microsoft.Boogie.Program.Typecheck(CoreOptions options, IErrorSink errorSink) in BOOGIE/Source/Core/AST/Program.cs:line 140
at Microsoft.Boogie.Program.Typecheck(CoreOptions options) in BOOGIE/Source/Core/AST/Program.cs:line 134
at Microsoft.Boogie.ExecutionEngine.ResolveAndTypecheck(Program program, String bplFileName, CivlTypeChecker& civlTypeChecker) in BOOGIE/Source/ExecutionEngine/ExecutionEngine.cs:line 402
at Microsoft.Boogie.ExecutionEngine.ProcessProgram(TextWriter output, Program program, String bplFileName, String programId) in BOOGIE/Source/ExecutionEngine/ExecutionEngine.cs:line 143
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at Microsoft.Boogie.ExecutionEngine.ProcessProgram(TextWriter output, Program program, String bplFileName, String programId)
at Microsoft.Boogie.ExecutionEngine.ProcessFiles(TextWriter output, IList`1 fileNames, Boolean lookForSnapshots, String programId) in BOOGIE/Source/ExecutionEngine/ExecutionEngine.cs:line 123
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at Microsoft.Boogie.ExecutionEngine.ProcessFiles(TextWriter output, IList`1 fileNames, Boolean lookForSnapshots, String programId)
at Microsoft.Boogie.OnlyBoogie.Main(String[] args) in BOOGIE/Source/BoogieDriver/BoogieDriver.cs:line 66
Here's an explanation:
The :never_pattern
attribute in Boogie on a function P
causes Boogie to attach the SMT
:no-pattern (P e)
to any quantifier that (doesn't already have an explicit trigger in Boogie and) contains an expression P(e)
in its body. I suppose it does that by visiting every AST node in the quantifier's body, and if it encounters a function call to P
, then it remembers the function arguments (e
in my example) and then blurts out the SMT :no-pattern (P e)
.
Evidently, this visitation does not handle let expressions correctly. When the visitor encounters the expression P(r)
in the example, it outputs :no-pattern (P r)
, even though r
is not bound to anything at the outer scope of the quantifier body. This certainly generates a meaningless :no-pattern
annotation. But before that, it also generates two type errors (see exception trace above). I imagine (but did not inspect the Boogie source code) that these type errors come about because the visitor adds Boogie :nopats
attributes, which are later type checked and translated into SMT :no-pattern
annotations. The type checking of those :nopats
attributes is probably what generates the errors and subsequently causes a crash.
While the crash you're seeing is a Boogie bug, the example also reveals two other issues in Dafny:
Dafny's automatic-induction machinery say that the induction hypothesis holds for smaller arguments. This can look something like
0 <= ih$tmp#0 && ih$tmp#0 < N
If the --disable-nonlinear-arithmetic
option is used, then it seems more appropriate to formula this condition as
INTERNAL_le_boogie(0, ih$tmp#0) && INTERNAL_lt_boogie(ih$tmp#0, N)
A serious problem (that has been known for some time, but never fixed) is that Dafny's automatic induction generates quantifiers and does not give them triggers. This might cause all sorts of weird brittleness problems. Dafny should figure out triggers for these quantifiers, and if the generated induction hypothesis does not have a good trigger, then Dafny should omit the induction hypothesis. That is, Dafny should generate an induction hypothesis only if it also generates a good trigger for it. (For the IDE, I would suggest that a successfully generated induction hypothesis ought to give some indication in a hover text, and that the lack of a generated induction hypothesis should give rise to no hover text.)
Dafny version
latest-nightly
Code to produce this issue
Command to run and resulting output
What happened?
What type of operating system are you experiencing the problem on?
Windows