Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
14f0f64
Preserve unknown initial keys when copying input reference sets
CaelmBleidd Oct 13, 2026
01cba79
Clarify copied input map regression setup
CaelmBleidd Oct 13, 2026
9115c30
Integrate experimental Go frontend with native regression tests
CaelmBleidd Oct 9, 2026
8120f82
Organize Go tests around symbolic semantic expectations
CaelmBleidd Oct 9, 2026
fc7d8be
Fix nil map operations and missing lookup values
CaelmBleidd Oct 9, 2026
d625ca9
Fix named numeric values and numeric conversions
CaelmBleidd Oct 9, 2026
7cdf113
Fix Go value semantics and replay generated inputs
CaelmBleidd Oct 9, 2026
7c80e03
Fix Go defer, interface and aggregate value semantics
CaelmBleidd Oct 9, 2026
0519f6d
Correct Go iteration and aggregate equality with native regressions
CaelmBleidd Oct 12, 2026
b019d02
Preserve Go map key value equality and immutable snapshots
CaelmBleidd Oct 12, 2026
3d43aa8
Clarify Go map representative decoding and import order
CaelmBleidd Oct 12, 2026
52b49e1
Materialize Go map entries without losing NaN or pointer identities
CaelmBleidd Oct 12, 2026
b895257
Keep nonreflexive and identity-sensitive Go keys in lossless witnesses
CaelmBleidd Oct 12, 2026
887839b
Make native map replay key comparisons explicit
CaelmBleidd Oct 12, 2026
051fa35
Add native signed-zero stored-key and input-map range oracles
CaelmBleidd Oct 13, 2026
9da7d21
Preserve actual map key payloads and constrain canonical input keys
CaelmBleidd Oct 13, 2026
f8440a8
Correct map expression and boolean accumulator static types
CaelmBleidd Oct 13, 2026
6de1cb1
Select typed property matcher overload for floating helpers
CaelmBleidd Oct 13, 2026
760503a
Refactor Go semantic helpers and satisfy source style checks
CaelmBleidd Oct 13, 2026
3378597
Store string range indexes with the declared Go integer width
CaelmBleidd Oct 13, 2026
aaece2a
Initialize Go allocations with the zero value of their pointee type
CaelmBleidd Oct 13, 2026
953468f
Use symbolic key proxies and isolated payload fields for input maps
CaelmBleidd Oct 13, 2026
bf50862
Observe deterministic map invariants without unreachable fixture bran…
CaelmBleidd Oct 13, 2026
a887a0d
Skip absent key payloads when finishing Go map range
CaelmBleidd Oct 13, 2026
85cae77
Revert "Initialize Go allocations with the zero value of their pointe…
CaelmBleidd Oct 13, 2026
f4617dc
Preserve distinct local Go type identities in exported SSA metadata
CaelmBleidd Oct 13, 2026
5518e13
Decode model map entries through their input memory domain
CaelmBleidd Oct 13, 2026
ad0f6f0
Exclude all observed negative keys when filling input map witnesses
CaelmBleidd Oct 13, 2026
92bcff9
Keep map witness decoding within source style limits
CaelmBleidd Oct 13, 2026
c6ddd37
Decode Go type schemas by serialized identity instead of API equality
CaelmBleidd Oct 13, 2026
d8a485a
Unbox named array pointees before indexing and slicing
CaelmBleidd Oct 13, 2026
c5131d6
Remove redundant converter trailing blank line
CaelmBleidd Oct 13, 2026
781423d
Constrain input map size by distinct observed member identities
CaelmBleidd Oct 13, 2026
f93739c
Explore both feasible input map zero signs explicitly
CaelmBleidd Oct 13, 2026
28ebf9b
Exclude null physical proxies from semantic map membership
CaelmBleidd Oct 13, 2026
1321084
Clarify Go key constraints and remove obsolete pointer checks
CaelmBleidd Oct 13, 2026
495d03c
Apply Go regression test argument wrapping
CaelmBleidd Oct 13, 2026
13800b7
[Go] Inherit the reviewed input reference set prerequisite
CaelmBleidd Oct 13, 2026
187fc84
[Go] Preserve rebased prototype source hygiene
CaelmBleidd Oct 13, 2026
e62eb16
[Go] Preserve collection size failure reasons during witness refinement
CaelmBleidd Oct 13, 2026
db1e40c
Preserve string range index types for discarded components
CaelmBleidd Oct 13, 2026
ef0f81d
Preserve materialization bound exception causes
CaelmBleidd Oct 13, 2026
b574403
[Go] Bound signed-zero map replay with a sufficient solver budget
CaelmBleidd Oct 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

- name: Compile Go prototype and run lint
- name: Run Go regression tests and lint
run: >-
./gradlew :usvm-go:compileTestKotlin :usvm-go:detektMain :usvm-go:detektTest
./gradlew :usvm-go:check :usvm-go:detektMain :usvm-go:detektTest
--configure-on-demand --no-daemon --max-workers=1

- name: Check Go exporter variadic method signatures
Expand Down
2 changes: 0 additions & 2 deletions usvm-core/src/main/kotlin/org/usvm/Composition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ open class UComposer<Type, USizeSort : USort>(
override fun transform(expr: UConcreteHeapRef): UExpr<UAddressSort> = expr

override fun transform(expr: UNullRef): UExpr<UAddressSort> = memory.nullRef()

override fun transform(expr: ULValuePointer): UExpr<UAddressSort> = expr
}

@Suppress("NOTHING_TO_INLINE")
Expand Down
2 changes: 0 additions & 2 deletions usvm-core/src/main/kotlin/org/usvm/ExprTransformer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ interface UTransformer<Type, USizeSort : USort> : KTransformer {
fun transform(expr: UConcreteHeapRef): UExpr<UAddressSort>

fun transform(expr: UNullRef): UExpr<UAddressSort>

fun transform(expr: ULValuePointer): UExpr<UAddressSort>
}

abstract class UExprTransformer<Type, USizeSort : USort>(
Expand Down
25 changes: 0 additions & 25 deletions usvm-core/src/main/kotlin/org/usvm/Expressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import io.ksmt.sort.KBvSort
import io.ksmt.sort.KFpSort
import io.ksmt.sort.KSort
import io.ksmt.sort.KUninterpretedSort
import org.usvm.memory.ULValue
import org.usvm.memory.USymbolicCollection
import org.usvm.memory.USymbolicCollectionId
import kotlin.contracts.ExperimentalContracts
Expand Down Expand Up @@ -316,30 +315,6 @@ class UIsSupertypeExpr<Type> internal constructor(

//endregion

//region Pointer Semantics

class ULValuePointer(
ctx: UContext<*>,
var lvalue: ULValue<*, *>
) : UExpr<UAddressSort>(ctx) {
override val sort: UAddressSort = ctx.addressSort

override fun accept(transformer: KTransformerBase): KExpr<UAddressSort> {
require(transformer is UTransformer<*, *>) { "Expected a UTransformer, but got: $transformer" }
return transformer.transform(this)
}

override fun internEquals(other: Any): Boolean = structurallyEqual(other) { lvalue }

override fun internHashCode(): Int = hash(lvalue)

override fun print(printer: ExpressionPrinter) {
printer.append("&$lvalue")
}
}

//endregion

//region Utils

val UBoolExpr.isFalse get() = this == ctx.falseExpr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,35 @@ import org.usvm.regions.Region
import org.usvm.sizeSort

object PrimitiveMapCollectionApi {
fun <MapType, KeySort : USort, ValueSort: USort, Reg : Region<Reg>> UState<MapType, *, *, *, *, *>.symbolicPrimitiveMapGet(
fun <
MapType,
KeySort : USort,
ValueSort : USort,
Reg : Region<Reg>,
> UState<MapType, *, *, *, *, *>.symbolicPrimitiveMapGet(
mapRef: UHeapRef,
key: UExpr<KeySort>,
mapType: MapType,
valueSort: ValueSort,
keyInfo: USymbolicCollectionKeyInfo<UExpr<KeySort>, Reg>,
): UExpr<ValueSort> = memory.read(UMapEntryLValue(key.sort, valueSort, mapRef, key, mapType, keyInfo))

fun <MapType, KeySort : USort, Reg : Region<Reg>> UState<MapType, *, *, *, *, *>.symbolicPrimitiveMapContains(
fun <
MapType,
KeySort : USort,
Reg : Region<Reg>,
> UState<MapType, *, *, *, *, *>.symbolicPrimitiveMapContains(
mapRef: UHeapRef,
key: UExpr<KeySort>,
mapType: MapType,
keyInfo: USymbolicCollectionKeyInfo<UExpr<KeySort>, Reg>,
): UBoolExpr = memory.setContainsElement(mapRef, key, mapType, keyInfo)

fun <MapType, KeySort : USort, Reg : Region<Reg>> UState<MapType, *, *, *, *, *>.symbolicPrimitiveMapAnyKey(
fun <
MapType,
KeySort : USort,
Reg : Region<Reg>,
> UState<MapType, *, *, *, *, *>.symbolicPrimitiveMapAnyKey(
mapRef: UHeapRef,
mapType: MapType,
keySort: KeySort,
Expand All @@ -66,7 +79,14 @@ object PrimitiveMapCollectionApi {
}
}

fun <MapType, USizeSort : USort, Ctx : UContext<USizeSort>, KeySort : USort, ValueSort : USort, Reg : Region<Reg>> UState<MapType, *, *, Ctx, *, *>.symbolicPrimitiveMapPut(
fun <
MapType,
USizeSort : USort,
Ctx : UContext<USizeSort>,
KeySort : USort,
ValueSort : USort,
Reg : Region<Reg>,
> UState<MapType, *, *, Ctx, *, *>.symbolicPrimitiveMapPut(
mapRef: UHeapRef,
key: UExpr<KeySort>,
value: UExpr<ValueSort>,
Expand All @@ -86,7 +106,13 @@ object PrimitiveMapCollectionApi {
memory.write(UMapLengthLValue(mapRef, mapType, sizeSort), updatedSize, keyIsNew)
}

fun <MapType, USizeSort : USort, Ctx : UContext<USizeSort>, KeySort : USort, Reg : Region<Reg>> UState<MapType, *, *, Ctx, *, *>.symbolicPrimitiveMapRemove(
fun <
MapType,
USizeSort : USort,
Ctx : UContext<USizeSort>,
KeySort : USort,
Reg : Region<Reg>,
> UState<MapType, *, *, Ctx, *, *>.symbolicPrimitiveMapRemove(
mapRef: UHeapRef,
key: UExpr<KeySort>,
mapType: MapType,
Expand All @@ -97,14 +123,20 @@ object PrimitiveMapCollectionApi {

val keyIsInMap = memory.read(mapContainsLValue)

// todo: skip values update?
memory.write(mapContainsLValue, rvalue = falseExpr, guard = trueExpr)

val updatedSize = mkSizeSubExpr(currentSize, mkSizeExpr(1))
memory.write(UMapLengthLValue(mapRef, mapType, sizeSort), updatedSize, keyIsInMap)
}

fun <MapType, USizeSort : USort, Ctx: UContext<USizeSort>, KeySort : USort, ValueSort : USort, Reg : Region<Reg>> UState<MapType, *, *, Ctx, *, *>.symbolicPrimitiveMapMergeInto(
fun <
MapType,
USizeSort : USort,
Ctx : UContext<USizeSort>,
KeySort : USort,
ValueSort : USort,
Reg : Region<Reg>,
> UState<MapType, *, *, Ctx, *, *>.symbolicPrimitiveMapCopyIntoEmpty(
dstRef: UHeapRef,
srcRef: UHeapRef,
mapType: MapType,
Expand All @@ -114,14 +146,21 @@ object PrimitiveMapCollectionApi {
) = with(ctx) {
val srcMapSize = symbolicObjectMapSize(srcRef, mapType)
val dstMapSize = symbolicObjectMapSize(dstRef, mapType)
require(dstMapSize == mkSizeExpr(0)) { "Map copy requires an empty destination" }

val containsSetId = USetRegionId(keySort, mapType, keyInfo)
memory.mapMerge(srcRef, dstRef, mapType, keySort, valueSort, keyInfo, containsSetId.uncheckedCast(), guard = trueExpr)
memory.mapMerge(
srcRef,
dstRef,
mapType,
keySort,
valueSort,
keyInfo,
containsSetId.uncheckedCast(),
guard = trueExpr
)
memory.setUnion(srcRef, dstRef, mapType, keySort, keyInfo, guard = trueExpr)

// todo: precise map size approximation?
// val sizeLowerBound = mkIte(mkBvSignedGreaterExpr(srcMapSize, dstMapSize), srcMapSize, dstMapSize)
val sizeUpperBound = mkSizeAddExpr(srcMapSize, dstMapSize)
memory.write(UMapLengthLValue(dstRef, mapType, sizeSort), sizeUpperBound, guard = trueExpr)
memory.write(UMapLengthLValue(dstRef, mapType, sizeSort), srcMapSize, guard = trueExpr)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,8 @@ class UInputToAllocatedSymbolicRefSetUnionAdapter<SetType>(

override fun collectSetElements(elements: USymbolicSetElementsCollector.Elements<UHeapRef>) {
val setElements = USymbolicSetElementsCollector.collect(setOfKeys.updates)
if (setElements.isInput) {
elements.isInput = true
}
// The initial input collection can contain elements absent from its update tree.
elements.isInput = true
setElements.elements.mapTo(elements.elements) { it.second }
}

Expand Down Expand Up @@ -211,9 +210,8 @@ class UInputToInputSymbolicRefSetUnionAdapter<SetType>(

override fun collectSetElements(elements: USymbolicSetElementsCollector.Elements<USymbolicSetElement<UAddressSort>>) {
val setElements = USymbolicSetElementsCollector.collect(setOfKeys.updates)
if (setElements.isInput) {
elements.isInput = true
}
// The initial input collection can contain elements absent from its update tree.
elements.isInput = true
setElements.elements.mapTo(elements.elements) { dstSetRef to it.second }
}

Expand Down
6 changes: 0 additions & 6 deletions usvm-core/src/main/kotlin/org/usvm/solver/ExprTranslator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import org.usvm.UIndexedMethodReturnValue
import org.usvm.UIsExpr
import org.usvm.UIsSubtypeExpr
import org.usvm.UIsSupertypeExpr
import org.usvm.ULValuePointer
import org.usvm.UNullRef
import org.usvm.URegisterReading
import org.usvm.USort
Expand Down Expand Up @@ -95,11 +94,6 @@ open class UExprTranslator<Type, USizeSort : USort>(
return const
}

override fun transform(expr: ULValuePointer): KExpr<UAddressSort> {
val const = expr.sort.mkConst("&${expr.lvalue}")
return const
}

override fun transform(expr: UConcreteHeapRef): KExpr<UAddressSort> {
require(isStaticHeapRef(expr)) { "Unexpected ref: $expr" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import org.usvm.UExpr
import org.usvm.UIndexedMethodReturnValue
import org.usvm.UIsSubtypeExpr
import org.usvm.UIsSupertypeExpr
import org.usvm.ULValuePointer
import org.usvm.UNullRef
import org.usvm.URegisterReading
import org.usvm.USort
Expand Down Expand Up @@ -125,8 +124,6 @@ open class USoftConstraintsProvider<Type, USizeSort : USort>(

override fun transform(expr: UNullRef): UExpr<UAddressSort> = expr

override fun transform(expr: ULValuePointer): UExpr<UAddressSort> = expr

override fun transform(expr: UIsSubtypeExpr<Type>): UBoolExpr = expr

override fun transform(expr: UIsSupertypeExpr<Type>): UBoolExpr = expr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,35 @@ class ObjectMapTest : SymbolicCollectionTestBase() {
testMapMerge(symbolicMap0, symbolicMap1)
}

@Test
fun testCopiedInputMapCanSelectAnInitialKey() = testInputCopyRetainsUnknownKey(throughSecondCopy = false)

@Test
fun testRepeatedInputMapCopyCanSelectAnInitialKey() = testInputCopyRetainsUnknownKey(throughSecondCopy = true)

private fun testInputCopyRetainsUnknownKey(throughSecondCopy: Boolean) = scope.doWithState {
val source = ctx.mkRegisterReading(idx = 99, sort = ctx.addressSort)
val firstCopy = mkSymbolicObjectMap(mapType)

symbolicObjectMapMergeInto(firstCopy, source, mapType, ctx.sizeSort)
val copy = if (throughSecondCopy) {
val secondCopy = mkSymbolicObjectMap(mapType)
symbolicObjectMapMergeInto(secondCopy, firstCopy, mapType, ctx.sizeSort)
secondCopy
} else {
firstCopy
}

val selectedKey = symbolicObjectMapAnyKey(copy, mapType)
val sourceContains = symbolicObjectMapContains(source, selectedKey, mapType)
val copyContains = symbolicObjectMapContains(copy, selectedKey, mapType)

checkWithSolver {
assertPossible { mkAnd(mkNot(mkHeapRefEq(source, nullRef)), sourceContains, copyContains) }
assertImpossible { mkAnd(copyContains, mkNot(sourceContains)) }
}
}

@Test
fun testMapRetrieveAnyKey() = scope.doWithState {
callStack.push(method = Any(), returnSite = null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.usvm.api.collections

import io.ksmt.utils.asExpr
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.usvm.UBv32Sort
import org.usvm.api.collection.ObjectMapCollectionApi.mkSymbolicObjectMap
import org.usvm.api.collection.ObjectMapCollectionApi.symbolicObjectMapSize
import org.usvm.api.collection.PrimitiveMapCollectionApi.symbolicPrimitiveMapContains
import org.usvm.api.collection.PrimitiveMapCollectionApi.symbolicPrimitiveMapCopyIntoEmpty
import org.usvm.api.collection.PrimitiveMapCollectionApi.symbolicPrimitiveMapGet
import org.usvm.api.collection.PrimitiveMapCollectionApi.symbolicPrimitiveMapPut
import org.usvm.api.collection.PrimitiveMapCollectionApi.symbolicPrimitiveMapRemove
import org.usvm.memory.key.USizeExprKeyInfo
import org.usvm.mkSizeExpr
import org.usvm.sizeSort
import org.usvm.types.single.SingleTypeSystem
import kotlin.test.assertEquals

class PrimitiveMapTest : SymbolicCollectionTestBase() {
private val mapType = SingleTypeSystem.SingleType

@Test
fun overwriteAndRepeatedRemovalPreserveSize() = scope.doWithState {
val map = mkSymbolicObjectMap(mapType)
val key = ctx.mkBv(7)
val keyInfo = USizeExprKeyInfo<UBv32Sort>()

symbolicPrimitiveMapPut(map, key, ctx.mkBv(1), mapType, keyInfo)
symbolicPrimitiveMapPut(map, key, ctx.mkBv(2), mapType, keyInfo)

assertEquals(ctx.mkSizeExpr(1), symbolicObjectMapSize(map, mapType))
assertEquals(ctx.mkBv(2), symbolicPrimitiveMapGet(map, key, mapType, ctx.bv32Sort, keyInfo))

symbolicPrimitiveMapRemove(map, key, mapType, keyInfo)
symbolicPrimitiveMapRemove(map, key, mapType, keyInfo)

assertEquals(ctx.mkSizeExpr(0), symbolicObjectMapSize(map, mapType))
assertEquals(ctx.falseExpr, symbolicPrimitiveMapContains(map, key, mapType, keyInfo))
}

@Test
fun symbolicKeyEqualityControlsSize() = scope.doWithState {
val map = mkSymbolicObjectMap(mapType)
val first = ctx.mkRegisterReading(idx = 0, sort = ctx.bv32Sort)
val second = ctx.mkRegisterReading(idx = 1, sort = ctx.bv32Sort)
val keyInfo = USizeExprKeyInfo<UBv32Sort>()

symbolicPrimitiveMapPut(map, first, ctx.mkBv(1), mapType, keyInfo)
symbolicPrimitiveMapPut(map, second, ctx.mkBv(2), mapType, keyInfo)
val size = symbolicObjectMapSize(map, mapType).asExpr(ctx.sizeSort)

checkWithSolver {
assertImpossible {
mkAnd(mkEq(first, second), mkNot(mkEq(size, mkBv(1))))
}
assertImpossible {
mkAnd(mkNot(mkEq(first, second)), mkNot(mkEq(size, mkBv(2))))
}
}
}

@Test
fun copyPreservesEntriesAndSize() = scope.doWithState {
val source = mkSymbolicObjectMap(mapType)
val destination = mkSymbolicObjectMap(mapType)
val key = ctx.mkBv(7)
val keyInfo = USizeExprKeyInfo<UBv32Sort>()
symbolicPrimitiveMapPut(source, key, ctx.mkBv(42), mapType, keyInfo)

symbolicPrimitiveMapCopyIntoEmpty(destination, source, mapType, ctx.bv32Sort, ctx.bv32Sort, keyInfo)

assertEquals(ctx.mkSizeExpr(1), symbolicObjectMapSize(destination, mapType))
assertEquals(ctx.trueExpr, symbolicPrimitiveMapContains(destination, key, mapType, keyInfo))
assertEquals(ctx.mkBv(42), symbolicPrimitiveMapGet(destination, key, mapType, ctx.bv32Sort, keyInfo))
}

@Test
fun copyingIntoNonemptyMapIsRejected() = scope.doWithState {
val source = mkSymbolicObjectMap(mapType)
val destination = mkSymbolicObjectMap(mapType)
val keyInfo = USizeExprKeyInfo<UBv32Sort>()
symbolicPrimitiveMapPut(destination, ctx.mkBv(1), ctx.mkBv(42), mapType, keyInfo)

assertThrows<IllegalArgumentException> {
symbolicPrimitiveMapCopyIntoEmpty(destination, source, mapType, ctx.bv32Sort, ctx.bv32Sort, keyInfo)
}
}
}
Loading
Loading