Skip to content

Commit 9213291

Browse files
bradrnTrueDoctorKeavon0HyperCube
authored
Remove subtyping for () from node graph type system (#2418)
* Remove subtyping for () from node graph type system * Remove special-case in DynAnyNode for downcasting to () * Correct input type for CreateGpuSurfaceNode * Remove unncessary imports --------- Co-authored-by: Dennis Kobert <dennis@kobert.dev> Co-authored-by: Keavon Chambers <keavon@keavon.com> Co-authored-by: hypercube <0hypercube@gmail.com>
1 parent dd27f46 commit 9213291

File tree

3 files changed

+9
-23
lines changed

3 files changed

+9
-23
lines changed

node-graph/gcore/src/registry.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::transform::Footprint;
21
use crate::{Node, NodeIO, NodeIOTypes, Type, WasmNotSend};
32
use dyn_any::{DynAny, StaticType};
43
use std::collections::HashMap;
@@ -251,21 +250,6 @@ where
251250
};
252251
match dyn_any::downcast(input) {
253252
Ok(input) => Box::pin(output(*input)),
254-
// If the input type of the node is `()` and we supply an invalid type, we can still call the
255-
// node and just ignore the input and call it with the unit type instead.
256-
Err(_) if core::any::TypeId::of::<_I::Static>() == core::any::TypeId::of::<()>() => {
257-
assert_eq!(std::mem::size_of::<_I>(), 0);
258-
// Rust can't know, that `_I` and `()` are the same size, so we have to use a `transmute_copy()` here
259-
Box::pin(output(unsafe { std::mem::transmute_copy(&()) }))
260-
}
261-
// If the Node expects a footprint but we provide (). In this case construct the default Footprint and pass that
262-
// This is pretty hacky pls fix
263-
Err(_) if core::any::TypeId::of::<_I::Static>() == core::any::TypeId::of::<Footprint>() => {
264-
assert_eq!(std::mem::size_of::<_I>(), std::mem::size_of::<Footprint>());
265-
assert_eq!(std::mem::align_of::<_I>(), std::mem::align_of::<Footprint>());
266-
// Rust can't know, that `_I` and `Footprint` are the same size, so we have to use a `transmute_copy()` here
267-
Box::pin(output(unsafe { std::mem::transmute_copy(&Footprint::default()) }))
268-
}
269253
Err(e) => panic!("DynAnyNode Input, {0} in:\n{1}", e, node_name),
270254
}
271255
}

node-graph/graph-craft/src/proto.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -691,13 +691,14 @@ impl TypingContext {
691691

692692
/// Checks if a proposed input to a particular (primary or secondary) input connector is valid for its type signature.
693693
/// `from` indicates the value given to a input, `to` indicates the input's allowed type as specified by its type signature.
694-
fn valid_subtype(from: &Type, to: &Type) -> bool {
694+
fn valid_type(from: &Type, to: &Type) -> bool {
695695
match (from, to) {
696696
// Direct comparison of two concrete types.
697697
(Type::Concrete(type1), Type::Concrete(type2)) => type1 == type2,
698698
// Check inner type for futures
699699
(Type::Future(type1), Type::Future(type2)) => type1 == type2,
700-
// Loose comparison of function types, where loose means that functions are considered on a "greater than or equal to" basis of its function type's generality.
700+
// Direct comparison of two function types.
701+
// Note: in the presence of subtyping, functions are considered on a "greater than or equal to" basis of its function type's generality.
701702
// That means we compare their types with a contravariant relationship, which means that a more general type signature may be substituted for a more specific type signature.
702703
// For example, we allow `T -> V` to be substituted with `T' -> V` or `() -> V` where T' and () are more specific than T.
703704
// This allows us to supply anything to a function that is satisfied with `()`.
@@ -706,8 +707,9 @@ impl TypingContext {
706707
// - `V >= V' ⇒ (T -> V) >= (T -> V')` (functions are covariant in their output types)
707708
// While these two relations aren't a truth about the universe, they are a design decision that we are employing in our language design that is also common in other languages.
708709
// For example, Rust implements these same relations as it describes here: <https://doc.rust-lang.org/nomicon/subtyping.html>
710+
// Graphite doesn't have subtyping currently, but it used to have it, and may do so again, so we make sure to compare types in this way to make things easier.
709711
// More details explained here: <https://github.com/GraphiteEditor/Graphite/issues/1741>
710-
(Type::Fn(in1, out1), Type::Fn(in2, out2)) => valid_subtype(out2, out1) && (valid_subtype(in1, in2) || **in1 == concrete!(())),
712+
(Type::Fn(in1, out1), Type::Fn(in2, out2)) => valid_type(out2, out1) && valid_type(in1, in2),
711713
// If either the proposed input or the allowed input are generic, we allow the substitution (meaning this is a valid subtype).
712714
// TODO: Add proper generic counting which is not based on the name
713715
(Type::Generic(_), _) | (_, Type::Generic(_)) => true,
@@ -719,7 +721,7 @@ impl TypingContext {
719721
// List of all implementations that match the input types
720722
let valid_output_types = impls
721723
.keys()
722-
.filter(|node_io| valid_subtype(&node_io.call_argument, &primary_input_or_call_argument) && inputs.iter().zip(node_io.inputs.iter()).all(|(p1, p2)| valid_subtype(p1, p2)))
724+
.filter(|node_io| valid_type(&node_io.call_argument, &primary_input_or_call_argument) && inputs.iter().zip(node_io.inputs.iter()).all(|(p1, p2)| valid_type(p1, p2)))
723725
.collect::<Vec<_>>();
724726

725727
// Attempt to substitute generic types with concrete types and save the list of results
@@ -753,7 +755,7 @@ impl TypingContext {
753755
.cloned()
754756
.zip([&node_io.call_argument].into_iter().chain(&node_io.inputs).cloned())
755757
.enumerate()
756-
.filter(|(_, (p1, p2))| !valid_subtype(p1, p2))
758+
.filter(|(_, (p1, p2))| !valid_type(p1, p2))
757759
.map(|(index, ty)| {
758760
let i = node.original_location.inputs(index).min_by_key(|s| s.node.len()).map(|s| s.index).unwrap_or(index);
759761
let i = if using_manual_composition { i } else { i + 1 };

node-graph/interpreted-executor/src/node_registry.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
8888
ProtoNodeIdentifier::new(stringify!(wgpu_executor::CreateGpuSurfaceNode<_>)),
8989
|args| {
9090
Box::pin(async move {
91-
let editor_api: DowncastBothNode<(), &WasmEditorApi> = DowncastBothNode::new(args[0].clone());
91+
let editor_api: DowncastBothNode<Context, &WasmEditorApi> = DowncastBothNode::new(args[0].clone());
9292
let node = <wgpu_executor::CreateGpuSurfaceNode<_>>::new(editor_api);
93-
let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(node);
93+
let any: DynAnyNode<Context, _, _> = graphene_std::any::DynAnyNode::new(node);
9494
Box::new(any) as TypeErasedBox
9595
})
9696
},

0 commit comments

Comments
 (0)