Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions crates/bevy_mod_scripting_functions/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,22 +767,22 @@ impl World {
/// * `asset`: The asset reference, if the asset is loaded.
fn get_asset(
ctxt: FunctionCallContext,
handle_reference: ReflectReference,
registration: V<ScriptTypeRegistration>,
) -> Result<Option<ReflectReference>, InteropError> {
handle_reference: ValueOf<Handle<A>>,
registration: TypeOf<V<ScriptTypeRegistration>, A>,
) -> Result<Option<ValueOf<A>>, InteropError> {
profiling::function_scope!("get_asset");
let untyped_handle = handle_reference.try_untyped_asset_handle(ctxt.world()?)?;
Ok(Some(ReflectReference::new_asset_ref(
Ok(Some(ValueOf::new(ReflectReference::new_asset_ref(
untyped_handle,
registration.type_id(),
ctxt.world()?,
)?))
)?)))
}

/// Checks if can get asset handle
fn has_asset(
ctxt: FunctionCallContext,
handle_reference: ReflectReference,
handle_reference: ValueOf<Handle<A>>,
) -> Result<bool, InteropError> {
profiling::function_scope!("has_asset");
Ok(handle_reference
Expand Down
21 changes: 11 additions & 10 deletions crates/bindings/bevy_mod_scripting_bevy_asset_bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use std::any::TypeId;

use bevy_app::{App, Plugin};
use bevy_asset::{AssetServer, LoadState, ReflectAsset, ReflectHandle, UntypedHandle};
use bevy_asset::{AssetServer, Handle, LoadState, ReflectAsset, ReflectHandle, UntypedHandle};
// `script_bindings` expects these in scope
use bevy_ecs::world::World;
use bevy_mod_scripting_bindings::{
Expand All @@ -15,6 +15,7 @@ use bevy_mod_scripting_bindings::{
function::{
bound::{BoundHandle, BoundValue},
from::V,
generics::{A, TypeOf, ValueOf},
script_function::FunctionCallContext,
},
};
Expand Down Expand Up @@ -239,9 +240,9 @@ impl World {
fn load_asset(
ctxt: FunctionCallContext,
path: String,
asset_type: V<ScriptTypeRegistration>,
) -> Result<ReflectReference, InteropError> {
load(&ctxt.world()?, asset_type.type_id(), path)
asset_type: TypeOf<V<ScriptTypeRegistration>, A>,
) -> Result<ValueOf<Handle<A>>, InteropError> {
load(&ctxt.world()?, asset_type.type_id(), path).map(ValueOf::new)
}

/// Whether the asset and all of its dependencies are loaded.
Expand All @@ -253,7 +254,7 @@ impl World {
/// * `loaded`: Whether the asset and its dependencies are loaded.
fn is_loaded(
ctxt: FunctionCallContext,
handle: ReflectReference,
handle: ValueOf<Handle<A>>,
) -> Result<bool, InteropError> {
let world = ctxt.world()?;
let handle = handle.try_untyped_asset_handle(world.clone())?;
Expand All @@ -271,7 +272,7 @@ impl World {
/// * `state`: The name of the loading state.
fn load_state(
ctxt: FunctionCallContext,
handle: ReflectReference,
handle: ValueOf<Handle<A>>,
) -> Result<String, InteropError> {
let world = ctxt.world()?;
let handle = handle.try_untyped_asset_handle(world.clone())?;
Expand All @@ -293,7 +294,7 @@ impl World {
/// * `handle`: The handle to the asset.
/// Returns:
/// * `path`: The asset path, if known.
fn asset_path(ctxt: FunctionCallContext, handle: ReflectReference) -> Option<String> {
fn asset_path(ctxt: FunctionCallContext, handle: ValueOf<Handle<A>>) -> Option<String> {
let world = ctxt.world().ok()?;
let handle = handle.try_untyped_asset_handle(world.clone()).ok()?;
world
Expand All @@ -313,8 +314,8 @@ impl World {
/// * `handle`: A handle to the new asset.
fn add_asset(
ctxt: FunctionCallContext,
value: ReflectReference,
) -> Result<ReflectReference, InteropError> {
add(&ctxt.world()?, value)
value: ValueOf<A>,
) -> Result<ValueOf<Handle<A>>, InteropError> {
add(&ctxt.world()?, value.into_inner()).map(ValueOf::new)
}
}
39 changes: 20 additions & 19 deletions crates/lad_backends/lua_language_server_lad_backend/src/convert.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod generics;

use bevy_mod_scripting_bindings_domain::ScriptOperatorNames;
use generics::{erase_generics, generic_type_modules, instantiation_class_name};
use generics::{erase_generics, generic_type_modules, instantiation_class_name, mentions_generic};
use indexmap::{IndexMap, IndexSet};
use ladfile::{
ArgumentVisitor, LadFile, LadFunction, LadNamedType, LadTypeId, LadTypeKind,
Expand Down Expand Up @@ -479,15 +479,13 @@ pub fn lad_function_to_lua_function(
};
let (params, returns) = signature(false)?;

// the types a generic function takes can also be passed untyped, i.e. a registration found by name,
// which LuaLS can only express as an overload, as overloads can't be generic
let mut takes_types = false;
for argument in &function.arguments {
argument
.kind
.for_each(&mut |kind| takes_types |= matches!(kind, LadTypeKind::Type { .. }));
}
let overloads = match takes_types {
// what a generic function takes can also be passed untyped, i.e. a registration found by name or a handle of
// unknown type, which LuaLS can only express as an overload, as overloads can't be generic
let takes_generics = function
.arguments
.iter()
.any(|argument| mentions_generic(&argument.kind, &function.generics));
let overloads = match takes_generics {
true => {
let (params, returns) = signature(true)?;
vec![FunctionSignature {
Expand Down Expand Up @@ -1585,7 +1583,7 @@ mod test {
},
LadTypeKind::Val(of_plain.clone()),
);
// and a generic function, generic over the type it is given
// and a generic function, generic over the type it is given, and over a generic type applied to it
let a = || LadNamedType::Param { param: "A".into() };
let get = LadFunctionId::new_string_id("get".into());
file.functions.insert(
Expand All @@ -1594,13 +1592,16 @@ mod test {
namespace: LadFunctionNamespace::Type(plain.clone().into()),
identifier: "get".into(),
overload_index: None,
arguments: vec![arg(
"registration",
LadTypeKind::Type {
of: a(),
erased: Box::new(LadTypeKind::Val(plain.clone().into())),
},
)],
arguments: vec![
arg(
"registration",
LadTypeKind::Type {
of: a(),
erased: Box::new(LadTypeKind::Val(plain.clone().into())),
},
),
arg("handle", LadTypeKind::Val(applied(LadTypeKind::Val(a())))),
],
return_type: arg(
"return",
LadTypeKind::Option(Box::new(LadTypeKind::Val(a()))),
Expand Down Expand Up @@ -1630,7 +1631,7 @@ mod test {
// the generic function returns the type it is given, or a reference when given it untyped
assert!(
rendered.contains(
"---@generic A\n---@param registration A\n---@overload fun(registration: ladfile_builder.PlainStructType): ReflectReference | nil\n---@return A | nil\nfunction PlainStructType.get(registration) end"
"---@generic A\n---@param registration A\n---@param handle ladfile_builder.OpaqueGenericType<A>\n---@overload fun(registration: ladfile_builder.PlainStructType, handle: ReflectReference): ReflectReference | nil\n---@return A | nil\nfunction PlainStructType.get(registration,handle) end"
),
"{rendered}"
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ pub(super) fn erase_generics(kind: &LadTypeKind, generics: &[String]) -> LadType
let mut kind = kind.clone();
match &kind {
LadTypeKind::Type { of, erased } if is_generic(of) => return (**erased).clone(),
kind if kind.named().is_some_and(is_generic) => {
// values of a parameter, or of a type applied to one like `Handle<A>`, are references
kind if kind.named().is_some() && mentions_generic(kind, generics) => {
return LadTypeKind::Primitive(ReflectionPrimitiveKind::ReflectReference);
}
_ => {}
Expand All @@ -300,3 +301,17 @@ pub(super) fn erase_generics(kind: &LadTypeKind, generics: &[String]) -> LadType
}
kind
}

/// Whether the kind uses one of the given parameters of a generic function.
pub(super) fn mentions_generic(kind: &LadTypeKind, generics: &[String]) -> bool {
let mut mentions = false;
kind.for_each(&mut |kind| {
let named = match kind {
LadTypeKind::Type { of, .. } | LadTypeKind::Fields { of, .. } => Some(of),
kind => kind.named(),
};
mentions |=
matches!(named, Some(LadNamedType::Param { param }) if generics.contains(param));
});
mentions
}
21 changes: 16 additions & 5 deletions crates/ladfile_builder/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,15 +638,17 @@ mod test {
// a function shared by all types, which is bound to the instantiation too
NamespaceBuilder::<StaticTypeRef>::new(app.world_mut())
.register("describe", |_: StaticTypeRef| 1usize);
// a generic function, over the markers of its signature
// a generic function, over the markers of its signature, which also uses one as an argument of a generic type
use bevy_mod_scripting_bindings::function::{
from::V,
generics::{TypeOf, ValueOf},
};
NamespaceBuilder::<OtherAsset>::new(app.world_mut())
.register("get", |_: TypeOf<V<OtherAsset>, A>| -> Option<ValueOf<A>> {
None
});
NamespaceBuilder::<OtherAsset>::new(app.world_mut()).register(
"get",
|_: TypeOf<V<OtherAsset>, A>,
_: ValueOf<bevy_asset::Handle<A>>|
-> Option<ValueOf<A>> { None },
);
let file = generate_from(app);

// the marker is registered, but not a type of its own
Expand Down Expand Up @@ -693,6 +695,15 @@ mod test {
erased: Box::new(LadTypeKind::Val(type_id(&file, "OtherAsset").into())),
}
);
// and its handle is the generic type applied to the parameter, which isn't recorded as an instantiation
assert_eq!(
get.arguments[1].kind,
LadTypeKind::Val(ladfile::LadNamedType::Generic {
generic_type: generic_id.clone(),
args: vec![LadTypeKind::Val(param())],
})
);
assert_eq!(file.generic_types[generic_id].instantiations.len(), 1);
assert_eq!(
get.return_type.kind,
LadTypeKind::Option(Box::new(LadTypeKind::Val(param())))
Expand Down
2 changes: 1 addition & 1 deletion docs/src/ReleaseNotes/0.23-to-0.24.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,4 @@ Other changes:
- `Ref`/`Mut`/`Val` hold a `LadNamedType`: a type id as before, or a declared generic type applied to arguments (`{ "generic_type": "bevy_asset::handle::Handle", "args": [...] }`), which is how instantiations are named everywhere (`ArgumentVisitor::visit_generic`).
- Instantiations of declared generic types aren't types of the file anymore: they are recorded on the generic type (`LadGenericType::instantiations`, with their arguments, metadata and functions, see `LadFile::get_instantiation`). `LadFunctionNamespace::Type`/`Variant` and `LadTypeKind::Fields` name types by `LadNamedType`. `LadTypeDefinition::generic_type`, `LadFile::get_generic_instance` and `LadFile::documented_types` are removed.
- The functions of an instantiation are in `LadInstantiation::associated_functions`. In Lua they are on a subclass of the generic class (`time.Time.Virtual : time.Time<time.Virtual>`), as are the functions of its variants (`asset.Handle.Image.Strong : asset.Handle.Strong<render.Image>`), and the mdbook page of the generic type lists them, titled by the instantiation (`Time<Virtual>.pause`).
- Generic functions are declared in their signature with `TypeOf<R, A>` (a type, passed as `R`) and `ValueOf<A>` (a value of it, a reference at runtime), see `bindings::function::generics`. `world.get_component`, `get_resource`, `insert_component` and `bms.construct` use them, so in Lua `world.get_component(entity, Transform)` is a `transform.Transform?` (`---@generic A`, with an `---@overload` for untyped registrations). LAD: `LadFunction::generics`, `LadTypeKind::Type { of, erased }` (`ArgumentVisitor::visit_type`), and `ThroughTypeInfo::TypeOf`.
- Generic functions are declared in their signature with `TypeOf<R, A>` (a type, passed as `R`) and `ValueOf<A>` (a value of it, a reference at runtime), see `bindings::function::generics`. `world.get_component`, `get_resource`, `insert_component`, `bms.construct` and the asset functions of `world` (`load_asset`, `get_asset`, `add_asset`, ..., taking a `ValueOf<Handle<A>>`) use them, so in Lua `world.get_component(entity, Transform)` is a `transform.Transform?` and `world.load_asset(path, Image)` an `asset.Handle<render.Image>` (`---@generic A`, with an `---@overload` for untyped registrations). LAD: `LadFunction::generics`, `LadTypeKind::Type { of, erased }` (`ArgumentVisitor::visit_type`), and `ThroughTypeInfo::TypeOf`.
Loading