Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
41b5c64
very basic setup
ilanashapiro Jul 23, 2025
563906d
Merge branch 'Z3Prover:master' into parallel-solving
ilanashapiro Jul 24, 2025
4b87458
Merge remote-tracking branch 'upstream/ilana' into parallel-solving
ilanashapiro Jul 24, 2025
a6c51df
ensure solve_eqs is fully disabled when smt.solve_eqs=false, #7743
NikolajBjorner Jul 24, 2025
01633f7
respect smt configuration parameter in elim_unconstrained simplifier
NikolajBjorner Jul 24, 2025
1a488bb
indentation
NikolajBjorner Jul 25, 2025
6550495
add bash files for test runs
ilanashapiro Jul 25, 2025
e549286
add option to selectively disable variable solving for only ground ex…
NikolajBjorner Jul 26, 2025
95be0cf
remove verbose output
NikolajBjorner Jul 26, 2025
0528c86
fix #7745
NikolajBjorner Jul 26, 2025
8e1a528
ensure atomic constraints are processed by arithmetic solver
NikolajBjorner Jul 26, 2025
1f8b081
#7739 optimization
NikolajBjorner Jul 26, 2025
ad2934f
fix unsound len(substr) axiom
NikolajBjorner Jul 26, 2025
eb24488
FreshConst is_sort (#7748)
humnrdble Jul 27, 2025
e3139d4
#7750
NikolajBjorner Jul 27, 2025
0761394
Add parameter validation for selected API functions
NikolajBjorner Jul 27, 2025
67695b4
updates to ac-plugin
NikolajBjorner Jul 27, 2025
f77123c
enable passive, add check for bloom up-to-date
NikolajBjorner Jul 28, 2025
36fbee3
add top-k fixed-sized min-heap priority queue for top scoring literals
ilanashapiro Jul 28, 2025
f607a70
Merge branch 'Z3Prover:master' into parallel-solving
ilanashapiro Jul 28, 2025
4eeb98d
Merge branch 'ilana' into parallel-solving
NikolajBjorner Jul 29, 2025
2c188a5
set up worker thread batch manager for multithreaded batch cubes para…
ilanashapiro Jul 29, 2025
375d537
Merge branch 'parallel-solving' of github.com:ilanashapiro/z3 into pa…
ilanashapiro Jul 29, 2025
8a6cbec
fix bug in parallel solving batch setup
ilanashapiro Jul 30, 2025
e6213f8
fix bug
ilanashapiro Jul 31, 2025
2d876d5
allow for internalize implies
NikolajBjorner Aug 1, 2025
89cc9bd
disable pre-processing during cubing
NikolajBjorner Aug 1, 2025
12df9f8
Merge branch 'Z3Prover:master' into parallel-solving
ilanashapiro Aug 1, 2025
2a26776
debugging
ilanashapiro Aug 1, 2025
33c184f
Merge branch 'parallel-solving' of github.com:ilanashapiro/z3 into pa…
ilanashapiro Aug 1, 2025
97aa46a
remove default constructor
nunoplopes Aug 3, 2025
f23b053
remove a bunch of string copies
nunoplopes Aug 3, 2025
d8fafd8
Update euf_ac_plugin.cpp
NikolajBjorner Aug 3, 2025
b9b3e0d
Update euf_completion.cpp
NikolajBjorner Aug 3, 2025
d66fabe
Update smt_parallel.cpp
NikolajBjorner Aug 3, 2025
aac8787
process cubes as lists of individual lits
ilanashapiro Aug 4, 2025
a0a0670
Merge branch 'ilana' into parallel-solving
NikolajBjorner Aug 4, 2025
c9c3548
Merge branch 'Z3Prover:master' into parallel-solving
ilanashapiro Aug 4, 2025
7df95c0
merge
ilanashapiro Aug 4, 2025
cc8bc84
Merge branch 'parallel-solving' of github.com:ilanashapiro/z3 into pa…
ilanashapiro Aug 4, 2025
e520a42
merge
ilanashapiro Aug 4, 2025
7a8ba4b
Add support for Algebraic Datatypes in JavaScript/TypeScript bindings…
Copilot Aug 5, 2025
3982b29
chipping away at the new code structure
ilanashapiro Aug 5, 2025
0b21376
Merge branch 'Z3Prover:master' into parallel-solving
ilanashapiro Aug 5, 2025
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
70 changes: 70 additions & 0 deletions src/api/js/src/high-level/high-level.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -890,4 +890,74 @@ describe('high-level', () => {
expect(model.eval(z).eqIdentity(Int.val(5))).toBeTruthy();
});
});

describe('datatypes', () => {
it('should create simple enum datatype', async () => {
const { Datatype, Int, Bool, Solver } = api.Context('main');

// Create a simple Color enum datatype
const Color = Datatype('Color');
Color.declare('red');
Color.declare('green');
Color.declare('blue');

const ColorSort = Color.create();

// Test that we can access the constructors
expect(typeof (ColorSort as any).red).not.toBe('undefined');
expect(typeof (ColorSort as any).green).not.toBe('undefined');
expect(typeof (ColorSort as any).blue).not.toBe('undefined');

// Test that we can access the recognizers
expect(typeof (ColorSort as any).is_red).not.toBe('undefined');
expect(typeof (ColorSort as any).is_green).not.toBe('undefined');
expect(typeof (ColorSort as any).is_blue).not.toBe('undefined');
});

it('should create recursive list datatype', async () => {
const { Datatype, Int, Solver } = api.Context('main');

// Create a recursive List datatype like in the Python example
const List = Datatype('List');
List.declare('cons', ['car', Int.sort()], ['cdr', List]);
List.declare('nil');

const ListSort = List.create();

// Test that constructors and accessors exist
expect(typeof (ListSort as any).cons).not.toBe('undefined');
expect(typeof (ListSort as any).nil).not.toBe('undefined');
expect(typeof (ListSort as any).is_cons).not.toBe('undefined');
expect(typeof (ListSort as any).is_nil).not.toBe('undefined');
expect(typeof (ListSort as any).car).not.toBe('undefined');
expect(typeof (ListSort as any).cdr).not.toBe('undefined');
});

it('should create mutually recursive tree datatypes', async () => {
const { Datatype, Int } = api.Context('main');

// Create mutually recursive Tree and TreeList datatypes
const Tree = Datatype('Tree');
const TreeList = Datatype('TreeList');

Tree.declare('leaf', ['value', Int.sort()]);
Tree.declare('node', ['children', TreeList]);
TreeList.declare('nil');
TreeList.declare('cons', ['car', Tree], ['cdr', TreeList]);

const [TreeSort, TreeListSort] = Datatype.createDatatypes(Tree, TreeList);

// Test that both datatypes have their constructors
expect(typeof (TreeSort as any).leaf).not.toBe('undefined');
expect(typeof (TreeSort as any).node).not.toBe('undefined');
expect(typeof (TreeListSort as any).nil).not.toBe('undefined');
expect(typeof (TreeListSort as any).cons).not.toBe('undefined');

// Test accessors exist
expect(typeof (TreeSort as any).value).not.toBe('undefined');
expect(typeof (TreeSort as any).children).not.toBe('undefined');
expect(typeof (TreeListSort as any).car).not.toBe('undefined');
expect(typeof (TreeListSort as any).cdr).not.toBe('undefined');
});
});
});
197 changes: 197 additions & 0 deletions src/api/js/src/high-level/high-level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
Z3_ast_print_mode,
Z3_ast_vector,
Z3_context,
Z3_constructor,
Z3_constructor_list,
Z3_decl_kind,
Z3_error_code,
Z3_func_decl,
Expand Down Expand Up @@ -88,6 +90,10 @@ import {
FuncEntry,
SMTSetSort,
SMTSet,
Datatype,
DatatypeSort,
DatatypeExpr,
DatatypeCreation,
} from './types';
import { allSatisfy, assert, assertExhaustive } from './utils';

Expand Down Expand Up @@ -825,6 +831,17 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
}
}

const Datatype = Object.assign(
(name: string): DatatypeImpl => {
return new DatatypeImpl(ctx, name);
},
{
createDatatypes(...datatypes: DatatypeImpl[]): DatatypeSortImpl[] {
return createDatatypes(...datatypes);
}
}
);

////////////////
// Operations //
////////////////
Expand Down Expand Up @@ -2647,6 +2664,185 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
}
}

////////////////////////////
// Datatypes
////////////////////////////

class DatatypeImpl implements Datatype<Name> {
readonly ctx: Context<Name>;
readonly name: string;
public constructors: Array<[string, Array<[string, Sort<Name> | Datatype<Name>]>]> = [];

constructor(ctx: Context<Name>, name: string) {
this.ctx = ctx;
this.name = name;
}

declare(name: string, ...fields: Array<[string, Sort<Name> | Datatype<Name>]>): this {
this.constructors.push([name, fields]);
return this;
}

create(): DatatypeSort<Name> {
const datatypes = createDatatypes(this);
return datatypes[0];
}
}

class DatatypeSortImpl extends SortImpl implements DatatypeSort<Name> {
declare readonly __typename: DatatypeSort['__typename'];

numConstructors(): number {
return Z3.get_datatype_sort_num_constructors(contextPtr, this.ptr);
}

constructorDecl(idx: number): FuncDecl<Name> {
const ptr = Z3.get_datatype_sort_constructor(contextPtr, this.ptr, idx);
return new FuncDeclImpl(ptr);
}

recognizer(idx: number): FuncDecl<Name> {
const ptr = Z3.get_datatype_sort_recognizer(contextPtr, this.ptr, idx);
return new FuncDeclImpl(ptr);
}

accessor(constructorIdx: number, accessorIdx: number): FuncDecl<Name> {
const ptr = Z3.get_datatype_sort_constructor_accessor(contextPtr, this.ptr, constructorIdx, accessorIdx);
return new FuncDeclImpl(ptr);
}

cast(other: CoercibleToExpr<Name>): DatatypeExpr<Name>;
cast(other: DatatypeExpr<Name>): DatatypeExpr<Name>;
cast(other: CoercibleToExpr<Name> | DatatypeExpr<Name>): DatatypeExpr<Name> {
if (isExpr(other)) {
assert(this.eqIdentity(other.sort), 'Value cannot be converted to this datatype');
return other as DatatypeExpr<Name>;
}
throw new Error('Cannot coerce value to datatype expression');
}

subsort(other: Sort<Name>) {
_assertContext(other.ctx);
return this.eqIdentity(other);
}
}

class DatatypeExprImpl extends ExprImpl<Z3_ast, DatatypeSortImpl> implements DatatypeExpr<Name> {
declare readonly __typename: DatatypeExpr['__typename'];
}

function createDatatypes(...datatypes: DatatypeImpl[]): DatatypeSortImpl[] {
if (datatypes.length === 0) {
throw new Error('At least one datatype must be provided');
}

// All datatypes must be from the same context
const dtCtx = datatypes[0].ctx;
for (const dt of datatypes) {
if (dt.ctx !== dtCtx) {
throw new Error('All datatypes must be from the same context');
}
}

const sortNames = datatypes.map(dt => dt.name);
const constructorLists: Z3_constructor_list[] = [];
const scopedConstructors: Z3_constructor[] = [];

try {
// Create constructor lists for each datatype
for (const dt of datatypes) {
const constructors: Z3_constructor[] = [];

for (const [constructorName, fields] of dt.constructors) {
const fieldNames: string[] = [];
const fieldSorts: Z3_sort[] = [];
const fieldRefs: number[] = [];

for (const [fieldName, fieldSort] of fields) {
fieldNames.push(fieldName);

if (fieldSort instanceof DatatypeImpl) {
// Reference to another datatype being defined
const refIndex = datatypes.indexOf(fieldSort);
if (refIndex === -1) {
throw new Error(`Referenced datatype "${fieldSort.name}" not found in datatypes being created`);
}
// For recursive references, we pass null and the ref index
fieldSorts.push(null as any); // null will be handled by the Z3 API
fieldRefs.push(refIndex);
} else {
// Regular sort
fieldSorts.push((fieldSort as Sort<Name>).ptr);
fieldRefs.push(0);
}
}

const constructor = Z3.mk_constructor(
contextPtr,
Z3.mk_string_symbol(contextPtr, constructorName),
Z3.mk_string_symbol(contextPtr, `is_${constructorName}`),
fieldNames.map(name => Z3.mk_string_symbol(contextPtr, name)),
fieldSorts,
fieldRefs
);
constructors.push(constructor);
scopedConstructors.push(constructor);
}

const constructorList = Z3.mk_constructor_list(contextPtr, constructors);
constructorLists.push(constructorList);
}

// Create the datatypes
const sortSymbols = sortNames.map(name => Z3.mk_string_symbol(contextPtr, name));
const resultSorts = Z3.mk_datatypes(contextPtr, sortSymbols, constructorLists);

// Create DatatypeSortImpl instances
const results: DatatypeSortImpl[] = [];
for (let i = 0; i < resultSorts.length; i++) {
const sortImpl = new DatatypeSortImpl(resultSorts[i]);

// Attach constructor, recognizer, and accessor functions dynamically
const numConstructors = sortImpl.numConstructors();
for (let j = 0; j < numConstructors; j++) {
const constructor = sortImpl.constructorDecl(j);
const recognizer = sortImpl.recognizer(j);
const constructorName = constructor.name().toString();

// Attach constructor function
if (constructor.arity() === 0) {
// Nullary constructor (constant)
(sortImpl as any)[constructorName] = constructor.call();
} else {
(sortImpl as any)[constructorName] = constructor;
}

// Attach recognizer function
(sortImpl as any)[`is_${constructorName}`] = recognizer;

// Attach accessor functions
for (let k = 0; k < constructor.arity(); k++) {
const accessor = sortImpl.accessor(j, k);
const accessorName = accessor.name().toString();
(sortImpl as any)[accessorName] = accessor;
}
}

results.push(sortImpl);
}

return results;
} finally {
// Clean up resources
for (const constructor of scopedConstructors) {
Z3.del_constructor(contextPtr, constructor);
}
for (const constructorList of constructorLists) {
Z3.del_constructor_list(contextPtr, constructorList);
}
}
}

class QuantifierImpl<
QVarSorts extends NonEmptySortArray<Name>,
QSort extends BoolSort<Name> | SMTArraySort<Name, QVarSorts>,
Expand Down Expand Up @@ -3029,6 +3225,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel {
BitVec,
Array,
Set,
Datatype,

////////////////
// Operations //
Expand Down
Loading