@@ -1138,6 +1138,72 @@ impl PointeeTy {
11381138 {
11391139 TypeSafety :: unknown_in_argument ( "should be of the correct type" )
11401140 }
1141+ // Passing `MTLFunction` is spiritually similar to passing an
1142+ // `unsafe` function pointer; we can't know without inspecting
1143+ // the function (or it's documentation) whether it has special
1144+ // safety requirements. Example:
1145+ //
1146+ // ```metal
1147+ // constant float data[5] = { 1.0, 2.0, 3.0, 4.0, 5.0 };
1148+ //
1149+ // // Safety: Must not be called with an index < 5.
1150+ // kernel void add_static(
1151+ // device const float* input,
1152+ // device float* result,
1153+ // uint index [[thread_position_in_grid]]
1154+ // ) {
1155+ // if (5 <= index) {
1156+ // // For illustration purposes.
1157+ // __builtin_unreachable();
1158+ // }
1159+ // result[index] = input[index] + data[index];
1160+ // }
1161+ // ```
1162+ [ ( protocol, _) ]
1163+ if protocol. is_subprotocol_of ( "MTLFunction" )
1164+ || protocol. is_subprotocol_of ( "MTLFunctionHandle" ) =>
1165+ {
1166+ TypeSafety :: unknown_in_argument ( "must be safe to call" ) . merge (
1167+ TypeSafety :: unknown_in_argument (
1168+ "must have the correct argument and return types" ,
1169+ ) ,
1170+ )
1171+ }
1172+ // Access to the contents of a resource has to be manually
1173+ // synchronized using things like `didModifyRange:` (CPU side)
1174+ // or `synchronizeResource:`, `useResource:usage:` and
1175+ // `MTLFence` (GPU side).
1176+ [ ( protocol, _) ] if protocol. is_subprotocol_of ( "MTLResource" ) => {
1177+ let safety = TypeSafety :: unknown_in_argument ( "may need to be synchronized" ) ;
1178+
1179+ // Additionally, resources in a command buffer must be
1180+ // kept alive by the application for as long as they're
1181+ // used. If this is not done, it is possible to encounter
1182+ // use-after-frees with:
1183+ // - `MTLCommandBufferDescriptor::setRetainedReferences(false)`.
1184+ // - `MTLCommandQueue::commandBufferWithUnretainedReferences()`.
1185+ // - All `MTL4CommandBuffer`s.
1186+ let safety = safety. merge ( TypeSafety :: unknown_in_argument (
1187+ "may be unretained, you must ensure it is kept alive while in use" ,
1188+ ) ) ;
1189+
1190+ // TODO: Should we also document the requirement for
1191+ // resources to be properly bound? What exactly are the
1192+ // requirements though, and when does Metal automatically
1193+ // bind resources?
1194+
1195+ // `MTLBuffer` is effectively a `Box<[u8]>` stored on the
1196+ // GPU (and depending on the storage mode, optionally also
1197+ // on the CPU). Type-safety of the contents is left
1198+ // completely up to the user.
1199+ if protocol. id . name == "MTLBuffer" {
1200+ safety. merge ( TypeSafety :: unknown_in_argument (
1201+ "contents should be of the correct type" ,
1202+ ) )
1203+ } else {
1204+ safety
1205+ }
1206+ }
11411207 // Other `ProtocolObject<dyn MyProtocol>`s are treated as
11421208 // proper types. (An example here is delegate protocols).
11431209 [ _] => TypeSafety :: SAFE ,
@@ -3982,6 +4048,43 @@ impl Ty {
39824048 }
39834049 }
39844050
4051+ /// Whether the type could in theory affect the bounds of the receiver.
4052+ ///
4053+ /// This is meant to catch `NSInteger`, `NSRange`, `MTL4BufferRange`, `MTLGPUAddress` and
4054+ /// similar constructs.
4055+ pub ( crate ) fn can_affect_bounds ( & self ) -> bool {
4056+ match self . through_typedef ( ) {
4057+ Self :: Pointer { pointee, .. } | Self :: IncompleteArray { pointee, .. } => {
4058+ pointee. can_affect_bounds ( )
4059+ }
4060+ Self :: Array { element_type, .. } => element_type. can_affect_bounds ( ) ,
4061+ Self :: Primitive ( prim) | Self :: Simd { ty : prim, .. } => matches ! (
4062+ prim,
4063+ // 32-bit and 64-bit integers.
4064+ Primitive :: I32
4065+ | Primitive :: I64
4066+ | Primitive :: Int
4067+ | Primitive :: Long
4068+ | Primitive :: ISize
4069+ | Primitive :: NSInteger
4070+ | Primitive :: U32
4071+ | Primitive :: U64
4072+ | Primitive :: UInt
4073+ | Primitive :: ULong
4074+ | Primitive :: USize
4075+ | Primitive :: NSUInteger
4076+ | Primitive :: PtrDiff
4077+ ) ,
4078+ Self :: Struct { fields, .. } | Self :: Union { fields, .. } => {
4079+ fields. iter ( ) . any ( |field| field. can_affect_bounds ( ) )
4080+ }
4081+ // Enumerations are intentionally not bounds-affecting (e.g. not
4082+ // `MTLIndexType`).
4083+ Self :: Pointee ( _) | Self :: Enum { .. } | Self :: Sel { .. } => false ,
4084+ Self :: TypeDef { .. } => unreachable ! ( "using through_typedef" ) ,
4085+ }
4086+ }
4087+
39854088 fn into_pointee ( self ) -> Option < PointeeTy > {
39864089 match self {
39874090 Self :: Pointee ( pointee) => Some ( pointee) ,
0 commit comments