Skip to content

Forbid unsafe in most crates in the engine #12684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
aedc4db
Forbid unsafe code in bevy_winit
james7132 Mar 24, 2024
0791934
Deny unsafe in most of bevy_ui
james7132 Mar 24, 2024
3894b26
Deny unsafe code in (most of) bevy_transform
james7132 Mar 24, 2024
59116d5
Forbid unsafe code in bevy_time
james7132 Mar 24, 2024
1d35985
Forbid unsafe code in bevy_text
james7132 Mar 24, 2024
d1ef180
Forbid unsafe code in bevy_sprite
james7132 Mar 24, 2024
aa46aff
Forbid unsafe code in bevy_scene
james7132 Mar 24, 2024
ae42634
Deny workspace wide
james7132 Mar 24, 2024
b5e2a7d
Allow annotations for bevy_reflect, bevy_ptr, bevy_tasks, and bevy_utils
james7132 Mar 24, 2024
d4a8eb6
Allow annotations for bevy_mikktspace
james7132 Mar 24, 2024
48ff84f
Blanket allow on bevy_ecs
james7132 Mar 24, 2024
e3a67be
Add the rest of the missing allow annotations
james7132 Mar 24, 2024
c3d6d1e
Forbid unsafe code in most of the remaining crates
james7132 Mar 24, 2024
2a8f1f5
Use the derive macros and forbid unsafe in bevy_color
james7132 Mar 24, 2024
371d3e0
Merge branch 'main' into forbid-unsafe
james7132 Mar 24, 2024
2cd1ab7
Formatting
james7132 Mar 24, 2024
50cc793
Allow unsafe in single threaded task pool
james7132 Mar 24, 2024
0904dfe
Allow unsafe in tests
james7132 Mar 24, 2024
61e6d4a
Shift comment locations
james7132 Mar 24, 2024
19437d5
Allow unsafe in the dynamic ECS example
james7132 Mar 24, 2024
ff25cd5
Covert existing denies to forbids
james7132 Mar 24, 2024
5e77a38
Remove the improper forbids
james7132 Mar 24, 2024
db52c04
Merge branch 'main' into forbid-unsafe
james7132 Mar 25, 2024
8b99a2a
Allow unsafe code in bevy_pbr
james7132 Mar 25, 2024
e23ba46
Module level allow
james7132 Mar 26, 2024
08cf890
Allow unsafe for android logging
james7132 Mar 27, 2024
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ ptr_cast_constness = "warn"
[workspace.lints.rust]
unsafe_op_in_unsafe_fn = "warn"
missing_docs = "warn"
unsafe_code = "deny"

[lints]
workspace = true
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_animation/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_app/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_asset/src/reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl ReflectAsset {
}

/// Equivalent of [`Assets::get_mut`]
#[allow(unsafe_code)]
pub fn get_mut<'w>(
&self,
world: &'w mut World,
Expand Down Expand Up @@ -82,6 +83,7 @@ impl ReflectAsset {
/// violating Rust's aliasing rules. To avoid this:
/// * Only call this method if you know that the [`UnsafeWorldCell`] may be used to access the corresponding `Assets<T>`
/// * Don't call this method more than once in the same scope.
#[allow(unsafe_code)]
pub unsafe fn get_unchecked_mut<'w>(
&self,
world: UnsafeWorldCell<'w>,
Expand Down Expand Up @@ -135,6 +137,7 @@ impl<A: Asset + FromReflect> FromType<A> for ReflectAsset {
get_unchecked_mut: |world, handle| {
// SAFETY: `get_unchecked_mut` must be called with `UnsafeWorldCell` having access to `Assets<A>`,
// and must ensure to only have at most one reference to it live at all times.
#[allow(unsafe_code)]
let assets = unsafe { world.get_resource_mut::<Assets<A>>().unwrap().into_inner() };
let asset = assets.get_mut(&handle.typed_debug_checked());
asset.map(|asset| asset as &mut dyn Reflect)
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_color/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
29 changes: 1 addition & 28 deletions crates/bevy_color/src/linear_rgba.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use bytemuck::{Pod, Zeroable};
/// <div>
#[doc = include_str!("../docs/diagrams/model_graph.svg")]
/// </div>
#[derive(Debug, Clone, Copy, PartialEq, Reflect)]
#[derive(Debug, Clone, Copy, PartialEq, Reflect, Pod, Zeroable)]
#[reflect(PartialEq, Default)]
#[cfg_attr(
feature = "serialize",
Expand Down Expand Up @@ -373,33 +373,6 @@ impl encase::private::CreateFrom for LinearRgba {
}
}

/// A [`Zeroable`] type is one whose bytes can be filled with zeroes while remaining valid.
///
/// SAFETY: [`LinearRgba`] is inhabited
/// SAFETY: [`LinearRgba`]'s all-zero bit pattern is a valid value
unsafe impl Zeroable for LinearRgba {
fn zeroed() -> Self {
LinearRgba {
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 0.0,
}
}
}

/// The [`Pod`] trait is [`bytemuck`]'s marker for types that can be safely transmuted from a byte array.
///
/// It is intended to only be implemented for types which are "Plain Old Data".
///
/// SAFETY: [`LinearRgba`] is inhabited.
/// SAFETY: [`LinearRgba`] permits any bit value.
/// SAFETY: [`LinearRgba`] does not have padding bytes.
/// SAFETY: all of the fields of [`LinearRgba`] are [`Pod`], as f32 is [`Pod`].
/// SAFETY: [`LinearRgba`] is `repr(C)`
/// SAFETY: [`LinearRgba`] does not permit interior mutability.
unsafe impl Pod for LinearRgba {}

impl encase::ShaderSize for LinearRgba {}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_dev_tools/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_diagnostic/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_dynamic_plugin/src/loader.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(unsafe_code)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😂 No targeted allows here.


use libloading::{Library, Symbol};
use std::ffi::OsStr;
use thiserror::Error;
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![allow(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_encase_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_gilrs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_gizmos/src/gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type GizmosState<T> = (
pub struct GizmosFetchState<T: GizmoConfigGroup> {
state: <GizmosState<T> as SystemParam>::State,
}

#[allow(unsafe_code)]
// SAFETY: All methods are delegated to existing `SystemParam` implementations
unsafe impl<T: GizmoConfigGroup> SystemParam for Gizmos<'_, '_, T> {
type State = GizmosFetchState<T>;
Expand Down Expand Up @@ -90,6 +92,8 @@ unsafe impl<T: GizmoConfigGroup> SystemParam for Gizmos<'_, '_, T> {
}
}
}

#[allow(unsafe_code)]
// Safety: Each field is `ReadOnlySystemParam`, and Gizmos SystemParam does not mutate world
unsafe impl<'w, 's, T: GizmoConfigGroup> ReadOnlySystemParam for Gizmos<'w, 's, T>
where
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_gltf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_hierarchy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_input/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_internal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_log/src/android_tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ impl<S: Subscriber + for<'a> LookupSpan<'a>> Layer<S> for AndroidLayer {
}
}

#[allow(unsafe_code)]
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
let mut recorder = StringRecorder::new();
event.record(&mut recorder);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_macro_utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![deny(unsafe_code)]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_math/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_mikktspace/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
non_upper_case_globals,
unused_mut,
unused_assignments,
unused_variables
unused_variables,
unsafe_code
)]

use std::ptr::null_mut;
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_mikktspace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub trait Geometry {
///
/// Returns `false` if the geometry is unsuitable for tangent generation including,
/// but not limited to, lack of vertices.
#[allow(unsafe_code)]
pub fn generate_tangents<I: Geometry>(geometry: &mut I) -> bool {
unsafe { generated::genTangSpace(geometry, 180.0) }
}
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_pbr/src/meshlet/persistent_buffer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(unsafe_code)]

use bevy_render::{
render_resource::{
BindingResource, Buffer, BufferAddress, BufferDescriptor, BufferUsages,
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/meshlet/persistent_buffer_impls.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(unsafe_code)]
#![allow(clippy::undocumented_unsafe_blocks)]

use super::{persistent_buffer::PersistentGpuBufferable, Meshlet, MeshletBoundingSphere};
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ptr/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![doc = include_str!("../README.md")]
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![allow(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_reflect/src/path/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl<'a> PathParser<'a> {
// the last byte before an ASCII utf-8 character (ie: it is a char
// boundary).
// - The slice always starts after a symbol ie: an ASCII character's boundary.
#[allow(unsafe_code)]
let ident = unsafe { from_utf8_unchecked(ident) };

self.remaining = remaining;
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_reflect/src/type_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ pub struct ReflectFromPtr {
from_ptr_mut: unsafe fn(PtrMut) -> &mut dyn Reflect,
}

#[allow(unsafe_code)]
impl ReflectFromPtr {
/// Returns the [`TypeId`] that the [`ReflectFromPtr`] was constructed for.
pub fn type_id(&self) -> TypeId {
Expand Down Expand Up @@ -714,6 +715,7 @@ impl ReflectFromPtr {
}
}

#[allow(unsafe_code)]
impl<T: Reflect> FromType<T> for ReflectFromPtr {
fn from_type() -> Self {
ReflectFromPtr {
Expand All @@ -733,6 +735,7 @@ impl<T: Reflect> FromType<T> for ReflectFromPtr {
}

#[cfg(test)]
#[allow(unsafe_code)]
mod test {
use crate::{GetTypeRegistration, ReflectFromPtr};
use bevy_ptr::{Ptr, PtrMut};
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![allow(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_scene/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_sprite/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_tasks/src/single_threaded_task_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl TaskPool {
/// to spawn tasks. This function will await the completion of all tasks before returning.
///
/// This is similar to `rayon::scope` and `crossbeam::scope`
#[allow(unsafe_code)]
pub fn scope_with_executor<'env, F, T>(
&self,
_tick_task_pool_executor: bool,
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_tasks/src/task_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ impl TaskPool {
})
}

#[allow(unsafe_code)]
fn scope_with_executor_inner<'env, F, T>(
&self,
tick_task_pool_executor: bool,
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_text/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// FIXME(3492): remove once docs are ready
#![allow(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
33 changes: 13 additions & 20 deletions crates/bevy_text/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,32 +129,25 @@ impl TextMeasureInfo {
scale_factor: f32,
) -> Result<TextMeasureInfo, TextError> {
let sections = &text.sections;
for section in sections {
if !fonts.contains(&section.style.font) {
return Err(TextError::NoSuchFont);
}
}
let (auto_fonts, sections) = sections
.iter()
.enumerate()
.map(|(i, section)| {
// SAFETY: we exited early earlier in this function if
// one of the fonts was missing.
let font = unsafe { fonts.get(&section.style.font).unwrap_unchecked() };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good choice, this is the sort of needless unsafe I was hoping to clean up.

(
font.font.clone(),
TextMeasureSection {
let mut auto_fonts = Vec::with_capacity(sections.len());
let mut out_sections = Vec::with_capacity(sections.len());
for (i, section) in sections.iter().enumerate() {
match fonts.get(&section.style.font) {
Some(font) => {
auto_fonts.push(font.font.clone());
out_sections.push(TextMeasureSection {
font_id: FontId(i),
scale: scale_value(section.style.font_size, scale_factor),
text: section.value.clone().into_boxed_str(),
},
)
})
.unzip();
});
}
None => return Err(TextError::NoSuchFont),
}
}

Ok(Self::new(
auto_fonts,
sections,
out_sections,
text.justify,
text.linebreak_behavior.into(),
))
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_time/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_transform/src/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub fn propagate_transforms(
// - Since each root entity is unique and the hierarchy is consistent and forest-like,
// other root entities' `propagate_recursive` calls will not conflict with this one.
// - Since this is the only place where `transform_query` gets used, there will be no conflicting fetches elsewhere.
#[allow(unsafe_code)]
unsafe {
propagate_recursive(
&global_transform,
Expand Down Expand Up @@ -106,6 +107,7 @@ pub fn propagate_transforms(
/// nor any of its descendants.
/// - The caller must ensure that the hierarchy leading to `entity`
/// is well-formed and must remain as a tree or a forest. Each entity must have at most one parent.
#[allow(unsafe_code)]
unsafe fn propagate_recursive(
parent: &GlobalTransform,
transform_query: &Query<
Expand Down
Loading