fixed infinite recursion in trait bounds for Visit and Reflect procmacro

- this bug was rare, but very annoying
This commit is contained in:
Dmitry Stepanov
2026-01-19 22:06:39 +03:00
parent 5e345b308c
commit 739b8abb70
19 changed files with 67 additions and 305 deletions
+4 -4
View File
@@ -41,9 +41,9 @@ use strum_macros::{AsRefStr, EnumString, VariantNames};
pub mod signal;
/// Trait for anything that can be used as a texture.
pub trait SpriteSheetTexture: PartialEq + Clone + Visit + Reflect {}
pub trait SpriteSheetTexture: PartialEq + Clone + Visit + Reflect + Default {}
impl<T: PartialEq + Clone + Visit + Reflect> SpriteSheetTexture for T {}
impl<T: PartialEq + Clone + Visit + Reflect + Default> SpriteSheetTexture for T {}
/// Animation playback status.
#[derive(
@@ -292,7 +292,7 @@ where
/// # };
/// # use fyrox_core::{reflect::prelude::*, visitor::prelude::*};
/// #
/// #[derive(PartialEq, Clone, Reflect, Visit, Debug)]
/// #[derive(PartialEq, Clone, Reflect, Visit, Debug, Default)]
/// struct MyTexture {}
///
/// fn extract_animations() {
@@ -595,7 +595,7 @@ mod test {
};
use fyrox_core::{algebra::Vector2, math::Rect, reflect::prelude::*, visitor::prelude::*};
#[derive(PartialEq, Clone, Reflect, Visit, Debug)]
#[derive(PartialEq, Clone, Reflect, Visit, Debug, Default)]
struct MyTexture {}
#[test]
+1 -1
View File
@@ -41,7 +41,7 @@ use syn::{parse_macro_input, DeriveInput};
/// ```
/// use fyrox_core::visitor::{Visit, VisitResult, Visitor};
/// #[derive(Visit)]
/// struct Foo<T> {
/// struct Foo<T: Visit> {
/// example_one: String,
/// example_two: T,
/// }
-13
View File
@@ -92,19 +92,6 @@ impl TypeArgs {
return generics;
}
// Add where clause for every reflectable field
let fields: Box<dyn Iterator<Item = &FieldArgs>> = match &self.data {
ast::Data::Struct(data) => Box::new(data.fields.iter()),
ast::Data::Enum(variants) => Box::new(variants.iter().flat_map(|v| v.fields.iter())),
};
clause.predicates.extend(
fields
.filter(|f| !(f.hidden || f.deref || f.field.is_some()))
.map(|f| &f.ty)
.map::<WherePredicate, _>(|ty| parse_quote! { #ty: Reflect }),
);
generics
}
+1 -2
View File
@@ -83,7 +83,7 @@ fn impl_visit_struct(
}
};
utils::create_impl(ty_args, field_args.iter().cloned(), visit_fn_body)
utils::create_impl(ty_args, visit_fn_body)
}
/// impl `Visit` for `enum`
@@ -235,7 +235,6 @@ fn impl_visit_enum(ty_args: &args::TypeArgs, variant_args: &[args::VariantArgs])
utils::create_impl(
ty_args,
variant_args.iter().flat_map(|v| v.fields.iter()).cloned(),
quote! {
let mut region = match visitor.enter_region(name) {
Ok(x) => x,
+1 -4
View File
@@ -40,10 +40,7 @@ pub struct TypeArgs {
#[darling(attributes(visit))]
pub struct FieldArgs {
pub ident: Option<Ident>,
// pub vis: Visibility,
pub ty: Type,
// pub attrs: Vec<Attribute>,
// ---
/// `#[visit(skip)]`
///
/// Skip on read and write
+2 -24
View File
@@ -28,14 +28,9 @@ use convert_case::*;
use crate::visit::args;
pub fn create_impl(
ty_args: &args::TypeArgs,
field_args: impl Iterator<Item = args::FieldArgs>,
impl_body: TokenStream2,
) -> TokenStream2 {
pub fn create_impl(ty_args: &args::TypeArgs, impl_body: TokenStream2) -> TokenStream2 {
let ty_ident = &ty_args.ident;
let generics = self::create_impl_generics(&ty_args.generics, field_args);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let (impl_generics, ty_generics, where_clause) = ty_args.generics.split_for_impl();
quote! {
#[allow(clippy::question_mark)]
@@ -51,23 +46,6 @@ pub fn create_impl(
}
}
fn create_impl_generics(
generics: &Generics,
field_args: impl Iterator<Item = args::FieldArgs>,
) -> Generics {
let mut generics = generics.clone();
// Add where clause for every visited field
generics.make_where_clause().predicates.extend(
field_args
.filter(|f| !f.skip)
.map(|f| f.ty)
.map::<WherePredicate, _>(|ty| parse_quote! { #ty: Visit }),
);
generics
}
/// `<prefix>field.visit("name", visitor)?;`
pub fn create_field_visits<'a>(
// false if enum variant
+2 -23
View File
@@ -45,7 +45,7 @@ struct SkipAttr {
}
#[derive(Debug, Clone, PartialEq, Visit)]
struct Generics<T> {
struct Generics<T: Visit + Default + 'static> {
items: Vec<T>,
}
@@ -75,7 +75,7 @@ enum ComplexEnum {
#[derive(Debug, Clone, PartialEq, Visit)]
enum GenericEnum<T>
where
T: std::fmt::Debug,
T: std::fmt::Debug + Default + Visit + 'static,
{
UnitVariant,
Tuple(i32, Vec<T>),
@@ -141,17 +141,6 @@ fn skip_attr() {
#[test]
fn generics() {
struct NotVisit;
#[allow(warnings)]
let mut not_compile = Generics {
items: vec![NotVisit],
};
// Compile error because `Generics<NotVisit> is not `Visit`:
// let mut visitor = Visitor::new();
// not_compile.visit("Data", &mut visitor).unwrap();
// But `Generics<T: Visit` is `Visit`
let mut data = Generics {
items: vec![100u32],
@@ -198,16 +187,6 @@ fn complex_enum() {
#[test]
fn generic_enum() {
#[derive(Debug)]
struct NotVisit;
#[allow(warnings)]
let mut not_compile = GenericEnum::Tuple(1, vec![NotVisit]);
// Compile error because `Generics<NotVisit> is not `Visit`:
// let mut visitor = Visitor::new();
// not_compile.visit("Data", &mut visitor).unwrap();
let mut data = GenericEnum::Tuple(1, vec![100u32]);
let mut data_default = GenericEnum::UnitVariant;
-1
View File
@@ -26,7 +26,6 @@ pub use fyrox_math::*;
use crate::math::curve::Curve;
use crate::math::curve::CurveKey;
use crate::math::curve::CurveKeyKind;
use crate::Uuid;
use crate::{
algebra::Scalar,
math::{aabb::AxisAlignedBoundingBox, frustum::Frustum, plane::Plane},
+3 -3
View File
@@ -27,14 +27,14 @@ use std::fmt::Debug;
use crate::reflect::prelude::*;
impl_reflect! {
pub struct Matrix<T: Copy + 'static, R: Dim + 'static, C: Dim + 'static, S: Copy + 'static> {
pub struct Matrix<T: Copy + 'static, R: Dim + 'static, C: Dim + 'static, S: Copy + Debug + FieldValue + 'static> {
pub data: S,
// _phantoms: PhantomData<(T, R, C)>,
}
}
impl_reflect! {
pub struct ArrayStorage<T: Copy + Debug, const R: usize, const C: usize>(pub [[T; R]; C]);
pub struct ArrayStorage<T: Copy + Debug + Reflect, const R: usize, const C: usize>(pub [[T; R]; C]);
}
impl_reflect! {
@@ -44,7 +44,7 @@ impl_reflect! {
}
impl_reflect! {
pub struct Quaternion<T: Copy + Debug> {
pub struct Quaternion<T: Copy + Debug + Reflect> {
pub coords: Vector4<T>,
}
}
+2 -2
View File
@@ -282,14 +282,14 @@ impl_reflect! {
}
impl_reflect! {
pub enum Option<T: Clone> {
pub enum Option<T: Clone + Debug + Reflect> {
Some(T),
None
}
}
impl_reflect! {
pub struct Range<Idx: Clone> {
pub struct Range<Idx: Clone + Debug + Reflect> {
pub start: Idx,
pub end: Idx,
}
+2 -195
View File
@@ -45,14 +45,11 @@ use crate::{
},
};
use fyrox_graph::constructor::ConstructorProvider;
use std::{
any::{type_name, Any, TypeId},
ops::{Deref, DerefMut},
};
use std::ops::{Deref, DerefMut};
/// A part of ragdoll, that has a physical rigid body, a bone and zero or more children limbs.
/// Multiple limbs together forms a ragdoll.
#[derive(Clone, Debug, PartialEq, Default)]
#[derive(Clone, Debug, PartialEq, Default, Visit, Reflect)]
pub struct Limb {
/// A handle of a scene node, that is used as a bone in some other scene node (mesh).
pub bone: Handle<Node>,
@@ -64,196 +61,6 @@ pub struct Limb {
uuid_provider!(Limb = "6d5bc2f7-8acc-4b64-8e4b-65d4551150bf");
// Rust has a compiler bug `overflow evaluating the requirement` that prevents deriving this impl.
impl Reflect for Limb {
fn source_path() -> &'static str {
file!()
}
fn derived_types() -> &'static [TypeId] {
&[]
}
fn query_derived_types(&self) -> &'static [TypeId] {
Self::derived_types()
}
fn type_name(&self) -> &'static str {
type_name::<Self>()
}
fn doc(&self) -> &'static str {
""
}
fn assembly_name(&self) -> &'static str {
env!("CARGO_PKG_NAME")
}
fn type_assembly_name() -> &'static str {
env!("CARGO_PKG_NAME")
}
fn fields_ref(&self, func: &mut dyn FnMut(&[FieldRef])) {
func(&[
{
static METADATA: FieldMetadata = FieldMetadata {
name: "Bone",
display_name: "Bone",
tag: "",
read_only: false,
immutable_collection: false,
min_value: None,
max_value: None,
step: None,
precision: None,
doc: "",
};
FieldRef {
metadata: &METADATA,
value: &self.bone,
}
},
{
static METADATA: FieldMetadata = FieldMetadata {
name: "PhysicalBone",
display_name: "Physical Bone",
tag: "",
read_only: false,
immutable_collection: false,
min_value: None,
max_value: None,
step: None,
precision: None,
doc: "",
};
FieldRef {
metadata: &METADATA,
value: &self.physical_bone,
}
},
{
static METADATA: FieldMetadata = FieldMetadata {
name: "Children",
display_name: "Children",
tag: "",
read_only: false,
immutable_collection: false,
min_value: None,
max_value: None,
step: None,
precision: None,
doc: "",
};
FieldRef {
metadata: &METADATA,
value: &self.children,
}
},
])
}
fn fields_mut(&mut self, func: &mut dyn FnMut(&mut [FieldMut])) {
func(&mut [
{
static METADATA: FieldMetadata = FieldMetadata {
name: "Bone",
display_name: "Bone",
tag: "",
read_only: false,
immutable_collection: false,
min_value: None,
max_value: None,
step: None,
precision: None,
doc: "",
};
FieldMut {
metadata: &METADATA,
value: &mut self.bone,
}
},
{
static METADATA: FieldMetadata = FieldMetadata {
name: "PhysicalBone",
display_name: "Physical Bone",
tag: "",
read_only: false,
immutable_collection: false,
min_value: None,
max_value: None,
step: None,
precision: None,
doc: "",
};
FieldMut {
metadata: &METADATA,
value: &mut self.physical_bone,
}
},
{
static METADATA: FieldMetadata = FieldMetadata {
name: "Children",
display_name: "Children",
tag: "",
read_only: false,
immutable_collection: false,
min_value: None,
max_value: None,
step: None,
precision: None,
doc: "",
};
FieldMut {
metadata: &METADATA,
value: &mut self.children,
}
},
])
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn as_any(&self, func: &mut dyn FnMut(&dyn Any)) {
func(self)
}
fn as_any_mut(&mut self, func: &mut dyn FnMut(&mut dyn Any)) {
func(self)
}
fn as_reflect(&self, func: &mut dyn FnMut(&dyn Reflect)) {
func(self)
}
fn as_reflect_mut(&mut self, func: &mut dyn FnMut(&mut dyn Reflect)) {
func(self)
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
let this = std::mem::replace(self, value.take()?);
Ok(Box::new(this))
}
fn try_clone_box(&self) -> Option<Box<dyn Reflect>> {
Some(Box::new(self.clone()))
}
}
impl Visit for Limb {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut guard = visitor.enter_region(name)?;
self.bone.visit("Bone", &mut guard)?;
self.physical_bone.visit("PhysicalBone", &mut guard)?;
self.children.visit("Children", &mut guard)?;
Ok(())
}
}
impl Limb {
/// Iterates recursively across the entire tree of descendant limbs and does the specified action
/// with every limb along the way.
+7 -1
View File
@@ -402,7 +402,7 @@ pub enum PageType {
/// Both pages and tiles have a TileDefinitionHandle and are rendered using
/// [`TileRenderData`]. For pages this is due to having an icon to allow the user to select the page.
/// Both pages and tiles can be selected by the user, moved, and deleted.
#[derive(Clone, Copy, Debug, PartialEq, Reflect)]
#[derive(Clone, Copy, Debug, PartialEq, Visit, Reflect)]
pub enum ResourceTilePosition {
/// This position refers to some page, and so it lacks tile coordinates.
Page(Vector2<i32>),
@@ -411,6 +411,12 @@ pub enum ResourceTilePosition {
Tile(Vector2<i32>, Vector2<i32>),
}
impl Default for ResourceTilePosition {
fn default() -> Self {
Self::Page(Default::default())
}
}
impl Display for ResourceTilePosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
+5 -5
View File
@@ -58,22 +58,22 @@ fn position_to_vector(source: PalettePosition) -> Vector2<i32> {
/// A 2D grid that contains tile data.
#[derive(Default, Debug, Clone, PartialEq, Reflect)]
pub struct TileGridMap<V: Debug + Clone>(FxHashMap<Vector2<i32>, V>);
pub struct TileGridMap<V: Debug + Clone + Reflect>(FxHashMap<Vector2<i32>, V>);
impl<V: Visit + Default + Debug + Clone> Visit for TileGridMap<V> {
impl<V: Visit + Default + Debug + Clone + Reflect> Visit for TileGridMap<V> {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.0.visit(name, visitor)
}
}
impl<V: Debug + Clone> Deref for TileGridMap<V> {
impl<V: Debug + Clone + Reflect> Deref for TileGridMap<V> {
type Target = FxHashMap<Vector2<i32>, V>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<V: Debug + Clone> DerefMut for TileGridMap<V> {
impl<V: Debug + Clone + Reflect> DerefMut for TileGridMap<V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
@@ -382,7 +382,7 @@ pub struct Stamp {
/// Each cell of a stamp must have a tile handle and it may optionally have
/// the handle of a brush cell where the tile was taken from.
#[derive(Clone, Debug, Reflect)]
#[derive(Clone, Debug, Reflect, Visit, Default)]
pub struct StampElement {
/// The stamp cell's tile handle
pub handle: TileDefinitionHandle,
+4 -4
View File
@@ -54,7 +54,7 @@ pub struct OrthoTransformation(i8);
/// A map from `Vector2<i32>` to values. It can be transformed to flip and rotate the positions of the values.
#[derive(Clone, Debug, Visit)]
pub struct OrthoTransformMap<V> {
pub struct OrthoTransformMap<V: Visit + Default> {
transform: OrthoTransformation,
map: FxHashMap<Vector2<i32>, V>,
}
@@ -249,7 +249,7 @@ impl<V: Number + SimdPartialOrd + Add + AddAssign + Neg<Output = V> + Scalar> Or
}
}
impl<V> OrthoTransformMap<V> {
impl<V: Visit + Default> OrthoTransformMap<V> {
/// Bounding rectangle the contains the keys.
pub fn bounding_rect(&self) -> OptionTileRect {
let mut result = OptionTileRect::default();
@@ -316,7 +316,7 @@ impl<V> OrthoTransformMap<V> {
}
}
impl<V> Default for OrthoTransformMap<V> {
impl<V: Visit + Default> Default for OrthoTransformMap<V> {
fn default() -> Self {
Self {
transform: Default::default(),
@@ -325,7 +325,7 @@ impl<V> Default for OrthoTransformMap<V> {
}
}
impl<V> OrthoTransform for OrthoTransformMap<V> {
impl<V: Visit + Default> OrthoTransform for OrthoTransformMap<V> {
fn x_flipped(self) -> Self {
Self {
transform: self.transform.x_flipped(),
+4 -1
View File
@@ -75,7 +75,10 @@ impl VertexData {
/// A trait, that describes and arbitrary vertex that could be used in a graph. It allows you to
/// use your structure to store additional info in the graph.
pub trait VertexDataProvider: Deref<Target = VertexData> + DerefMut + PositionProvider {}
pub trait VertexDataProvider:
Deref<Target = VertexData> + DerefMut + PositionProvider + Default + Visit + 'static
{
}
/// A default graph vertex with no additional data.
#[derive(Default, PartialEq, Debug)]
+4 -3
View File
@@ -25,6 +25,7 @@
//! is returned from any descendant node. In other worlds `Selector` implement OR logical
//! function.
use crate::utils::behavior::BaseBehavior;
use crate::{
core::{pool::Handle, visitor::prelude::*},
utils::behavior::{BehaviorNode, BehaviorTree},
@@ -48,7 +49,7 @@ pub enum CompositeNodeKind {
#[derive(Debug, PartialEq, Visit, Eq, Clone)]
pub struct CompositeNode<B>
where
B: Clone,
B: BaseBehavior,
{
/// A set of children.
pub children: Vec<Handle<BehaviorNode<B>>>,
@@ -58,7 +59,7 @@ where
impl<B> Default for CompositeNode<B>
where
B: Clone,
B: BaseBehavior,
{
fn default() -> Self {
Self {
@@ -70,7 +71,7 @@ where
impl<B> CompositeNode<B>
where
B: Clone + 'static,
B: BaseBehavior,
{
/// Creates new composite node of given kind and set of children nodes.
pub fn new(kind: CompositeNodeKind, children: Vec<Handle<BehaviorNode<B>>>) -> Self {
+4 -3
View File
@@ -21,6 +21,7 @@
//! A node, that inverts its child state ([`super::Status::Failure`] becomes [`super::Status::Success`] and vice versa, [`super::Status::Running`] remains
//! unchanged)
use crate::utils::behavior::BaseBehavior;
use crate::{
core::{pool::Handle, visitor::prelude::*},
utils::behavior::{BehaviorNode, BehaviorTree},
@@ -30,7 +31,7 @@ use crate::{
#[derive(Debug, PartialEq, Visit, Eq, Clone)]
pub struct Inverter<B>
where
B: Clone,
B: BaseBehavior,
{
/// A handle of child node, the state of which will be inverted.
pub child: Handle<BehaviorNode<B>>,
@@ -38,7 +39,7 @@ where
impl<B> Default for Inverter<B>
where
B: Clone,
B: BaseBehavior,
{
fn default() -> Self {
Self {
@@ -49,7 +50,7 @@ where
impl<B> Inverter<B>
where
B: Clone + 'static,
B: BaseBehavior,
{
/// Creates new inverter node with given action.
pub fn new(child: Handle<BehaviorNode<B>>) -> Self {
+4 -3
View File
@@ -21,6 +21,7 @@
//! Leaf is a "final" node of a behavior tree. It contains user-defined action which
//! is able to mutate given context.
use crate::utils::behavior::BaseBehavior;
use crate::{
core::{pool::Handle, visitor::prelude::*},
utils::behavior::{BehaviorNode, BehaviorTree},
@@ -31,7 +32,7 @@ use std::cell::RefCell;
#[derive(Debug, PartialEq, Visit, Eq, Clone)]
pub struct LeafNode<B>
where
B: Clone,
B: BaseBehavior,
{
/// User-defined behavior.
pub behavior: Option<RefCell<B>>,
@@ -39,7 +40,7 @@ where
impl<B> Default for LeafNode<B>
where
B: Clone,
B: BaseBehavior,
{
fn default() -> Self {
Self { behavior: None }
@@ -48,7 +49,7 @@ where
impl<B> LeafNode<B>
where
B: Clone + 'static,
B: BaseBehavior,
{
/// Creates new leaf node with given action.
pub fn new(behavior: B) -> Self {
+17 -13
View File
@@ -65,8 +65,12 @@ pub enum Status {
Running,
}
/// Base trait for all behaviors. This trait has auto-impl.
pub trait BaseBehavior: Visit + Default + PartialEq + Debug + Clone + 'static {}
impl<T: Visit + Default + PartialEq + Debug + Clone + 'static> BaseBehavior for T {}
/// A trait for user-defined actions for behavior tree.
pub trait Behavior<'a>: Visit + Default + PartialEq + Debug + Clone {
pub trait Behavior<'a>: BaseBehavior {
/// A context in which the behavior will be performed.
type Context;
@@ -80,14 +84,14 @@ pub trait Behavior<'a>: Visit + Default + PartialEq + Debug + Clone {
#[derive(Debug, PartialEq, Visit, Eq, Clone)]
pub struct RootNode<B>
where
B: Clone,
B: BaseBehavior,
{
child: Handle<BehaviorNode<B>>,
}
impl<B> Default for RootNode<B>
where
B: Clone,
B: BaseBehavior,
{
fn default() -> Self {
Self {
@@ -100,7 +104,7 @@ where
#[derive(Debug, PartialEq, Visit, Eq, Clone, Default)]
pub enum BehaviorNode<B>
where
B: Clone,
B: BaseBehavior,
{
#[doc(hidden)]
#[default]
@@ -120,7 +124,7 @@ where
#[derive(Debug, PartialEq, Visit, Clone)]
pub struct BehaviorTree<B>
where
B: Clone + 'static,
B: BaseBehavior,
{
nodes: Pool<BehaviorNode<B>>,
root: Handle<BehaviorNode<B>>,
@@ -128,7 +132,7 @@ where
impl<B> Default for BehaviorTree<B>
where
B: Clone + 'static,
B: BaseBehavior,
{
fn default() -> Self {
Self {
@@ -140,7 +144,7 @@ where
impl<B> BehaviorTree<B>
where
B: Clone + 'static,
B: BaseBehavior,
{
/// Creates new behavior tree with single root node.
pub fn new() -> Self {
@@ -251,7 +255,7 @@ where
}
}
impl<B: Clone + 'static> Index<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
impl<B: BaseBehavior> Index<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
type Output = BehaviorNode<B>;
fn index(&self, index: Handle<BehaviorNode<B>>) -> &Self::Output {
@@ -259,7 +263,7 @@ impl<B: Clone + 'static> Index<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
}
}
impl<B: Clone + 'static> IndexMut<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
impl<B: BaseBehavior> IndexMut<Handle<BehaviorNode<B>>> for BehaviorTree<B> {
fn index_mut(&mut self, index: Handle<BehaviorNode<B>>) -> &mut Self::Output {
&mut self.nodes[index]
}
@@ -271,7 +275,7 @@ pub fn sequence<B, const N: usize>(
tree: &mut BehaviorTree<B>,
) -> Handle<BehaviorNode<B>>
where
B: Clone + 'static,
B: BaseBehavior,
{
CompositeNode::new_sequence(children.to_vec()).add_to(tree)
}
@@ -282,7 +286,7 @@ pub fn selector<B, const N: usize>(
tree: &mut BehaviorTree<B>,
) -> Handle<BehaviorNode<B>>
where
B: Clone + 'static,
B: BaseBehavior,
{
CompositeNode::new_selector(children.to_vec()).add_to(tree)
}
@@ -290,7 +294,7 @@ where
/// Creates a new leaf.
pub fn leaf<B>(behavior: B, tree: &mut BehaviorTree<B>) -> Handle<BehaviorNode<B>>
where
B: Clone + 'static,
B: BaseBehavior,
{
LeafNode::new(behavior).add_to(tree)
}
@@ -301,7 +305,7 @@ pub fn inverter<B>(
tree: &mut BehaviorTree<B>,
) -> Handle<BehaviorNode<B>>
where
B: Clone + 'static,
B: BaseBehavior,
{
Inverter::new(child).add_to(tree)
}