1mod adapter;
2mod argument;
3mod return_;
4
5use crate::host::{HostProfile, HostProvider};
6use crate::plan::{FunctionType, TypeScheme};
7use ecow::EcoString;
8use std::collections::BTreeSet;
9use std::fmt;
10
11#[cfg(test)]
12pub(crate) use argument::CallArguments;
13pub(crate) use argument::{
14 HostBitArrayArgumentSlot, HostBoolArgumentSlot, HostCallArguments, HostCustomArgumentSlot,
15 HostExternalArgumentSlot, HostFloatArgumentSlot, HostFunctionArgumentSlot, HostIntArgumentSlot,
16 HostListArgumentSlot, HostNilArgumentSlot, HostParameter, HostStringArgumentSlot,
17 HostTupleArgumentSlot, HostUtfCodepointArgumentSlot, HostValueArgumentSlot,
18};
19pub(crate) use return_::HostNeverFunction;
20pub(crate) use return_::{HostFunctionImplementation, HostValueFunction};
21#[cfg(test)]
22pub(crate) use return_::{expect_never_implementation, expect_value_implementation};
23
24pub trait HostFunction<Arguments, Return>:
63 adapter::HostFunctionAdapter<Arguments, Return> + Send + Sync + 'static
64{
65}
66
67pub trait FallibleHostFunction<Arguments, Return>:
68 adapter::FallibleHostFunctionAdapter<Arguments, Return> + Send + Sync + 'static
69{
70}
71
72pub trait ScopedHostFunction<Profile, Provider, Arguments, Return>:
73 adapter::ScopedHostFunctionAdapter<Profile, Provider, Arguments, Return> + Send + Sync + 'static
74where
75 Profile: HostProfile,
76 Provider: HostProvider<Profile>,
77{
78}
79
80pub trait ScopedConstructingHostFunction<Profile, Provider, Arguments, Return, Constructions>:
85 adapter::ScopedConstructingHostFunctionAdapter<
86 Profile,
87 Provider,
88 Arguments,
89 Return,
90 Constructions,
91 > + Send
92 + Sync
93 + 'static
94where
95 Profile: HostProfile,
96 Provider: HostProvider<Profile>,
97 Constructions: crate::host::HostTypeSequence,
98{
99}
100
101pub trait ScopedDivergingHostFunction<Profile, Provider, Arguments, Return>:
102 adapter::ScopedDivergingHostFunctionAdapter<Profile, Provider, Arguments, Return>
103 + Send
104 + Sync
105 + 'static
106where
107 Profile: HostProfile,
108 Provider: HostProvider<Profile>,
109{
110}
111
112impl<Function, Arguments, Return> HostFunction<Arguments, Return> for Function where
113 Function: adapter::HostFunctionAdapter<Arguments, Return> + Send + Sync + 'static
114{
115}
116
117impl<Function, Arguments, Return> FallibleHostFunction<Arguments, Return> for Function where
118 Function: adapter::FallibleHostFunctionAdapter<Arguments, Return> + Send + Sync + 'static
119{
120}
121
122impl<Profile, Provider, Function, Arguments, Return>
123 ScopedHostFunction<Profile, Provider, Arguments, Return> for Function
124where
125 Profile: HostProfile,
126 Provider: HostProvider<Profile>,
127 Function: adapter::ScopedHostFunctionAdapter<Profile, Provider, Arguments, Return>
128 + Send
129 + Sync
130 + 'static,
131{
132}
133
134impl<Profile, Provider, Function, Arguments, Return, Constructions>
135 ScopedConstructingHostFunction<Profile, Provider, Arguments, Return, Constructions> for Function
136where
137 Profile: HostProfile,
138 Provider: HostProvider<Profile>,
139 Constructions: crate::host::HostTypeSequence,
140 Function: adapter::ScopedConstructingHostFunctionAdapter<
141 Profile,
142 Provider,
143 Arguments,
144 Return,
145 Constructions,
146 > + Send
147 + Sync
148 + 'static,
149{
150}
151
152impl<Profile, Provider, Function, Arguments, Return>
153 ScopedDivergingHostFunction<Profile, Provider, Arguments, Return> for Function
154where
155 Profile: HostProfile,
156 Provider: HostProvider<Profile>,
157 Function: adapter::ScopedDivergingHostFunctionAdapter<Profile, Provider, Arguments, Return>
158 + Send
159 + Sync
160 + 'static,
161{
162}
163
164#[derive(Clone, PartialEq, Eq)]
165pub struct HostFunctionSchema {
166 name: EcoString,
167 scheme: TypeScheme,
168 layout: Box<[HostParameter]>,
169 parameters: Box<[crate::host::HostTypeDescriptor]>,
170 return_: crate::host::HostTypeDescriptor,
171 custom_schemas: Box<[crate::host::HostCustomTypeSchema]>,
172 external_schemas: Box<[crate::host::HostExternalTypeSchema]>,
173 type_: FunctionType,
174}
175
176struct HostFunctionSchemaRegistration {
177 layout: Box<[HostParameter]>,
178 parameters: Box<[crate::host::HostTypeDescriptor]>,
179 return_: crate::host::HostTypeDescriptor,
180 custom_schemas: Box<[crate::host::HostCustomTypeSchema]>,
181}
182
183pub(crate) struct HostFunctionDefinition<Profile: HostProfile> {
184 schema: HostFunctionSchema,
185 constructions: RegisteredHostConstructions,
186 implementation: HostFunctionImplementation<Profile>,
187}
188
189pub(crate) struct RegisteredHostConstructions {
190 types: Box<[crate::host::HostTypeDescriptor]>,
191 custom_schemas: Box<[crate::host::HostCustomTypeSchema]>,
192 external_schemas: Box<[crate::host::HostExternalTypeSchema]>,
193}
194
195impl HostFunctionSchema {
196 pub fn name(&self) -> &EcoString {
197 &self.name
198 }
199
200 pub fn type_(&self) -> &FunctionType {
201 &self.type_
202 }
203
204 pub fn scheme(&self) -> &TypeScheme {
205 &self.scheme
206 }
207
208 pub(crate) fn parameters(&self) -> &[crate::host::HostTypeDescriptor] {
209 &self.parameters
210 }
211
212 pub(crate) fn layout(&self) -> &[HostParameter] {
213 &self.layout
214 }
215
216 pub(crate) fn return_type(&self) -> &crate::host::HostTypeDescriptor {
217 &self.return_
218 }
219
220 pub(crate) fn custom_schemas(&self) -> &[crate::host::HostCustomTypeSchema] {
221 &self.custom_schemas
222 }
223
224 pub(crate) fn external_schemas(&self) -> &[crate::host::HostExternalTypeSchema] {
225 &self.external_schemas
226 }
227
228 fn from_registration(
229 name: EcoString,
230 registration: HostFunctionSchemaRegistration,
231 ) -> Result<Self, crate::HostRegistrationError> {
232 let argument_types = registration
233 .parameters
234 .iter()
235 .map(crate::host::HostTypeDescriptor::value_type)
236 .collect();
237 let return_type = registration.return_.value_type();
238 let mut type_parameters = BTreeSet::new();
239 for parameter in ®istration.parameters {
240 parameter.collect_type_parameters(&mut type_parameters);
241 }
242 registration
243 .return_
244 .collect_type_parameters(&mut type_parameters);
245 let type_parameters = type_parameters.into_iter().collect::<Vec<_>>();
246 if type_parameters.iter().copied().ne(0..type_parameters.len()) {
247 return Err(crate::HostRegistrationError::NonContiguousTypeParameters {
248 function: name,
249 parameters: type_parameters.into_boxed_slice(),
250 });
251 }
252 let mut external_schemas = Vec::new();
253 let mut external_identities = std::collections::HashSet::new();
254 for parameter in ®istration.parameters {
255 parameter.collect_external_schemas(&mut external_schemas, &mut external_identities);
256 }
257 registration
258 .return_
259 .collect_external_schemas(&mut external_schemas, &mut external_identities);
260 Ok(Self {
261 name,
262 scheme: TypeScheme::new(type_parameters.len()),
263 layout: registration.layout,
264 parameters: registration.parameters,
265 return_: registration.return_,
266 custom_schemas: registration.custom_schemas,
267 external_schemas: external_schemas.into_boxed_slice(),
268 type_: FunctionType::new(argument_types, return_type),
269 })
270 }
271}
272
273impl RegisteredHostConstructions {
274 fn new(
275 types: Box<[crate::host::HostTypeDescriptor]>,
276 custom_schemas: Box<[crate::host::HostCustomTypeSchema]>,
277 ) -> Self {
278 let mut external_schemas = Vec::new();
279 let mut external_identities = std::collections::HashSet::new();
280 for type_ in &types {
281 type_.collect_external_schemas(&mut external_schemas, &mut external_identities);
282 }
283 Self {
284 types,
285 custom_schemas,
286 external_schemas: external_schemas.into_boxed_slice(),
287 }
288 }
289
290 pub(crate) fn empty() -> Self {
291 Self::new(Box::new([]), Box::new([]))
292 }
293
294 pub(crate) fn types(&self) -> &[crate::host::HostTypeDescriptor] {
295 &self.types
296 }
297
298 pub(crate) fn custom_schemas(&self) -> &[crate::host::HostCustomTypeSchema] {
299 &self.custom_schemas
300 }
301
302 pub(crate) fn external_schemas(&self) -> &[crate::host::HostExternalTypeSchema] {
303 &self.external_schemas
304 }
305
306 fn unbound_type_parameters(&self, parameter_count: usize) -> Box<[usize]> {
307 let mut parameters = BTreeSet::new();
308 for type_ in &self.types {
309 type_.collect_type_parameters(&mut parameters);
310 }
311 parameters
312 .into_iter()
313 .filter(|parameter| *parameter >= parameter_count)
314 .collect::<Vec<_>>()
315 .into_boxed_slice()
316 }
317}
318
319impl fmt::Debug for HostFunctionSchema {
320 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
321 let mut debug = formatter.debug_struct("HostFunctionSchema");
322 debug
323 .field("name", &self.name)
324 .field("scheme", &self.scheme)
325 .field("type_", &self.type_);
326 if !self.custom_schemas.is_empty() {
327 debug.field("custom_schemas", &self.custom_schemas);
328 }
329 if !self.external_schemas.is_empty() {
330 debug.field("external_schemas", &self.external_schemas);
331 }
332 debug.finish()
333 }
334}
335
336impl<Profile: HostProfile> HostFunctionDefinition<Profile> {
337 pub(crate) fn new<Arguments, Return, Function>(
338 name: EcoString,
339 function: Function,
340 ) -> Result<Self, crate::HostRegistrationError>
341 where
342 Function: HostFunction<Arguments, Return>,
343 {
344 let registration = <Function as adapter::HostFunctionAdapter<Arguments, Return>>::register::<
345 Profile,
346 >(function);
347 Self::from_registration(name, registration)
348 }
349
350 pub(crate) fn new_fallible<Arguments, Return, Function>(
351 name: EcoString,
352 function: Function,
353 ) -> Result<Self, crate::HostRegistrationError>
354 where
355 Function: FallibleHostFunction<Arguments, Return>,
356 {
357 let registration =
358 <Function as adapter::FallibleHostFunctionAdapter<Arguments, Return>>::register::<
359 Profile,
360 >(function);
361 Self::from_registration(name, registration)
362 }
363
364 pub(crate) fn new_scoped<Provider, Arguments, Return, Function>(
365 name: EcoString,
366 function: Function,
367 ) -> Result<Self, crate::HostRegistrationError>
368 where
369 Provider: HostProvider<Profile>,
370 Function: ScopedHostFunction<Profile, Provider, Arguments, Return>,
371 {
372 let registration = <Function as adapter::ScopedHostFunctionAdapter<
373 Profile,
374 Provider,
375 Arguments,
376 Return,
377 >>::register(function);
378 Self::from_registration(name, registration)
379 }
380
381 pub(crate) fn new_scoped_with_constructions<
382 Provider,
383 Arguments,
384 Return,
385 Constructions,
386 Function,
387 >(
388 name: EcoString,
389 function: Function,
390 ) -> Result<Self, crate::HostRegistrationError>
391 where
392 Provider: HostProvider<Profile>,
393 Constructions: crate::host::HostTypeSequence,
394 Function:
395 ScopedConstructingHostFunction<Profile, Provider, Arguments, Return, Constructions>,
396 {
397 let registration = <Function as adapter::ScopedConstructingHostFunctionAdapter<
398 Profile,
399 Provider,
400 Arguments,
401 Return,
402 Constructions,
403 >>::register(function);
404 let construction_types =
405 <Constructions as crate::host::HostAbiTypeSequence>::descriptors().into_boxed_slice();
406 let mut custom_schemas = Vec::new();
407 let mut visited = std::collections::HashSet::new();
408 <Constructions as crate::host::HostAbiTypeSequence>::collect_custom_schemas(
409 &mut custom_schemas,
410 &mut visited,
411 );
412 let constructions =
413 RegisteredHostConstructions::new(construction_types, custom_schemas.into_boxed_slice());
414 Self::from_registration_with_constructions(name, registration, constructions)
415 }
416
417 pub(crate) fn new_scoped_diverging<Provider, Arguments, Return, Function>(
418 name: EcoString,
419 function: Function,
420 ) -> Result<Self, crate::HostRegistrationError>
421 where
422 Provider: HostProvider<Profile>,
423 Function: ScopedDivergingHostFunction<Profile, Provider, Arguments, Return>,
424 {
425 let registration = <Function as adapter::ScopedDivergingHostFunctionAdapter<
426 Profile,
427 Provider,
428 Arguments,
429 Return,
430 >>::register(function);
431 Self::from_registration(name, registration)
432 }
433
434 fn from_registration(
435 name: EcoString,
436 registration: adapter::HostFunctionRegistration<Profile>,
437 ) -> Result<Self, crate::HostRegistrationError> {
438 Self::from_registration_with_constructions(
439 name,
440 registration,
441 RegisteredHostConstructions::empty(),
442 )
443 }
444
445 fn from_registration_with_constructions(
446 name: EcoString,
447 registration: adapter::HostFunctionRegistration<Profile>,
448 constructions: RegisteredHostConstructions,
449 ) -> Result<Self, crate::HostRegistrationError> {
450 let schema = HostFunctionSchemaRegistration {
451 layout: registration.parameters,
452 parameters: registration.parameter_types,
453 return_: registration.return_type,
454 custom_schemas: registration.custom_schemas,
455 };
456 let schema = HostFunctionSchema::from_registration(name, schema)?;
457 let unbound = constructions.unbound_type_parameters(schema.scheme().parameters().len());
458 if !unbound.is_empty() {
459 return Err(
460 crate::HostRegistrationError::UnboundConstructionTypeParameters {
461 function: schema.name().clone(),
462 parameters: unbound,
463 },
464 );
465 }
466 Ok(Self {
467 schema,
468 constructions,
469 implementation: registration.implementation,
470 })
471 }
472
473 pub(crate) fn schema(&self) -> &HostFunctionSchema {
474 &self.schema
475 }
476
477 pub(crate) fn into_parts(
478 self,
479 ) -> (
480 HostFunctionSchema,
481 RegisteredHostConstructions,
482 HostFunctionImplementation<Profile>,
483 ) {
484 (self.schema, self.constructions, self.implementation)
485 }
486}
487
488#[cfg(test)]
489mod tests {
490 use super::{HostFunctionDefinition, HostFunctionSchema, RegisteredHostConstructions};
491 use crate::BitArrayValue;
492 use crate::host::function::argument::CallArguments;
493 use crate::host::test::{TestHostCallRuntime, TestHostProfile, TestRunState};
494 use crate::host::{
495 HostCall, HostCallCompletion, HostCallError, HostCustomConstructorSchema,
496 HostCustomFieldSchema, HostCustomTypeSchema, HostExternalTypeSchema, HostListType,
497 HostProvider, HostRegistrationError, HostSchemaType, HostScopedValue, HostTypeDescriptor,
498 HostTypeIndex0, HostTypeList, HostTypeListEnd, HostValueFamily,
499 expect_value_implementation,
500 };
501 use crate::plan::ValueType;
502 use ecow::EcoString;
503 use num_bigint::BigInt;
504
505 struct ConstructionProvider;
506
507 impl HostProvider<TestHostProfile> for ConstructionProvider {
508 type State = usize;
509
510 fn project(state: &mut TestRunState) -> &mut Self::State {
511 &mut state.counter
512 }
513 }
514
515 fn ready<'call>(
516 call: HostCall<'call, TestHostProfile, ConstructionProvider, bool>,
517 ) -> Result<HostCallCompletion<'call, bool>, HostCallError> {
518 Ok(call.return_value(true))
519 }
520
521 type ConstructionTypes = HostTypeList<HostListType<BigInt>, HostTypeListEnd>;
522
523 fn ready_with_constructions<'call>(
524 call: HostCall<'call, TestHostProfile, ConstructionProvider, bool>,
525 constructions: crate::HostConstructions<'call, ConstructionTypes>,
526 ) -> Result<HostCallCompletion<'call, bool>, HostCallError> {
527 let _ = constructions.at::<HostTypeIndex0>();
528 Ok(call.return_value(true))
529 }
530
531 #[test]
532 fn definition_assembles_schema_and_int_implementation_together() {
533 let definition = HostFunctionDefinition::new(
534 "choose".into(),
535 |condition: bool, left: BigInt, right: BigInt| {
536 if condition { left } else { right }
537 },
538 )
539 .expect("contiguous scalar function should register");
540
541 assert_eq!(definition.schema().name(), "choose");
542 assert_eq!(
543 definition.schema().type_().argument_types(),
544 [ValueType::Bool, ValueType::Int, ValueType::Int],
545 );
546 assert_eq!(definition.schema().type_().return_(), &ValueType::Int);
547 assert_eq!(definition.schema().return_type(), &HostTypeDescriptor::Int);
548
549 let (_, _, implementation) = definition.into_parts();
550 let implementation = expect_value_implementation(&implementation);
551 let mut state = TestRunState::default();
552 let arguments = CallArguments::new(vec![10.into(), 20.into()], vec![false]);
553 let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
554 assert_eq!(
555 implementation.call(&mut runtime).map(|token| token.family),
556 Ok(HostValueFamily::Int),
557 );
558 assert_eq!(
559 runtime.completed(),
560 Some(&HostScopedValue::Int(BigInt::from(20))),
561 );
562 let arguments = CallArguments::new(vec![10.into(), 20.into()], vec![true]);
563 let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
564 assert_eq!(
565 implementation.call(&mut runtime).map(|token| token.family),
566 Ok(HostValueFamily::Int),
567 );
568 assert_eq!(
569 runtime.completed(),
570 Some(&HostScopedValue::Int(BigInt::from(10))),
571 );
572 }
573
574 #[test]
575 fn definition_assembles_schema_and_bool_implementation_together() {
576 let definition =
577 HostFunctionDefinition::new("is_positive".into(), |value: BigInt| value > 0.into())
578 .expect("monomorphic function should register");
579
580 assert_eq!(definition.schema().name(), "is_positive");
581 assert_eq!(
582 definition.schema().type_().argument_types(),
583 [ValueType::Int],
584 );
585 assert_eq!(definition.schema().type_().return_(), &ValueType::Bool);
586 assert_eq!(definition.schema().return_type(), &HostTypeDescriptor::Bool);
587
588 let (_, _, implementation) = definition.into_parts();
589 let implementation = expect_value_implementation(&implementation);
590 let mut state = TestRunState::default();
591 let arguments = CallArguments::new(vec![1.into()], Vec::new());
592 let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
593 assert_eq!(
594 implementation.call(&mut runtime).map(|token| token.family),
595 Ok(HostValueFamily::Bool),
596 );
597 assert_eq!(runtime.completed(), Some(&HostScopedValue::Bool(true)));
598 }
599
600 #[test]
601 fn definition_assembles_every_scalar_parameter_from_one_layout() {
602 let definition: HostFunctionDefinition<TestHostProfile> = HostFunctionDefinition::new(
603 "consume".into(),
604 |_: BigInt, _: f64, _: EcoString, _: BitArrayValue, _: char, _: bool, (): ()| (),
605 )
606 .expect("monomorphic scalar function should register");
607
608 assert_eq!(
609 definition.schema().type_().argument_types(),
610 [
611 ValueType::Int,
612 ValueType::Float,
613 ValueType::String,
614 ValueType::BitArray,
615 ValueType::UtfCodepoint,
616 ValueType::Bool,
617 ValueType::Nil,
618 ],
619 );
620 assert_eq!(definition.schema().type_().return_(), &ValueType::Nil);
621 assert_eq!(definition.schema().return_type(), &HostTypeDescriptor::Nil);
622
623 let (_, _, implementation) = definition.into_parts();
624 let implementation = expect_value_implementation(&implementation);
625 let arguments = CallArguments::new(vec![1.into()], vec![true]).with_scalar_values(
626 vec![1.5],
627 vec!["one".into()],
628 vec![BitArrayValue::from_bytes(vec![1])],
629 vec!['A'],
630 1,
631 );
632 let mut state = TestRunState::default();
633 let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
634 assert_eq!(
635 implementation.call(&mut runtime).map(|token| token.family),
636 Ok(HostValueFamily::Nil),
637 );
638 assert_eq!(runtime.completed(), Some(&HostScopedValue::Nil));
639 }
640
641 #[test]
642 fn schema_clone_contains_only_the_registered_signature() {
643 let definition: HostFunctionDefinition<TestHostProfile> =
644 HostFunctionDefinition::new("negate".into(), <bool as std::ops::Not>::not)
645 .expect("monomorphic function should register");
646 let schema = definition.schema().clone();
647
648 assert_eq!(schema, *definition.schema());
649 assert_eq!(schema.name(), "negate");
650 assert_eq!(schema.type_().argument_types(), [ValueType::Bool],);
651 assert_eq!(schema.type_().return_(), &ValueType::Bool);
652 assert_eq!(
653 format!("{schema:?}"),
654 r#"HostFunctionSchema { name: "negate", scheme: TypeScheme { parameters: [] }, type_: FunctionType { arguments: [Bool], return_: Bool } }"#,
655 );
656 }
657
658 #[test]
659 fn hidden_construction_types_stay_outside_the_public_function_schema() {
660 let plain = HostFunctionDefinition::new_scoped::<ConstructionProvider, (), bool, _>(
661 "ready".into(),
662 ready,
663 )
664 .expect("plain scoped function should register");
665 let with_constructions = HostFunctionDefinition::new_scoped_with_constructions::<
666 ConstructionProvider,
667 (),
668 bool,
669 ConstructionTypes,
670 _,
671 >("ready".into(), ready_with_constructions)
672 .expect("scoped function with hidden constructions should register");
673 let (schema, constructions, constructing_implementation) = with_constructions.into_parts();
674
675 assert_eq!(schema, *plain.schema());
676 assert_eq!(schema.scheme(), &crate::plan::TypeScheme::new(0));
677 assert_eq!(schema.type_(), plain.schema().type_());
678 assert_eq!(
679 constructions.types(),
680 [HostTypeDescriptor::List(Box::new(HostTypeDescriptor::Int))],
681 );
682 assert!(constructions.custom_schemas().is_empty());
683 assert!(constructions.external_schemas().is_empty());
684
685 let (_, _, plain_implementation) = plain.into_parts();
686 for implementation in [&plain_implementation, &constructing_implementation] {
687 let implementation = expect_value_implementation(implementation);
688 let mut state = TestRunState::default();
689 assert!(std::ptr::eq(
690 ConstructionProvider::project(&mut state),
691 &state.counter,
692 ));
693 let arguments = CallArguments::new(Vec::new(), Vec::new());
694 let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
695 assert_eq!(
696 implementation.call(&mut runtime).map(|token| token.family),
697 Ok(HostValueFamily::Bool),
698 );
699 assert_eq!(runtime.completed(), Some(&HostScopedValue::Bool(true)));
700 }
701 }
702
703 #[test]
704 fn registered_constructions_report_parameters_outside_the_function_scheme() {
705 let constructions = RegisteredHostConstructions::new(
706 vec![
707 HostTypeDescriptor::List(Box::new(HostTypeDescriptor::Parameter(0))),
708 HostTypeDescriptor::Parameter(2),
709 HostTypeDescriptor::Parameter(2),
710 ]
711 .into_boxed_slice(),
712 Box::new([]),
713 );
714
715 assert_eq!(
716 constructions.unbound_type_parameters(0),
717 vec![0, 2].into_boxed_slice(),
718 );
719 assert_eq!(
720 constructions.unbound_type_parameters(1),
721 vec![2].into_boxed_slice(),
722 );
723 assert_eq!(
724 constructions.unbound_type_parameters(3),
725 Vec::<usize>::new().into_boxed_slice(),
726 );
727 }
728
729 #[test]
730 fn schema_debug_includes_custom_definitions_not_derived_from_the_function_type() {
731 let custom_schema = HostCustomTypeSchema::new(
732 "host_shapes",
733 "host/shape",
734 "Shape",
735 0,
736 [HostCustomConstructorSchema::new(
737 "Circle",
738 [HostCustomFieldSchema::new(
739 Some("radius"),
740 HostSchemaType::Float,
741 )],
742 )],
743 );
744 let return_ = HostTypeDescriptor::Custom {
745 schema: custom_schema.clone(),
746 arguments: Box::new([]),
747 };
748 let schema = HostFunctionSchema {
749 name: "origin".into(),
750 scheme: crate::plan::TypeScheme::new(0),
751 layout: Box::new([]),
752 parameters: Box::new([]),
753 type_: crate::plan::FunctionType::new(Vec::new(), return_.value_type()),
754 return_,
755 custom_schemas: vec![custom_schema].into_boxed_slice(),
756 external_schemas: Box::new([]),
757 };
758
759 assert_eq!(
760 format!("{schema:?}"),
761 r#"HostFunctionSchema { name: "origin", scheme: TypeScheme { parameters: [] }, type_: FunctionType { arguments: [], return_: Custom(CustomType { name: CustomTypeName { package: "host_shapes", module: "host/shape", name: "Shape" }, arguments: [] }) }, custom_schemas: [HostCustomTypeSchema { package: "host_shapes", module: "host/shape", name: "Shape", parameter_count: 0, constructors: [HostCustomConstructorSchema { name: "Circle", fields: [HostCustomFieldSchema { label: Some("radius"), type_: Float }] }] }] }"#,
762 );
763 }
764
765 #[test]
766 fn schema_debug_includes_external_definitions_not_derived_from_the_function_type() {
767 let external_schema =
768 HostExternalTypeSchema::new("host_shapes", "host/resource", "Resource", 1);
769 let return_ = HostTypeDescriptor::External {
770 schema: external_schema.clone(),
771 arguments: vec![HostTypeDescriptor::Parameter(0)].into_boxed_slice(),
772 };
773 let schema = HostFunctionSchema {
774 name: "resource".into(),
775 scheme: crate::plan::TypeScheme::new(1),
776 layout: Box::new([]),
777 parameters: Box::new([]),
778 type_: crate::plan::FunctionType::new(Vec::new(), return_.value_type()),
779 return_,
780 custom_schemas: Box::new([]),
781 external_schemas: vec![external_schema].into_boxed_slice(),
782 };
783
784 assert_eq!(
785 format!("{schema:?}"),
786 r#"HostFunctionSchema { name: "resource", scheme: TypeScheme { parameters: [TypeParameterId(0)] }, type_: FunctionType { arguments: [], return_: External(ExternalType { name: ExternalTypeName { package: "host_shapes", module: "host/resource", name: "Resource" }, arguments: [Parameter(TypeParameterId(0))] }) }, external_schemas: [HostExternalTypeSchema { package: "host_shapes", module: "host/resource", name: "Resource", parameter_count: 1 }] }"#,
787 );
788 }
789
790 #[test]
791 fn definition_rejects_non_contiguous_type_parameter_indices_before_allocating_a_scheme() {
792 let mut registration = <_ as super::adapter::HostFunctionAdapter<(), bool>>::register::<
793 TestHostProfile,
794 >(|| true);
795 registration.return_type = HostTypeDescriptor::Parameter(2);
796 let error = HostFunctionDefinition::from_registration("identity".into(), registration)
797 .err()
798 .expect("sparse type parameters should be rejected");
799
800 assert_eq!(
801 error,
802 HostRegistrationError::NonContiguousTypeParameters {
803 function: "identity".into(),
804 parameters: vec![2].into_boxed_slice(),
805 },
806 );
807 assert_eq!(
808 error.to_string(),
809 "host function identity uses type parameter indices [2]; indices must be contiguous from zero",
810 );
811 }
812}