Merge pull request #3108 from Skgland/issue-3073

emit a more appropriate error when passing the wrong argument count when constructing an ffi struct with the wrong argument count
This commit is contained in:
Mark Thom
2025-10-02 23:43:13 -07:00
committed by GitHub
4 changed files with 90 additions and 14 deletions

View File

@@ -180,10 +180,17 @@ impl StructImpl {
fn build( fn build(
&self, &self,
name: Atom,
structs_table: &HashMap<Atom, StructImpl>, structs_table: &HashMap<Atom, StructImpl>,
struct_args: &mut [Value], struct_args: &mut [Value],
) -> Result<FfiStruct, FfiError> { ) -> Result<FfiStruct, FfiError> {
let args = ArgValue::build_args(struct_args, &self.fields, structs_table)?; let args = ArgValue::build_args(
name,
ArgCountMismatchKind::Struct,
struct_args,
&self.fields,
structs_table,
)?;
let alloc = FfiStruct::new(self.layout()?, FfiAllocator::Rust)?; let alloc = FfiStruct::new(self.layout()?, FfiAllocator::Rust)?;
@@ -530,19 +537,30 @@ impl<'val> ArgValue<'val> {
return Err(FfiError::StructNotFound(*arg_type_name)); return Err(FfiError::StructNotFound(*arg_type_name));
}; };
Ok(Self::Struct(struct_type.build(structs_table, args)?)) Ok(Self::Struct(struct_type.build(
*arg_type_name,
structs_table,
args,
)?))
} }
FfiType::Void => Err(FfiError::VoidArgumentType), FfiType::Void => Err(FfiError::VoidArgumentType),
} }
} }
fn build_args( fn build_args(
name: Atom,
kind: ArgCountMismatchKind,
args: &'val mut [Value], args: &'val mut [Value],
types: &[FfiType], types: &[FfiType],
structs_table: &HashMap<Atom, StructImpl>, structs_table: &HashMap<Atom, StructImpl>,
) -> Result<Vec<Self>, FfiError> { ) -> Result<Vec<Self>, FfiError> {
if types.len() != args.len() { if types.len() != args.len() {
return Err(FfiError::ArgCountMismatch); return Err(FfiError::ArgCountMismatch {
name,
kind,
expected: types.len(),
got: args.len(),
});
} }
args.iter_mut() args.iter_mut()
@@ -699,9 +717,15 @@ impl ForeignFunctionTable {
let fn_impl = self let fn_impl = self
.table .table
.get(&fn_name) .get(&fn_name)
.ok_or(FfiError::FunctionNotFound(fn_name))?; .ok_or(FfiError::FunctionNotFound(fn_name, args.len()))?;
let args = ArgValue::build_args(&mut args, &fn_impl.args, &self.structs)?; let args = ArgValue::build_args(
fn_name,
ArgCountMismatchKind::Function,
&mut args,
&fn_impl.args,
&self.structs,
)?;
let args = PointerArgs::new(&args); let args = PointerArgs::new(&args);
@@ -760,7 +784,7 @@ impl ForeignFunctionTable {
let (_, args) = args.as_struct()?; let (_, args) = args.as_struct()?;
let ffi_struct = struct_impl.build(&self.structs, args)?; let ffi_struct = struct_impl.build(kind, &self.structs, args)?;
let ptr = ManuallyDrop::new(ffi_struct).ptr; let ptr = ManuallyDrop::new(ffi_struct).ptr;
@@ -946,9 +970,14 @@ pub enum FfiError {
ValueCast(Atom, Atom), ValueCast(Atom, Atom),
ValueOutOfRange(DomainErrorType, Value), ValueOutOfRange(DomainErrorType, Value),
VoidArgumentType, VoidArgumentType,
FunctionNotFound(Atom), FunctionNotFound(Atom, usize),
StructNotFound(Atom), StructNotFound(Atom),
ArgCountMismatch, ArgCountMismatch {
name: Atom, // ffi function or struct
kind: ArgCountMismatchKind,
expected: usize,
got: usize,
},
AllocationFailed, AllocationFailed,
// LayoutError should never occour // LayoutError should never occour
LayoutError, LayoutError,
@@ -958,6 +987,12 @@ pub enum FfiError {
NullPtr, NullPtr,
} }
#[derive(Debug)]
pub(crate) enum ArgCountMismatchKind {
Function,
Struct,
}
impl std::fmt::Display for FfiError { impl std::fmt::Display for FfiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f) std::fmt::Debug::fmt(self, f)

View File

@@ -447,10 +447,26 @@ impl MachineState {
location: None, location: None,
} }
} }
ExistenceError::FfiFunction(atom) => { ExistenceError::FfiFunction(name, arity) => {
let culprit = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]);
let stub = functor!( let stub = functor!(
atom!("existence_error"), atom!("existence_error"),
[atom_as_cell((atom!("ffi_function"))), atom_as_cell(atom)] [atom_as_cell((atom!("ffi_function"))), functor(culprit)]
);
MachineError {
stub,
location: None,
}
}
ExistenceError::FfiStructConstructor(name, arity) => {
let culprit = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]);
let stub = functor!(
atom!("existence_error"),
[
atom_as_cell((atom!("ffi_struct_constructor"))),
functor(culprit)
]
); );
MachineError { MachineError {
@@ -688,13 +704,25 @@ impl MachineState {
} }
} }
FfiError::ValueOutOfRange(domain, culprit) => self.domain_error(domain, culprit), FfiError::ValueOutOfRange(domain, culprit) => self.domain_error(domain, culprit),
FfiError::FunctionNotFound(name) => { FfiError::FunctionNotFound(name, arity) => {
self.existence_error(ExistenceError::FfiFunction(name)) self.existence_error(ExistenceError::FfiFunction(name, arity))
} }
FfiError::StructNotFound(name) => { FfiError::StructNotFound(name) => {
self.existence_error(ExistenceError::FfiStructType(name)) self.existence_error(ExistenceError::FfiStructType(name))
} }
FfiError::ArgCountMismatch => self.unreachable_error(), FfiError::ArgCountMismatch {
name,
kind,
expected: _,
got,
} => match kind {
ffi::ArgCountMismatchKind::Function => {
self.existence_error(ExistenceError::FfiFunction(name, got))
}
ffi::ArgCountMismatchKind::Struct => {
self.existence_error(ExistenceError::FfiStructConstructor(name, got))
}
},
FfiError::AllocationFailed => MachineError { FfiError::AllocationFailed => MachineError {
stub: functor!(atom!("resource_error"), [atom_as_cell((atom!("heap")))]), stub: functor!(atom!("resource_error"), [atom_as_cell((atom!("heap")))]),
location: None, location: None,
@@ -1137,7 +1165,8 @@ pub enum ExistenceError {
SourceSink(HeapCellValue), SourceSink(HeapCellValue),
Stream(HeapCellValue), Stream(HeapCellValue),
Process(HeapCellValue), Process(HeapCellValue),
FfiFunction(Atom), FfiFunction(Atom, usize),
FfiStructConstructor(Atom, usize),
FfiStructType(Atom), FfiStructType(Atom),
} }

View File

@@ -0,0 +1,7 @@
:- use_module(library(ffi)).
test :- ffi:array_type(u8, 2, Type), ffi:allocate(rust, Type, [Type, 0], _ArrayPtr).
?- test.
error(existence_error(ffi_struct_constructor,'$[u8;2]'/1),'$ffi_allocate'/4).

View File

@@ -0,0 +1,5 @@
```trycmd
$ scryer-prolog -f --no-add-history input.pl -g test -g halt
test causes: error(existence_error(ffi_struct_constructor,$[u8;2]/1),$ffi_allocate/4)
```