Skip to content
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

Attempting to return a resource in PVL triggers NoSuchElementException #1267

Open
wandernauta opened this issue Oct 16, 2024 · 2 comments
Open
Labels
A-Bug Fuzzing Found by fuzzing M-refactoring Used to track issues that would be fixed by a refactoring.

Comments

@wandernauta
Copy link
Contributor

In the wiki, the type resource is described as a "Boolean-like type" in the specification language, noting that...

In PVL, the specification types above can also be used in the regular program.

And indeed the following PVL program verifies:

void foo() {
    resource a = true;
    assert a;
}

The following program also verifies (note that bar is not pure):

requires x;
void bar(resource x) {
}

void foo() {
    bar(true);
}

However, the following program causes VerCors to crash with a NoSuchElementException:

resource bar() {
    return true;
}

void foo() {
    assert bar();
}
java.util.NoSuchElementException: head of empty Stack
	at scala.collection.IndexedSeqOps.head(IndexedSeq.scala:99)
	at scala.collection.IndexedSeqOps.head$(IndexedSeq.scala:94)
	at scala.collection.mutable.ArrayDeque.head(ArrayDeque.scala:39)
	at scala.collection.mutable.Stack.top(Stack.scala:115)
	at hre.util.ScopedStack.top(ScopedStack.scala:25)
	at vct.rewrite.EncodeResourceValues.dispatch(EncodeResourceValues.scala:458)
	at vct.col.ast.ops.rewrite.ProcedureRewrite.$anonfun$rewrite$1(ProcedureRewrite.scala:7)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.scope(Scopes.scala:84)
	at vct.col.ast.ops.rewrite.ProcedureRewrite.rewrite(ProcedureRewrite.scala:5)
	at vct.col.ast.ops.rewrite.ProcedureRewrite.rewrite$(ProcedureRewrite.scala:4)
	at vct.col.ast.Procedure.rewrite(Node.scala:702)
	at vct.col.ast.ops.rewrite.ProcedureRewrite.rewriteDefault(ProcedureRewrite.scala:3)
	at vct.col.ast.ops.rewrite.ProcedureRewrite.rewriteDefault$(ProcedureRewrite.scala:3)
	at vct.col.ast.Procedure.rewriteDefault(Node.scala:702)
	at vct.col.ast.Procedure.rewriteDefault(Node.scala:702)
	at vct.col.rewrite.NonLatchingRewriter.dispatch(NonLatchingRewriter.scala:16)
	at vct.col.util.Scopes.$anonfun$dispatch$3(Scopes.scala:137)
	at vct.col.util.Scopes.$anonfun$dispatch$3$adapted(Scopes.scala:136)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at vct.col.util.Scopes.$anonfun$dispatch$2(Scopes.scala:136)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.collect(Scopes.scala:92)
	at vct.col.util.Scopes.dispatch(Scopes.scala:136)
	at vct.col.ast.ops.rewrite.ProgramRewrite.$anonfun$rewrite$7(ProgramRewrite.scala:13)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.scope(Scopes.scala:84)
	at vct.col.ast.ops.rewrite.ProgramRewrite.$anonfun$rewrite$6(ProgramRewrite.scala:11)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.scope(Scopes.scala:84)
	at vct.col.ast.ops.rewrite.ProgramRewrite.$anonfun$rewrite$5(ProgramRewrite.scala:10)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.scope(Scopes.scala:84)
	at vct.col.ast.ops.rewrite.ProgramRewrite.$anonfun$rewrite$4(ProgramRewrite.scala:9)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.scope(Scopes.scala:84)
	at vct.col.ast.ops.rewrite.ProgramRewrite.$anonfun$rewrite$3(ProgramRewrite.scala:8)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.scope(Scopes.scala:84)
	at vct.col.ast.ops.rewrite.ProgramRewrite.$anonfun$rewrite$2(ProgramRewrite.scala:7)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.scope(Scopes.scala:84)
	at vct.col.ast.ops.rewrite.ProgramRewrite.$anonfun$rewrite$1(ProgramRewrite.scala:6)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.col.util.Scopes.scope(Scopes.scala:84)
	at vct.col.ast.ops.rewrite.ProgramRewrite.rewrite(ProgramRewrite.scala:5)
	at vct.col.ast.ops.rewrite.ProgramRewrite.rewrite$(ProgramRewrite.scala:4)
	at vct.col.ast.Program.rewrite(Node.scala:111)
	at vct.col.ast.ops.rewrite.ProgramRewrite.rewriteDefault(ProgramRewrite.scala:3)
	at vct.col.ast.ops.rewrite.ProgramRewrite.rewriteDefault$(ProgramRewrite.scala:3)
	at vct.col.ast.Program.rewriteDefault(Node.scala:111)
	at vct.col.ast.AbstractRewriter.rewriteDefault(AbstractRewriter.scala:109)
	at vct.col.ast.AbstractRewriter.rewriteDefault$(AbstractRewriter.scala:109)
	at vct.col.rewrite.NonLatchingRewriter.rewriteDefault(NonLatchingRewriter.scala:8)
	at vct.rewrite.EncodeResourceValues.$anonfun$dispatch$1(EncodeResourceValues.scala:150)
	at hre.util.ScopedStack.having(ScopedStack.scala:35)
	at vct.rewrite.EncodeResourceValues.dispatch(EncodeResourceValues.scala:150)
	at vct.col.ast.ops.rewrite.VerificationContextRewrite.rewrite(VerificationContextRewrite.scala:6)
	at vct.col.ast.ops.rewrite.VerificationContextRewrite.rewrite$(VerificationContextRewrite.scala:4)
	at vct.col.ast.VerificationContext.rewrite(Node.scala:100)
	at vct.col.ast.ops.rewrite.VerificationContextRewrite.rewriteDefault(VerificationContextRewrite.scala:3)
	at vct.col.ast.ops.rewrite.VerificationContextRewrite.rewriteDefault$(VerificationContextRewrite.scala:3)
	at vct.col.ast.VerificationContext.rewriteDefault(Node.scala:100)
	at vct.col.ast.rewrite.BaseNonLatchingRewriter.dispatch(BaseNonLatchingRewriter.scala:4)
	at vct.col.ast.rewrite.BaseNonLatchingRewriter.dispatch$(BaseNonLatchingRewriter.scala:4)
	at vct.col.rewrite.NonLatchingRewriter.dispatch(NonLatchingRewriter.scala:8)
	at vct.col.ast.ops.rewrite.VerificationRewrite.$anonfun$rewrite$1(VerificationRewrite.scala:6)
	at scala.collection.immutable.List.map(List.scala:246)
	at scala.collection.immutable.List.map(List.scala:79)
	at vct.col.ast.ops.rewrite.VerificationRewrite.rewrite(VerificationRewrite.scala:6)
	at vct.col.ast.ops.rewrite.VerificationRewrite.rewrite$(VerificationRewrite.scala:4)
	at vct.col.ast.Verification.rewrite(Node.scala:94)
	at vct.col.ast.ops.rewrite.VerificationRewrite.rewriteDefault(VerificationRewrite.scala:3)
	at vct.col.ast.ops.rewrite.VerificationRewrite.rewriteDefault$(VerificationRewrite.scala:3)
	at vct.col.ast.Verification.rewriteDefault(Node.scala:94)
	at vct.col.ast.rewrite.BaseNonLatchingRewriter.dispatch(BaseNonLatchingRewriter.scala:3)
	at vct.col.ast.rewrite.BaseNonLatchingRewriter.dispatch$(BaseNonLatchingRewriter.scala:3)
	at vct.col.rewrite.NonLatchingRewriter.dispatch(NonLatchingRewriter.scala:8)
	at vct.main.stages.Transformation.liftedTree1$1(Transformation.scala:239)
	at vct.main.stages.Transformation.$anonfun$run$4(Transformation.scala:239)
	at vct.main.stages.Transformation.$anonfun$run$4$adapted(Transformation.scala:233)
	at hre.progress.Progress$.$anonfun$foreach$2(Progress.scala:25)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
	at hre.progress.task.AbstractTask.frame(AbstractTask.scala:166)
	at hre.progress.Progress$.$anonfun$foreach$1(Progress.scala:25)
	at hre.progress.Progress$.$anonfun$foreach$1$adapted(Progress.scala:24)
	at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)
	at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)
	at scala.collection.AbstractIterator.foreach(Iterator.scala:1300)
	at hre.progress.Progress$.foreach(Progress.scala:24)
	at vct.main.stages.Transformation.$anonfun$run$2(Transformation.scala:233)
	at hre.debug.TimeTravel$.safelyRepeatable(TimeTravel.scala:32)
	at vct.main.stages.Transformation.run(Transformation.scala:227)
	at vct.main.stages.Transformation.run(Transformation.scala:200)
	at hre.stages.Stages.$anonfun$run$3(Stages.scala:104)
	at hre.stages.Stages.$anonfun$run$3$adapted(Stages.scala:101)
	at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:576)
	at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:574)
	at scala.collection.AbstractIterable.foreach(Iterable.scala:933)
	at scala.collection.IterableOps$WithFilter.foreach(Iterable.scala:903)
	at hre.stages.Stages.$anonfun$run$1(Stages.scala:101)
	at hre.progress.task.NameSequenceTask.scope(NameSequenceTask.scala:16)
	at hre.progress.Progress$.stages(Progress.scala:47)
	at hre.stages.Stages.run(Stages.scala:98)
	at hre.stages.Stages.run$(Stages.scala:95)
	at hre.stages.StagesPair.run(Stages.scala:145)
	at vct.main.modes.Verify$.verifyWithOptions(Verify.scala:64)
	at vct.main.modes.Verify$.$anonfun$runOptions$3(Verify.scala:99)
	at scala.runtime.java8.JFunction0$mcI$sp.apply(JFunction0$mcI$sp.scala:17)
	at hre.util.Time$.logTime(Time.scala:23)
	at vct.main.modes.Verify$.runOptions(Verify.scala:99)
	at vct.main.Main$.runMode(Main.scala:107)
	at vct.main.Main$.$anonfun$runOptions$3(Main.scala:100)
	at scala.runtime.java8.JFunction0$mcI$sp.apply(JFunction0$mcI$sp.scala:17)
	at hre.middleware.Middleware$.using(Middleware.scala:78)
	at vct.main.Main$.$anonfun$runOptions$2(Main.scala:100)
	at scala.runtime.java8.JFunction0$mcI$sp.apply(JFunction0$mcI$sp.scala:17)
	at hre.io.Watch$.booleanWithWatch(Watch.scala:58)
	at vct.main.Main$.$anonfun$runOptions$1(Main.scala:100)
	at scala.runtime.java8.JFunction0$mcI$sp.apply(JFunction0$mcI$sp.scala:17)
	at hre.middleware.Middleware$.using(Middleware.scala:78)
	at vct.main.Main$.runOptions(Main.scala:95)
	at vct.main.Main$.main(Main.scala:50)
	at vct.main.Main.main(Main.scala)
[ERROR] !*!*!*!*!*!*!*!*!*!*!*!
[ERROR] ! VerCors has crashed !
[ERROR] !*!*!*!*!*!*!*!*!*!*!*!

If bar is marked pure, the error goes away.

Version: 3313255 (dev branch).

This issue was found by fuzzing.

@superaxander superaxander added A-Bug Fuzzing Found by fuzzing labels Oct 17, 2024
@bobismijnnaam bobismijnnaam self-assigned this Oct 17, 2024
@bobismijnnaam
Copy link
Contributor

@wandernauta For context, VerCors supports some degree of first class permissions:

class C {
	int x;
}

void m(C c) {
  resource a = Perm(c.x, 1);
  exhale a;
}

Currently it's not the case because of some other bug but that's an easy fix.

I'll have a quick look to see if it's worth fixing this because the support is quite experimental, so I might just put a try/catch wrapper around it for a friendly message.

@bobismijnnaam
Copy link
Contributor

To fix this, we need to make sure Return is actually typechecked. This can be done by adding an intermediate AmbiguousReturn(Expr), which is transformed into a Return(Expr, Ref[AbstractMethod]) after the resolution phase. Typechecking then needs to be added here:

Return(result) // TODO coerce return, make AmbiguousReturn?
)

This is a bit of a bigger project because, while the AST changes are logical and straightforward, it's not really clear how many passes require extra bookkeeping to construct a proper back pointer to the successor of the current method being rewritten. For now I'm just adding a proper error that resource values as method return types are not supported with a link to this issue.

bobismijnnaam added a commit that referenced this issue Oct 17, 2024
@bobismijnnaam bobismijnnaam removed their assignment Oct 17, 2024
@bobismijnnaam bobismijnnaam added the M-refactoring Used to track issues that would be fixed by a refactoring. label Oct 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Bug Fuzzing Found by fuzzing M-refactoring Used to track issues that would be fixed by a refactoring.
Projects
None yet
Development

No branches or pull requests

3 participants