Skip to content

Commit

Permalink
Bogus safety violation checking if a set is a subset of Nat. (#2960)
Browse files Browse the repository at this point in the history
* Bogus safety violation checking if a set is a subset of Nat.

Fixes Github issue #2948
#2948

* Bogus safety violation checking if a set is a subset of Nat.

Fixes Github issue #2948
#2948

* Bogus safety violation checking if a set is a subset of Nat.

All encoding are affected.

Fixes Github issue #2948
#2948

* Bogus safety violation checking if a set is a subset of Nat.

Part of Github issue $2948
#2948

* Add issue number in changelog

---------

Co-authored-by: Igor Konnov <[email protected]>
Co-authored-by: Thomas Pani <[email protected]>
  • Loading branch information
3 people authored Sep 26, 2024
1 parent 0144525 commit 169d142
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 2 deletions.
1 change: 1 addition & 0 deletions .unreleased/bug-fixes/BogusSafetyViolationSubsetNat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Show note that expression is unsupported instead of reporting a counterexample claiming that e.g. `{42} \in SUBSET Nat` is false, see #2690
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,11 @@ class LazyEquality(rewriter: SymbStateRewriter)
val leftElems = state.arena.getHas(left)
val rightElems = state.arena.getHas(right)
if (leftElems.isEmpty) {
assert(left.cellType != InfSetT(CellTFrom(IntT1)))
// SE-SUBSETEQ1
state.setRex(state.arena.cellTrue().toBuilder)
} else if (rightElems.isEmpty) {
assert(right.cellType != InfSetT(CellTFrom(IntT1)))
// SE-SUBSETEQ2
def notIn(le: ArenaCell) = {
tla.not(tla.selectInSet(le.toBuilder, left.toBuilder))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SetInRule(rewriter: SymbStateRewriter) extends RewritingRule {
}

// TODO: x \in S should be equivalent to SkolemExists(\E t \in S: x = t)
// This is only true for the positive occurences of x \in S!
// This is only true for the positive occurrences of x \in S!
override def apply(state: SymbState): SymbState = {
state.ex match {
// a common pattern x \in {y} that is equivalent to x = y, e.g., the assignment solver creates it
Expand Down Expand Up @@ -72,6 +72,20 @@ class SetInRule(rewriter: SymbStateRewriter) extends RewritingRule {
}

protected def powSetIn(state: SymbState, powsetCell: ArenaCell, elemCell: ArenaCell): SymbState = {
// Check that the powsetCell.cellType is not an infinite set.
state.arena.getDom(powsetCell).cellType match {
case InfSetT(ct) =>
throw new RewriterException("SetInRule.powSetIn is not implemented for infinite type %s (found in %s)"
.format(InfSetT(ct), state.ex), state.ex)
case _ =>
}
elemCell.cellType match {
case InfSetT(_) =>
throw new RewriterException("SetInRule.powSetIn is not implemented for infinite type %s (found in %s)"
.format(elemCell.cellType, state.ex), state.ex)
case _ =>
}

def checkType: PartialFunction[(CellT, CellT), Unit] = {
case (PowSetT(SetT1(expectedType)), CellTFrom(SetT1(actualType))) =>
if (expectedType != actualType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ class SetInRuleWithArrays(rewriter: SymbStateRewriter) extends SetInRule(rewrite
private val simplifier = new ConstSimplifierForSmt

override protected def powSetIn(state: SymbState, powsetCell: ArenaCell, elemCell: ArenaCell): SymbState = {
// Check that the powsetCell.cellType is not an infinite set.
state.arena.getDom(powsetCell).cellType match {
case InfSetT(ct) =>
throw new RewriterException("SetInRule.powSetIn is not implemented for infinite type %s (found in %s)"
.format(InfSetT(ct), state.ex), state.ex)
case _ =>
}
elemCell.cellType match {
case InfSetT(_) =>
throw new RewriterException("SetInRule.powSetIn is not implemented for infinite type %s (found in %s)"
.format(elemCell.cellType, state.ex), state.ex)
case _ =>
}

def checkType: PartialFunction[(CellT, CellT), Unit] = {
case (PowSetT(SetT1(expectedType)), CellTFrom(SetT1(actualType))) =>
assert(expectedType == actualType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import at.forsyte.apalache.tla.bmcmt.rules.aux.PowSetCtor
import at.forsyte.apalache.tla.bmcmt.types.{CellTFrom, PowSetT}
import at.forsyte.apalache.tla.lir.TypedPredefs._
import at.forsyte.apalache.tla.lir.convenience.tla._
import at.forsyte.apalache.tla.lir.{BoolT1, IntT1, NameEx, SetT1}
import at.forsyte.apalache.tla.lir.values.{TlaIntSet, TlaNatSet}
import at.forsyte.apalache.tla.lir.{BoolT1, IntT1, NameEx, SetT1, Typed, ValEx}

trait TestSymbStateRewriterPowerset extends RewriterBase {
private val types = Map(
Expand Down Expand Up @@ -76,6 +77,91 @@ trait TestSymbStateRewriterPowerset extends RewriterBase {
assertTlaExAndRestore(create(rewriterType), state)
}

test("""SSE-SUBSETEQ: {1, 2, 3, 4} \in SUBSET Nat""") { rewriterType: SMTEncoding =>
def setTo(k: Int) = enumSet((1 to k).map(int): _*)

val set1to4 = setTo(4) ? "I"
val powset = powSet(ValEx(TlaNatSet)(Typed(SetT1(IntT1)))) ? "II"
val inEx = in(set1to4, powset)
.typed(types, "b")
val state = new SymbState(inEx, arena, Binding())

val rewriter = create(rewriterType)
try {
val _ = rewriter.rewriteUntilDone(state)
fail("expected an error message about subseteq over infinite sets being unsupported")
} catch {
case _: RewriterException => () // OK
}
}

test("""SSE-SUBSETEQ: Nat \in SUBSET {1, 2, 3, 4}""") { rewriterType: SMTEncoding =>
def setTo(k: Int) = enumSet((1 to k).map(int): _*)

val set1to4 = setTo(4) ? "I"
val powset = powSet(set1to4) ? "II"
val nat = ValEx(TlaNatSet)(Typed(SetT1(IntT1))) ? "I"
val inEx = in(nat, powset)
.typed(types, "b")
val state = new SymbState(inEx, arena, Binding())

val rewriter = create(rewriterType)
try {
val _ = rewriter.rewriteUntilDone(state)
fail("expected an error message about subseteq over infinite sets being unsupported")
} catch {
case _: RewriterException => () // OK
}
}

test("""SSE-SUBSETEQ: Nat \in SUBSET Nat""") { rewriterType: SMTEncoding =>
val nat = ValEx(TlaNatSet)(Typed(SetT1(IntT1))) ? "I"
val powset = powSet(ValEx(TlaNatSet)(Typed(SetT1(IntT1)))) ? "II"
val inEx = in(nat, powset)
.typed(types, "b")
val state = new SymbState(inEx, arena, Binding())

val rewriter = create(rewriterType)
try {
val _ = rewriter.rewriteUntilDone(state)
fail("expected an error message about subseteq over infinite sets being unsupported")
} catch {
case _: RewriterException => () // OK
}
}

test("""SSE-SUBSETEQ: Nat \in SUBSET Int""") { rewriterType: SMTEncoding =>
val nat = ValEx(TlaNatSet)(Typed(SetT1(IntT1))) ? "I"
val powset = powSet(ValEx(TlaIntSet)(Typed(SetT1(IntT1)))) ? "II"
val inEx = in(nat, powset)
.typed(types, "b")
val state = new SymbState(inEx, arena, Binding())

val rewriter = create(rewriterType)
try {
val _ = rewriter.rewriteUntilDone(state)
fail("expected an error message about subseteq over infinite sets being unsupported")
} catch {
case _: RewriterException => () // OK
}
}

test("""SSE-SUBSETEQ: Int \in SUBSET Nat""") { rewriterType: SMTEncoding =>
val nat = ValEx(TlaIntSet)(Typed(SetT1(IntT1))) ? "I"
val powset = powSet(ValEx(TlaNatSet)(Typed(SetT1(IntT1)))) ? "II"
val inEx = in(nat, powset)
.typed(types, "b")
val state = new SymbState(inEx, arena, Binding())

val rewriter = create(rewriterType)
try {
val _ = rewriter.rewriteUntilDone(state)
fail("expected an error message about subseteq over infinite sets being unsupported")
} catch {
case _: RewriterException => () // OK
}
}

test("""SE-SUBSET: \E X \in SUBSET {1, 2}: TRUE (sat)""") { rewriterType: SMTEncoding =>
// a regression test that failed in the previous versions
val set = enumSet(int(1), int(2)) ? "I"
Expand Down

0 comments on commit 169d142

Please sign in to comment.