11 Commits

Author SHA1 Message Date
Mark Thom
4e7d9f9010 update README.md
Some checks failed
CI / style (push) Has been cancelled
CI / read-msrv (push) Has been cancelled
CI / build-test (--no-default-features --features=all-simple-cross, miri, true, ubuntu-22.04, nightly, s390x-unknown-linux-gnu, --no-run --no-default-features --features=all-simple-cross) (push) Has been cancelled
CI / build-test (--no-default-features, ubuntu-22.04, true, nightly, wasm32-unknown-unknown, --no-run --no-default-features, true) (push) Has been cancelled
CI / build-test (miri, true, ubuntu-22.04, nightly, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (true, macos-latest, true, stable, x86_64-apple-darwin) (push) Has been cancelled
CI / build-test (true, ubuntu-22.04, true, stable, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (true, windows-latest, true, stable, x86_64-pc-windows-msvc) (push) Has been cancelled
CI / build-test (ubuntu-22.04, ${{ needs.read-msrv.outputs.msrv }}, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-22.04, beta, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-22.04, true, stable, i686-unknown-linux-gnu) (push) Has been cancelled
CI / logtalk-test (push) Has been cancelled
CI / report (push) Has been cancelled
CI / release (push) Has been cancelled
Docker Publish / build (push) Has been cancelled
2026-07-21 23:51:19 -07:00
Mark Thom
d3edffb7fc update README.md 2026-07-21 23:48:56 -07:00
Mark Thom
295a642034 Merge pull request #3372 from triska/global_cardinality
FIXED: Correct global_cardinality/2 propagation with reified constraints.
2026-06-30 17:25:30 -07:00
Mark Thom
ae0999a3bb Merge pull request #3398 from Skgland/fix-3320
fix incorrect missing feature errors
2026-06-30 17:25:02 -07:00
Mark Thom
11fd39bbaf Merge pull request #3275 from abmclin/Issue-3223-migrate-to-Rust-edition-2024
Issue 3223 migrate to rust edition 2024
2026-06-30 17:22:27 -07:00
Skgland
eb06f19c32 fix incorrect missing feature errors
this was accidentally introduced in mthom/scryer-prolog#3320

fixes mthom/scryer-prolog#3397
2026-06-29 20:13:10 +02:00
Alexander McLin
ea88ca552d Issue 3223: reformat via cargo fmt 2026-06-16 21:18:49 -04:00
Alexander McLin
eabbf67e86 Issue 3223: add more unsafe scopes 2026-06-16 21:14:43 -04:00
Alexander McLin
7a636b0394 Issue 3223: trying again repair for the failing wasm32 build
Removed `mut` modifier completely.
2026-06-16 21:05:12 -04:00
Alexander McLin
71ea74a077 Issue 3223: repair for the failing wasm32 build
Removed `ref` modifier due to not being allowed when implicitly borrowing.
2026-06-16 21:05:12 -04:00
Markus Triska
9b73536cdd FIXED: Correct global_cardinality/2 propagation with reified constraints.
This issue was found by @david-sitsky in a Prolog formulation of an
interesting scheduling task:

    https://github.com/mthom/scryer-prolog/discussions/3341

See also the filed issue:

    https://github.com/mthom/scryer-prolog/issues/3369

Thank you a lot!

This issue needs more analysis to find out what exactly happened here,
whether other cases like it exist, and whether it can be addressed in
a better way. I would greatly appreciate any help!
2026-06-10 20:31:20 +02:00
5 changed files with 120 additions and 146 deletions

View File

@@ -73,9 +73,10 @@ Extend Scryer Prolog to include the following, among other features:
- [ ] Inlining all built-ins and system call instructions. - [ ] Inlining all built-ins and system call instructions.
- [x] Greatly reducing the number of instructions used to compile disjunctives. - [x] Greatly reducing the number of instructions used to compile disjunctives.
- [x] Storing short atoms to heap cells without writing them to the atom table. - [x] Storing short atoms to heap cells without writing them to the atom table.
- [ ] Configurable JIT/on-demand indexing over all arguments
(documented in "[Demand-Driven Indexing of Prolog Clauses](https://user.it.uu.se/~kostis/Papers/iclp07.pdf)"). (_in progress_)
- [ ] A compacting garbage collector satisfying the five properties of - [ ] A compacting garbage collector satisfying the five properties of
"[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_) "[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_)
- [ ] Mode declarations.
## Phase 3 ## Phase 3
@@ -103,8 +104,6 @@ Gustafson's book "[The End of Error](http://www.johngustafson.net/unums.html)."
3. Add concurrent tables to manage shared references to atoms and 3. Add concurrent tables to manage shared references to atoms and
strings. strings.
4. Add some form of JIT predicate indexing.
## Installing Scryer Prolog ## Installing Scryer Prolog
### Binaries ### Binaries

View File

@@ -3,7 +3,7 @@
Author: Markus Triska Author: Markus Triska
E-mail: triska@metalevel.at E-mail: triska@metalevel.at
WWW: https://www.metalevel.at WWW: https://www.metalevel.at
Copyright (C): 2016-2024 Markus Triska Copyright (C): 2016-2026 Markus Triska
This library provides CLP(): This library provides CLP():
@@ -6844,6 +6844,22 @@ gcc_global(Vs, KNs) -->
% the propagator of tuples_in/2). Hence: We need this only if % the propagator of tuples_in/2). Hence: We need this only if
% an example shows it, ideally found by a systematic search % an example shows it, ideally found by a systematic search
% that can be used to test the implementation. % that can be used to test the implementation.
%
% UPDATE: A case that requires gcc_check//1 was recently found,
% see: https://github.com/mthom/scryer-prolog/issues/3369.
%
% We need to study carefully what exactly happens in this case.
% The example involves reified constraints, which post constraints
% in their propagators. The example also involves multiple queues,
% because not all reified constraints use variables_same_queue/1
% (for instance (#==>)/2 does not), and variables_same_queue/1 would
% in fact need changes to handle cases where variables no longer
% have a clpz attribute attached. Such cases arise for variables
% involved in entailed reified constraints.
gcc_check(KNs), % see comment above.
do_queue, % is this needed? this (also) needs more analysis.
{ with_local_attributes(Vs, { with_local_attributes(Vs,
(gcc_arcs(KNs, S, Vals), (gcc_arcs(KNs, S, Vals),
variables_with_num_occurrences(Vs, VNs), variables_with_num_occurrences(Vs, VNs),

View File

@@ -7915,7 +7915,7 @@ impl Machine {
#[cfg(not(feature = "crypto-full"))] #[cfg(not(feature = "crypto-full"))]
{ {
let stub_gen = || functor_stub(atom!("crypto_random_byte"), 1); let stub_gen = || functor_stub(atom!("crypto_random_byte"), 1);
let err = self.machine_st.missing_feature_error(atom!("crypto")); let err = self.machine_st.missing_feature_error(atom!("crypto-full"));
let exception = self.machine_st.error_form(err, stub_gen()); let exception = self.machine_st.error_form(err, stub_gen());
self.machine_st.throw_exception(exception); self.machine_st.throw_exception(exception);
} }
@@ -8079,7 +8079,7 @@ impl Machine {
#[cfg(not(feature = "crypto-full"))] #[cfg(not(feature = "crypto-full"))]
{ {
let stub_gen = || functor_stub(atom!("crypto_data_hash"), 1); let stub_gen = || functor_stub(atom!("crypto_data_hash"), 1);
let err = self.machine_st.missing_feature_error(atom!("crypto")); let err = self.machine_st.missing_feature_error(atom!("crypto-full"));
let exception = self.machine_st.error_form(err, stub_gen()); let exception = self.machine_st.error_form(err, stub_gen());
self.machine_st.throw_exception(exception); self.machine_st.throw_exception(exception);
return; return;
@@ -8132,7 +8132,7 @@ impl Machine {
#[cfg(not(feature = "crypto-full"))] #[cfg(not(feature = "crypto-full"))]
{ {
let err = self.machine_st.missing_feature_error(atom!("crypto")); let err = self.machine_st.missing_feature_error(atom!("crypto-full"));
let exception = self.machine_st.error_form(err, stub_gen()); let exception = self.machine_st.error_form(err, stub_gen());
self.machine_st.throw_exception(exception); self.machine_st.throw_exception(exception);
} }
@@ -8215,7 +8215,7 @@ impl Machine {
#[cfg(not(feature = "crypto-full"))] #[cfg(not(feature = "crypto-full"))]
{ {
let err = self.machine_st.missing_feature_error(atom!("crypto")); let err = self.machine_st.missing_feature_error(atom!("crypto-full"));
let exception = self.machine_st.error_form(err, stub1_gen()); let exception = self.machine_st.error_form(err, stub1_gen());
self.machine_st.throw_exception(exception); self.machine_st.throw_exception(exception);
} }
@@ -8283,7 +8283,7 @@ impl Machine {
#[cfg(not(feature = "crypto-full"))] #[cfg(not(feature = "crypto-full"))]
{ {
let err = self.machine_st.missing_feature_error(atom!("crypto")); let err = self.machine_st.missing_feature_error(atom!("crypto-full"));
let exception = self.machine_st.error_form(err, stub1_gen()); let exception = self.machine_st.error_form(err, stub1_gen());
self.machine_st.throw_exception(exception); self.machine_st.throw_exception(exception);
} }
@@ -8409,152 +8409,111 @@ impl Machine {
pub(crate) fn crypto_curve_scalar_mult(&mut self) { pub(crate) fn crypto_curve_scalar_mult(&mut self) {
let stub_gen = || functor_stub(atom!("crypto_curve_scalar_mult"), 4); let stub_gen = || functor_stub(atom!("crypto_curve_scalar_mult"), 4);
{ let scalar_bytes = self
let scalar_bytes = self .machine_st
.machine_st .integers_to_bytevec(self.machine_st.registers[2], stub_gen);
.integers_to_bytevec(self.machine_st.registers[2], stub_gen); let point_bytes = self
let point_bytes = self .machine_st
.machine_st .integers_to_bytevec(self.machine_st.registers[3], stub_gen);
.integers_to_bytevec(self.machine_st.registers[3], stub_gen);
let mut point = secp256k1::Point::decode(&point_bytes).unwrap(); let mut point = secp256k1::Point::decode(&point_bytes).unwrap();
let scalar = secp256k1::Scalar::decode_reduce(&scalar_bytes); let scalar = secp256k1::Scalar::decode_reduce(&scalar_bytes);
point *= scalar; point *= scalar;
let uncompressed = step_or_resource_error!( let uncompressed = step_or_resource_error!(
self.machine_st, self.machine_st,
self.u8s_to_string(&point.encode_uncompressed()) self.u8s_to_string(&point.encode_uncompressed())
); );
unify!(self.machine_st, self.machine_st.registers[4], uncompressed); unify!(self.machine_st, self.machine_st.registers[4], uncompressed);
}
{
let err = self.machine_st.missing_feature_error(atom!("crypto"));
let exception = self.machine_st.error_form(err, stub_gen());
self.machine_st.throw_exception(exception);
}
} }
#[inline(always)] #[inline(always)]
pub(crate) fn ed25519_seed_to_public_key(&mut self) { pub(crate) fn ed25519_seed_to_public_key(&mut self) {
let stub_gen = || functor_stub(atom!("ed25519_seed_keypair"), 2); let stub_gen = || functor_stub(atom!("ed25519_seed_keypair"), 2);
{ let seed_bytes = self
let seed_bytes = self .machine_st
.machine_st .integers_to_bytevec(self.machine_st.registers[1], stub_gen);
.integers_to_bytevec(self.machine_st.registers[1], stub_gen);
let skey = ed25519::PrivateKey::from_seed(&seed_bytes); let skey = ed25519::PrivateKey::from_seed(&seed_bytes);
let complete_string = step_or_resource_error!( let complete_string = step_or_resource_error!(
self.machine_st, self.machine_st,
self.u8s_to_string(skey.public_key.encoded.as_ref()) self.u8s_to_string(skey.public_key.encoded.as_ref())
); );
unify!( unify!(
self.machine_st, self.machine_st,
self.machine_st.registers[2], self.machine_st.registers[2],
complete_string complete_string
); );
}
{
let err = self.machine_st.missing_feature_error(atom!("crypto"));
let exception = self.machine_st.error_form(err, stub_gen());
self.machine_st.throw_exception(exception);
}
} }
#[inline(always)] #[inline(always)]
pub(crate) fn ed25519_sign_raw(&mut self) { pub(crate) fn ed25519_sign_raw(&mut self) {
let stub_gen = || functor_stub(atom!("ed25519_sign"), 4); let stub_gen = || functor_stub(atom!("ed25519_sign"), 4);
{ let seed_bytes = self
let seed_bytes = self .machine_st
.machine_st .integers_to_bytevec(self.machine_st.registers[1], stub_gen);
.integers_to_bytevec(self.machine_st.registers[1], stub_gen);
let skey = ed25519::PrivateKey::from_seed(&seed_bytes); let skey = ed25519::PrivateKey::from_seed(&seed_bytes);
let encoding = cell_as_atom!(self.deref_register(3)); let encoding = cell_as_atom!(self.deref_register(3));
let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding);
let sig = skey.sign_raw(&data); let sig = skey.sign_raw(&data);
let sig_list = step_or_resource_error!( let sig_list = step_or_resource_error!(
self.machine_st, self.machine_st,
sized_iter_to_heap_list( sized_iter_to_heap_list(
&mut self.machine_st.heap, &mut self.machine_st.heap,
sig.as_ref().len(), sig.as_ref().len(),
sig.as_ref() sig.as_ref()
.iter() .iter()
.map(|b| fixnum_as_cell!(Fixnum::build_with(*b))) .map(|b| fixnum_as_cell!(Fixnum::build_with(*b)))
) )
); );
unify!(self.machine_st, self.machine_st.registers[4], sig_list); unify!(self.machine_st, self.machine_st.registers[4], sig_list);
}
{
let err = self.machine_st.missing_feature_error(atom!("crypto"));
let exception = self.machine_st.error_form(err, stub_gen());
self.machine_st.throw_exception(exception);
}
} }
#[inline(always)] #[inline(always)]
pub(crate) fn ed25519_verify_raw(&mut self) { pub(crate) fn ed25519_verify_raw(&mut self) {
let stub_gen = || functor_stub(atom!("ed25519_verify"), 4); let stub_gen = || functor_stub(atom!("ed25519_verify"), 4);
{ let key_bytes = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet"));
let key_bytes = let pkey = ed25519::PublicKey::decode(&key_bytes).unwrap();
self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet"));
let pkey = ed25519::PublicKey::decode(&key_bytes).unwrap();
let encoding = cell_as_atom!(self.deref_register(3)); let encoding = cell_as_atom!(self.deref_register(3));
let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding);
let signature = self let signature = self
.machine_st .machine_st
.integers_to_bytevec(self.machine_st.registers[4], stub_gen); .integers_to_bytevec(self.machine_st.registers[4], stub_gen);
self.machine_st.fail = !pkey.verify_raw(&signature, &data); self.machine_st.fail = !pkey.verify_raw(&signature, &data);
}
{
let err = self.machine_st.missing_feature_error(atom!("crypto"));
let exception = self.machine_st.error_form(err, stub_gen());
self.machine_st.throw_exception(exception);
}
} }
#[inline(always)] #[inline(always)]
pub(crate) fn curve25519_scalar_mult(&mut self) { pub(crate) fn curve25519_scalar_mult(&mut self) {
let stub_gen = || functor_stub(atom!("curve25519_scalar_mult"), 3); let stub_gen = || functor_stub(atom!("curve25519_scalar_mult"), 3);
{ let scalar_bytes = self
let scalar_bytes = self .machine_st
.machine_st .integers_to_bytevec(self.machine_st.registers[1], stub_gen);
.integers_to_bytevec(self.machine_st.registers[1], stub_gen); let point_bytes = self
let point_bytes = self .machine_st
.machine_st .integers_to_bytevec(self.machine_st.registers[2], stub_gen);
.integers_to_bytevec(self.machine_st.registers[2], stub_gen);
let result = x25519::x25519( let result = x25519::x25519(
&<[u8; 32]>::try_from(&point_bytes[..]).unwrap(), &<[u8; 32]>::try_from(&point_bytes[..]).unwrap(),
&<[u8; 32]>::try_from(&scalar_bytes[..]).unwrap(), &<[u8; 32]>::try_from(&scalar_bytes[..]).unwrap(),
); );
let string = step_or_resource_error!(self.machine_st, self.u8s_to_string(&result[..])); let string = step_or_resource_error!(self.machine_st, self.u8s_to_string(&result[..]));
unify!(self.machine_st, self.machine_st.registers[3], string); unify!(self.machine_st, self.machine_st.registers[3], string);
}
{
let err = self.machine_st.missing_feature_error(atom!("crypto"));
let exception = self.machine_st.error_form(err, stub_gen());
self.machine_st.throw_exception(exception);
}
} }
#[inline(always)] #[inline(always)]

View File

@@ -212,32 +212,34 @@ impl<T: RawBlockTraits> SerialOffsetTable<T> {
} }
unsafe fn build_with(&mut self, value: T) -> usize { unsafe fn build_with(&mut self, value: T) -> usize {
let mut ptr; unsafe {
let mut ptr;
loop { loop {
ptr = self.block.alloc(size_of::<T>()); ptr = self.block.alloc(size_of::<T>());
if ptr.is_null() { if ptr.is_null() {
let new_block = self.block.grow_new().unwrap(); let new_block = self.block.grow_new().unwrap();
self.block = new_block; self.block = new_block;
} else { } else {
break; break;
}
} }
}
ptr::write(ptr as *mut T, value); ptr::write(ptr as *mut T, value);
// SAFETY: `ptr` was obtained from `self.block.alloc()` // SAFETY: `ptr` was obtained from `self.block.alloc()`
self.block.get_offset(ptr) self.block.get_offset(ptr)
}
} }
#[inline] #[inline]
unsafe fn lookup(&self, offset: usize) -> &T { unsafe fn lookup(&self, offset: usize) -> &T {
&*self.block.get_unchecked(offset).cast::<T>() unsafe { &*self.block.get_unchecked(offset).cast::<T>() }
} }
#[inline] #[inline]
unsafe fn lookup_mut(&mut self, offset: usize) -> &mut T { unsafe fn lookup_mut(&mut self, offset: usize) -> &mut T {
&mut *self.block.get_unchecked(offset).cast::<T>().cast_mut() unsafe { &mut *self.block.get_unchecked(offset).cast::<T>().cast_mut() }
} }
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]

View File

@@ -129,23 +129,21 @@ impl WasmQueryState {
let mut error = None; let mut error = None;
let mut to_drop = false; let mut to_drop = false;
match &mut self.inner { match &mut self.inner {
Some(ref mut inner) => { Some(inner) => inner.with_query_state_mut(|query_state| match query_state.next() {
inner.with_query_state_mut(|query_state| match query_state.next() { Some(Ok(leaf_answer)) => {
Some(Ok(leaf_answer)) => { js_sys::Reflect::set(&ret, &"value".into(), &leaf_answer.into()).unwrap();
js_sys::Reflect::set(&ret, &"value".into(), &leaf_answer.into()).unwrap(); js_sys::Reflect::set(&ret, &"done".into(), &false.into()).unwrap();
js_sys::Reflect::set(&ret, &"done".into(), &false.into()).unwrap(); }
} Some(Err(error_term)) => {
Some(Err(error_term)) => { let js_error = js_sys::Error::new("Prolog error");
let js_error = js_sys::Error::new("Prolog error"); js_error.set_cause(&error_term.into());
js_error.set_cause(&error_term.into()); error = Some(js_error);
error = Some(js_error); }
} None => {
None => { js_sys::Reflect::set(&ret, &"done".into(), &true.into()).unwrap();
js_sys::Reflect::set(&ret, &"done".into(), &true.into()).unwrap(); to_drop = true;
to_drop = true; }
} }),
})
}
None => return Err(js_sys::Error::new("This query was already dropped").into()), None => return Err(js_sys::Error::new("This query was already dropped").into()),
} }