From 9125df4fc553963a63355a4ffe8d917259c82b84 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 7 Feb 2023 09:50:03 -0800 Subject: [PATCH] Implement generic signature checking for PartialType.create(with:) Check other constraints some more fixes --- include/swift/Runtime/Metadata.h | 5 + .../Sources/Reflection/PartialType.swift | 439 ++++++++++++++---- .../Reflection/Sources/Reflection/Type.swift | 6 +- .../ContextDescriptorValues.swift | 52 ++- .../ContextDescriptor/GenericSignature.swift | 209 ++++++++- .../Sources/_Runtime/Functions.swift | 4 + .../_Runtime/Metadata/ClassMetadata.swift | 2 +- .../Sources/_Runtime/Metadata/Metadata.swift | 17 + .../Metadata/MetadataAccessFunction.swift | 186 ++++++-- .../_Runtime/Utils/MangledTypeReference.swift | 7 + .../Sources/_Runtime/Utils/PointerStuff.swift | 28 +- .../RelativeIndirectablePointerIntPair.swift | 15 + stdlib/public/runtime/ProtocolConformance.cpp | 7 + test/stdlib/Reflection/PartialType.swift | 225 +++++++++ 14 files changed, 1052 insertions(+), 150 deletions(-) create mode 100644 test/stdlib/Reflection/PartialType.swift diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 03a2e10e88b0f..7366f92f4c0d1 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -873,6 +873,11 @@ SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL MetadataResponse getSuperclassMetadata(MetadataRequest request, const ClassMetadata *self); +SWIFT_CC(swift) +SWIFT_RUNTIME_STDLIB_SPI +bool _swift_class_isSubclass(const Metadata *subclass, + const Metadata *superclass); + #if !NDEBUG /// Verify that the given metadata pointer correctly roundtrips its /// mangled name through the demangler. diff --git a/stdlib/public/Reflection/Sources/Reflection/PartialType.swift b/stdlib/public/Reflection/Sources/Reflection/PartialType.swift index 7d4262b42bf2e..51eed79b4aba8 100644 --- a/stdlib/public/Reflection/Sources/Reflection/PartialType.swift +++ b/stdlib/public/Reflection/Sources/Reflection/PartialType.swift @@ -39,118 +39,379 @@ extension PartialType { @available(SwiftStdlib 5.9, *) extension PartialType { + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// This variant takes no arguments which assumes the type being referenced by + /// this `PartialType` is completely non-generic. In the following example, + /// `Nested` is technically generic, but in reality there's only a single + /// spelling of this which is `Generic.Nested`. Considering this, + /// `PartialType` does not consider this type to be generic thus you can call + /// the no argument variant of `create(with:)` to create a type of `Nested`. + /// + /// struct Generic {} + /// + /// extension Generic where T == Int { + /// struct Nested {} + /// } + /// + /// let nestedTy = Type(Generic.Nested.self) + /// print(nestedTy.partial?.create()) // Optional(Generic.Nested) + /// + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable public func create() -> Type? { - guard !descriptor.base.flags.isGeneric else { - return nil - } - - return Type(descriptor.accessor(.complete)) + _create(with: UnsafeBufferPointer(start: nil, count: 0)) } - + + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// Takes a list of types to use when making the full `Type` instance. It + /// expects the exact same number of generic arguments that is needed for + /// creation. Note that all of the provided types must fulfill the + /// requirements of the type's generic signature. In the following example, + /// `Dictionary` requires that its `Key` argument must conform to `Hashable`. + /// If the provided `Key` argument in the list of types does not conform to + /// `Hashable`, this operation will fail and return a `nil`. + /// + /// struct Dictionary {} + /// + /// let dictTy = Type([String: String].self) + /// // Optional(Dictionary) + /// print(dictTy.partial?.create(with: Type(String.self), Type(Int.self))) + /// + /// The order in which the provided types are assigned start from the + /// outermost generic context and works its way inward. + /// + /// struct ABC { + /// struct XYZ {} + /// } + /// + /// Effectively `XYZ` has 4 generic arguments needed to fully realize it. + /// Given the following list of types: `[Int, String, Double, Array]`, + /// we start with the outermost type, `ABC`, and assign the following generic + /// arguments: `` and lookup the `Equatable` conformance + /// for `String`. Now for `XYZ`, we finish the generic assignment with the + /// following: `>` and lookup `Double`'s + /// `Hashable`. + /// + /// - Parameters: + /// - types: A variadic list of `Type` instances used when making this + /// `PartialType`'s realized `Type`. + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable - public func create(with arg: Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 1 else { - return nil - } - - return Type(descriptor.accessor(.complete, arg.metadata)) + public func create(with types: Type...) -> Type? { + create(with: types) } - + + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// Takes a list of types to use when making the full `Type` instance. It + /// expects the exact same number of generic arguments that is needed for + /// creation. Note that all of the provided types must fulfill the + /// requirements of the type's generic signature. In the following example, + /// `Dictionary` requires that its `Key` argument must conform to `Hashable`. + /// If the provided `Key` argument in the list of types does not conform to + /// `Hashable`, this operation will fail and return a `nil`. + /// + /// struct Dictionary {} + /// + /// let dictTy = Type([String: String].self) + /// // Optional(Dictionary) + /// print(dictTy.partial?.create( + /// with: [Type(String.self), Type(Int.self)] + /// )) + /// + /// The order in which the provided types are assigned start from the + /// outermost generic context and works its way inward. + /// + /// struct ABC { + /// struct XYZ {} + /// } + /// + /// Effectively `XYZ` has 4 generic arguments needed to fully realize it. + /// Given the following list of types: `[Int, String, Double, Array]`, + /// we start with the outermost type, `ABC`, and assign the following generic + /// arguments: `` and lookup the `Equatable` conformance + /// for `String`. Now for `XYZ`, we finish the generic assignment with the + /// following: `>` and lookup `Double`'s + /// `Hashable`. + /// + /// - Parameters: + /// - types: An array of `Type` instances used when making this + /// `PartialType`'s realized `Type`. + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable - public func create(with arg0: Type, _ arg1: Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 2 else { - return nil + public func create(with args: [Type]) -> Type? { + args.withUnsafeBufferPointer { + let buffer = UnsafeBufferPointer( + start: UnsafePointer( + $0.baseAddress.unsafelyUnwrapped._rawValue + ), + count: $0.count + ) + + return _create(with: buffer) } - - return Type(descriptor.accessor(.complete, arg0.metadata, arg1.metadata)) } - + + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// Takes a list of types to use when making the full `Type` instance. It + /// expects the exact same number of generic arguments that is needed for + /// creation. Note that all of the provided types must fulfill the + /// requirements of the type's generic signature. In the following example, + /// `Dictionary` requires that its `Key` argument must conform to `Hashable`. + /// If the provided `Key` argument in the list of types does not conform to + /// `Hashable`, this operation will fail and return a `nil`. + /// + /// struct Dictionary {} + /// + /// let dictTy = Type([String: String].self) + /// // Optional(Dictionary) + /// print(dictTy.partial?.create(with: String.self, Int.self)) + /// + /// The order in which the provided types are assigned start from the + /// outermost generic context and works its way inward. + /// + /// struct ABC { + /// struct XYZ {} + /// } + /// + /// Effectively `XYZ` has 4 generic arguments needed to fully realize it. + /// Given the following list of types: `[Int, String, Double, Array]`, + /// we start with the outermost type, `ABC`, and assign the following generic + /// arguments: `` and lookup the `Equatable` conformance + /// for `String`. Now for `XYZ`, we finish the generic assignment with the + /// following: `>` and lookup `Double`'s + /// `Hashable`. + /// + /// - Parameters: + /// - types: A variadic list of `Any.Type` instances used when making this + /// `PartialType`'s realized `Type`. + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable - public func create(with arg0: Type, _ arg1: Type, _ arg2: Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 3 else { - return nil - } - - return Type(descriptor.accessor( - .complete, - arg0.metadata, - arg1.metadata, - arg2.metadata - )) + public func create(with args: any Any.Type...) -> Type? { + create(with: args) } - + + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// Takes a list of types to use when making the full `Type` instance. It + /// expects the exact same number of generic arguments that is needed for + /// creation. Note that all of the provided types must fulfill the + /// requirements of the type's generic signature. In the following example, + /// `Dictionary` requires that its `Key` argument must conform to `Hashable`. + /// If the provided `Key` argument in the list of types does not conform to + /// `Hashable`, this operation will fail and return a `nil`. + /// + /// struct Dictionary {} + /// + /// let dictTy = Type([String: String].self) + /// // Optional(Dictionary) + /// print(dictTy.partial?.create(with: [String.self, Int.self])) + /// + /// The order in which the provided types are assigned start from the + /// outermost generic context and works its way inward. + /// + /// struct ABC { + /// struct XYZ {} + /// } + /// + /// Effectively `XYZ` has 4 generic arguments needed to fully realize it. + /// Given the following list of types: `[Int, String, Double, Array]`, + /// we start with the outermost type, `ABC`, and assign the following generic + /// arguments: `` and lookup the `Equatable` conformance + /// for `String`. Now for `XYZ`, we finish the generic assignment with the + /// following: `>` and lookup `Double`'s + /// `Hashable`. + /// + /// - Parameters: + /// - types: An array of `Any.Type` instances used when making this + /// `PartialType`'s realized `Type`. + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable - public func create(with args: Type...) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == args.count else { - return nil - } - - let metadataArgs = args.map { - $0.metadata + public func create(with args: [any Any.Type]) -> Type? { + args.withUnsafeBufferPointer { + let buffer = UnsafeBufferPointer( + start: UnsafePointer( + $0.baseAddress.unsafelyUnwrapped._rawValue + ), + count: $0.count + ) + + return _create(with: buffer) } - - return Type(descriptor.accessor(.complete, metadataArgs)) } -} -@available(SwiftStdlib 5.9, *) -extension PartialType { - @inlinable - public func create(with arg: Any.Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 1 else { - return nil + @available(SwiftStdlib 5.9, *) + @usableFromInline + internal func _create( + with args: UnsafeBufferPointer + ) -> Type? { + // If a descriptor doesn't have a generic signature, it itself is not + // generic. Thus, we have the 0 argument case, so just call the accessor + // with no arguments if we were passed no arguments. + guard let genericSig = descriptor.genericSignature else { + guard args.count == 0 else { + return nil + } + + return Type(descriptor.accessor(.complete)) } - - return Type(descriptor.accessor(.complete, Metadata(arg))) - } - - @inlinable - public func create(with arg0: Any.Type, _ arg1: Any.Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 2 else { + + // Otherwise, this type is generic. + + // Gather the number of key parameters defined by the actual parameters in a + // generic signature. + var numberOfParameterKeyArguments = 0 + + for parameter in genericSig.parameters { + if parameter.hasKeyArgument { + numberOfParameterKeyArguments += 1 + } + } + + // Make sure the number of arguments we were given is equal to the number of + // parameter key arguments in our generic signature. + guard numberOfParameterKeyArguments == args.count else { return nil } - - return Type(descriptor.accessor(.complete, Metadata(arg0), Metadata(arg1))) + + // If we don't have any parameter key arguments, then we're done and can + // call the accessor with nothing. + guard numberOfParameterKeyArguments > 0 else { + return Type(descriptor.accessor(.complete)) + } + + // If we don't have requirements to deal with, just do the simple thing and + // call the accessor with just our metadata arguments. Otherwise, we need to + // ensure our arguments conform to all their respective protocols, layouts, + // or same type requirements. + guard genericSig.requirements.count > 0 else { + return createNoRequirements(with: args) + } + + return createRequirements(with: args, genericSig) } - - @inlinable - public func create( - with arg0: Any.Type, - _ arg1: Any.Type, - _ arg2: Any.Type + + @available(SwiftStdlib 5.9, *) + internal func createNoRequirements( + with args: UnsafeBufferPointer ) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 3 else { - return nil + switch args.count { + case 1: + return Type(descriptor.accessor(.complete, args[0])) + case 2: + return Type(descriptor.accessor(.complete, args[0], args[1])) + case 3: + return Type(descriptor.accessor(.complete, args[0], args[1], args[2])) + default: + return Type(descriptor.accessor(.complete, args)) } - - return Type(descriptor.accessor( - .complete, - Metadata(arg0), - Metadata(arg1), - Metadata(arg2) - )) } - - @inlinable - public func create(with args: Any.Type...) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == args.count else { - return nil + + @available(SwiftStdlib 5.9, *) + internal func createRequirements( + with args: UnsafeBufferPointer, + _ genericSig: GenericSignature + ) -> Type? { + var keyArguments = Array(args) + let argPtr = UnsafeRawPointer(args.baseAddress.unsafelyUnwrapped) + + for req in genericSig.requirements { + switch req.flags.kind { + // There are 3 kinds of same type requirements: + // + // 1. Concrete type like 'T == String' + // 2. Associated type same type constraints. E.g. 'T.Element == X' + // 3. Same type constraints to other generic parameters. E.g. 'T == U' + // + // The first case should have been handled before we got here because + // those are purely syntactical parameters at that point. However, we must + // still check the 2nd case. The 3rd case is also somewhat syntactical, + // but we still have a key argument in that case which is why it makes + // its way down here. + case .sameType: + if !req.checkSameType(in: descriptor.base, with: argPtr) { + return nil + } + + // Protocol conformance requirement like 'T: P'. + case .protocol: + // Ensure that the passed parameters conform to their respectful + // protocols and pass the witness table + guard let witnessTable = req.checkProtocolConformance( + in: descriptor.base, + with: argPtr + ) else { + return nil + } + + // If this requirement doesn't introduce a key argument, don't append + // the found witness table to our arguments. Not sure if this is + // possible, but let's be defensive here. + guard req.flags.hasKeyArgument else { + continue + } + + keyArguments.append(witnessTable.ptr) + + // Check that the appropriate argument is a subclass of the required base + // class. + case .baseClass: + if !req.checkBaseClass(in: descriptor.base, with: argPtr) { + return nil + } + + // The only currently legal layout constraint is 'AnyObject'. Ensure that + // whatever argument is supposed to bind to this constraint is in fact + // an object. + case .layout: + if !req.checkLayout(in: descriptor.base, with: argPtr) { + return nil + } + + // Same conformance requirements are currently not emitted, so it's safe + // to skip this kind. + case .sameConformance: + continue + + // If we have a requirement that we don't know about, just be defensive + // and return nil. + default: + return nil + } } - - let metadataArgs = args.map { - Metadata($0) + + switch keyArguments.count { + case 1: + return Type(descriptor.accessor(.complete, keyArguments[0])) + case 2: + return Type(descriptor.accessor( + .complete, + keyArguments[0], + keyArguments[1] + )) + case 3: + return Type(descriptor.accessor( + .complete, + keyArguments[0], + keyArguments[1], + keyArguments[2] + )) + default: + return Type(descriptor.accessor(.complete, keyArguments)) } - - return Type(descriptor.accessor(.complete, metadataArgs)) } } diff --git a/stdlib/public/Reflection/Sources/Reflection/Type.swift b/stdlib/public/Reflection/Sources/Reflection/Type.swift index e15e25088d3d7..489df2bf62f4f 100644 --- a/stdlib/public/Reflection/Sources/Reflection/Type.swift +++ b/stdlib/public/Reflection/Sources/Reflection/Type.swift @@ -65,8 +65,8 @@ extension Type { @available(SwiftStdlib 5.9, *) extension Type { @inlinable - public var swiftType: Any.Type { - unsafeBitCast(metadata) + public var swiftType: any Any.Type { + unsafeBitCast(metadata, to: Any.Type.self) } } @@ -117,7 +117,7 @@ extension Type { extension Type: CustomStringConvertible { @inlinable public var description: String { - _typeName(unsafeBitCast(metadata.ptr), qualified: false) + _typeName(swiftType, qualified: false) } } diff --git a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ContextDescriptorValues.swift b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ContextDescriptorValues.swift index a3903eb918766..c37dd8a05f109 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ContextDescriptorValues.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ContextDescriptorValues.swift @@ -441,6 +441,41 @@ extension FieldDescriptor.Element { } } +@available(SwiftStdlib 5.9, *) +extension GenericSignature { + @available(SwiftStdlib 5.9, *) + @frozen + public struct LayoutKind { + @usableFromInline + let value: UInt32 + + @available(SwiftStdlib 5.9, *) + @inlinable + init(_ value: UInt32) { + self.value = value + } + + @available(SwiftStdlib 5.9, *) + @inline(__always) + @inlinable + public static var `class`: LayoutKind { + LayoutKind(0x0) + } + } +} + +@available(SwiftStdlib 5.9, *) +extension GenericSignature.LayoutKind: Equatable { + @available(SwiftStdlib 5.9, *) + @inlinable + public static func ==( + lhs: GenericSignature.LayoutKind, + rhs: GenericSignature.LayoutKind + ) -> Bool { + lhs.value == rhs.value + } +} + @available(SwiftStdlib 5.9, *) extension GenericSignature { @frozen @@ -542,7 +577,22 @@ extension GenericSignature.RequirementDescriptor { Kind(value: 0x1F) } } - +} + +@available(SwiftStdlib 5.9, *) +extension GenericSignature.RequirementDescriptor.Kind: Equatable { + @available(SwiftStdlib 5.9, *) + @inlinable + public static func ==( + lhs: GenericSignature.RequirementDescriptor.Kind, + rhs: GenericSignature.RequirementDescriptor.Kind + ) -> Bool { + lhs.value == rhs.value + } +} + +@available(SwiftStdlib 5.9, *) +extension GenericSignature.RequirementDescriptor { @frozen public struct Flags { @usableFromInline diff --git a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift index 60bfe29a52ed3..9626905195de4 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift @@ -17,7 +17,7 @@ public struct GenericSignature { public let header: Header public let parameters: BufferView public let requirements: IndirectBufferView - + @inlinable init( _ header: Header, @@ -41,15 +41,15 @@ extension GenericSignature { numberOfKeyArguments: UInt16, numberOfExtraArguments: UInt16 ) - + @usableFromInline let storage: Storage - + @inlinable public var numberOfParameters: Int { Int(truncatingIfNeeded: storage.numberOfParameters) } - + public var numberOfRequirements: Int { Int(truncatingIfNeeded: storage.numberOfRequirements) } @@ -66,14 +66,15 @@ extension GenericSignature { // This field is a union which represents the type of requirement // that this parameter is constrained to. It is represented by the following: // 1. Same type requirement (RelativeDirectPointer) - // 2. Protocol requirement (RelativeIndirectablePointerIntPair) - // 3. Conformance requirement (RelativeIndirectablePointer) - // 4. Layout requirement (LayoutKind) + // 2. Base class requirement (RelativeDirectPointer) + // 3. Protocol requirement (RelativeIndirectablePointerIntPair) + // 4. Conformance requirement (RelativeIndirectablePointer) + // 5. Layout requirement (LayoutKind) requirement: Int32 ) - + public let ptr: UnsafeRawPointer - + @inlinable public init(_ ptr: UnsafeRawPointer) { self.ptr = ptr @@ -81,25 +82,207 @@ extension GenericSignature { } } +@available(SwiftStdlib 5.9, *) +extension GenericSignature.RequirementDescriptor { + @available(SwiftStdlib 5.9, *) + @inlinable + public var flags: Flags { + layout.flags + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var parameter: MangledTypeReference { + MangledTypeReference(address(for: \.parameter)) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var `protocol`: ProtocolDescriptor { + let addr = address(for: \.requirement) + .relativeIndirectablePointerIntPairAddress( + as: ProtocolDescriptor.self, + and: UInt8.self + ) + + return ProtocolDescriptor(addr) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var sameType: MangledTypeReference { + MangledTypeReference( + address(for: \.requirement).relativeDirectAddress(as: CChar.self) + ) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var layoutKind: GenericSignature.LayoutKind { + address(for: \.requirement).loadUnaligned( + as: GenericSignature.LayoutKind.self + ) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var baseClass: MangledTypeReference { + sameType + } +} + +@available(SwiftStdlib 5.9, *) +extension GenericSignature.RequirementDescriptor { + @available(SwiftStdlib 5.9, *) + internal func parameterType( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> Any.Type? { + _getTypeByMangledNameInContext( + UnsafePointer(parameter.ptr._rawValue), + UInt(parameter.length), + genericContext: context.ptr, + genericArguments: argPtr + ) + } + + @available(SwiftStdlib 5.9, *) + public func checkProtocolConformance( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> WitnessTable? { + guard flags.kind == .protocol else { + return nil + } + + guard let parameterTy = parameterType(in: context, with: argPtr) else { + return nil + } + + return swift_conformsToProtocol(Metadata(parameterTy), `protocol`) + } + + // This function handles 2 cases: + // + // 1. Associated type same type constraints. E.g. 'T.Element == X' + // 2. Same type constraints to other generic parameters. E.g. 'T == U' + // + // In the first case, we will have both a parameter type and same type type + // be resolved. Simply check that they are the same and move on. For the + // second case, either the parameter type or the same type type will not be + // resolved because this type of constraint makes one of the parameters + // non-key. For example, 'T == U' this constraint causes 'U' to be a non-key + // argument, but 'T' still is however. In the same vein, 'U == [T]' causes + // 'U' to be our key argument now. + // + // Same type constraints that look like 'T == String' cause 'T' to be a purely + // syntactical generic parameter, thus it is resolved ealier in the stack and + // not provided to our argument pointer. + @available(SwiftStdlib 5.9, *) + public func checkSameType( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> Bool { + guard flags.kind == .sameType else { + return false + } + + guard let parameterTy = parameterType(in: context, with: argPtr) else { + // Because of the 2nd case, there might not be a resolved parameter type + // or same type type, so we have to return true to indicate that there is + // no constraint to solve. + return true + } + + let sameTypeTy = _getTypeByMangledNameInContext( + UnsafePointer(sameType.ptr._rawValue), + UInt(sameType.length), + genericContext: context.ptr, + genericArguments: argPtr + ) + + guard let sameTypeTy = sameTypeTy else { + // Because of the 2nd case, there might not be a resolved parameter type + // or same type type, so we have to return true to indicate that there is + // no constraint to solve. + return true + } + + return parameterTy == sameTypeTy + } + + @available(SwiftStdlib 5.9, *) + public func checkLayout( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> Bool { + guard flags.kind == .layout else { + return false + } + + guard let parameterTy = parameterType(in: context, with: argPtr) else { + return false + } + + switch layoutKind { + case .class: + return Metadata(parameterTy).isAnyClass + + // There is currently only class layouts supported, but in case we somehow + // find something else return false. + default: + return false + } + } + + @available(SwiftStdlib 5.9, *) + public func checkBaseClass( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> Bool { + guard flags.kind == .baseClass else { + return false + } + + guard let parameterTy = parameterType(in: context, with: argPtr), + Metadata(parameterTy).isAnyClass else { + return false + } + + let baseClassTy = _getTypeByMangledNameInContext( + UnsafePointer(baseClass.ptr._rawValue), + UInt(baseClass.length), + genericContext: context.ptr, + genericArguments: argPtr + ) + + guard let baseClassTy = baseClassTy else { + return false + } + + return _isSubclass(Metadata(parameterTy), Metadata(baseClassTy)) + } +} + @available(SwiftStdlib 5.9, *) @inlinable func getGenericSignature(at address: UnsafeRawPointer) -> GenericSignature { var address = address - + let header = address.loadUnaligned(as: GenericSignature.Header.self) address += MemoryLayout.size - + let parameters = BufferView( start: address, count: header.numberOfParameters ) // This accounts for padding address += (-header.numberOfParameters & 0x3) + header.numberOfParameters - + let requirements = IndirectBufferView( start: address, count: header.numberOfRequirements ) - + return GenericSignature(header, parameters, requirements) } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Functions.swift b/stdlib/public/Reflection/Sources/_Runtime/Functions.swift index a7714a705ba39..ee360a6e2a63c 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Functions.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Functions.swift @@ -38,6 +38,10 @@ public struct BoxPair { @_silgen_name("swift_allocBox") public func swift_allocBox(_: Metadata) -> BoxPair +@available(SwiftStdlib 5.9, *) +@_silgen_name("_swift_class_isSubclass") +internal func _isSubclass(_: Metadata, _: Metadata) -> Bool + @available(SwiftStdlib 5.9, *) @inlinable public func swift_conformsToProtocol( diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/ClassMetadata.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/ClassMetadata.swift index f85d024a2d2bd..e3fa66dfb2874 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/ClassMetadata.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/ClassMetadata.swift @@ -14,7 +14,7 @@ import Swift @available(SwiftStdlib 5.9, *) @frozen public struct ClassMetadata: PublicLayout { -#if canImport(ObjectiveC) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) public typealias Layout = ( base: Metadata.Layout, superclass: Metadata?, diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/Metadata.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/Metadata.swift index 966d27adac2cd..8e39780390bec 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/Metadata.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/Metadata.swift @@ -85,6 +85,23 @@ extension Metadata { } } +@available(SwiftStdlib 5.9, *) +extension Metadata { + @available(SwiftStdlib 5.9, *) + internal var isAnyClass: Bool { + switch kind { + case .class, + .objcClassWrapper, + .foreignClass, + .foreignReferenceType: + return true + + default: + return false + } + } +} + //===----------------------------------------------------------------------===// // Stdlib conformances //===----------------------------------------------------------------------===// diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/MetadataAccessFunction.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/MetadataAccessFunction.swift index 8efc8881ce470..ea3dbd2d8c0f3 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/MetadataAccessFunction.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/MetadataAccessFunction.swift @@ -13,98 +13,193 @@ import Swift @available(SwiftStdlib 5.9, *) extension Metadata { + @available(SwiftStdlib 5.9, *) @frozen public struct AccessFunction { @usableFromInline let ptr: UnsafeRawPointer - + + @available(SwiftStdlib 5.9, *) @inlinable init(_ ptr: UnsafeRawPointer) { self.ptr = ptr } - - // MARK: Access Function 0 Args - + +//===----------------------------------------------------------------------===// +// 0 Arguments +//===----------------------------------------------------------------------===// + @usableFromInline typealias AccessFn0 = @convention(thin) ( Request ) -> Response - + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction(_ request: Request) -> Metadata { let fn = unsafeBitCast(ptr, to: AccessFn0.self) - + return fn(request).metadata } - - // MARK: Access Function 1 Arg - + +//===----------------------------------------------------------------------===// +// 1 Argument +//===----------------------------------------------------------------------===// + @usableFromInline typealias AccessFn1 = @convention(thin) ( Request, - Metadata + UnsafeRawPointer ) -> Response - + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: any Any.Type + ) -> Metadata { + self(request, Metadata(arg0)) + } + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction( _ request: Request, _ arg0: Metadata + ) -> Metadata { + self(request, arg0.ptr) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: UnsafeRawPointer ) -> Metadata { let fn = unsafeBitCast(ptr, to: AccessFn1.self) - + return fn(request, arg0).metadata } - - // MARK: Access Function 2 Args - + +//===----------------------------------------------------------------------===// +// 2 Arguments +//===----------------------------------------------------------------------===// + @usableFromInline typealias AccessFn2 = @convention(thin) ( Request, - Metadata, - Metadata + UnsafeRawPointer, + UnsafeRawPointer ) -> Response - + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: any Any.Type, + _ arg1: any Any.Type + ) -> Metadata { + self(request, Metadata(arg0), Metadata(arg1)) + } + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction( _ request: Request, _ arg0: Metadata, _ arg1: Metadata + ) -> Metadata { + self(request, arg0.ptr, arg1.ptr) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: UnsafeRawPointer, + _ arg1: UnsafeRawPointer ) -> Metadata { let fn = unsafeBitCast(ptr, to: AccessFn2.self) - + return fn(request, arg0, arg1).metadata } - - // MARK: Access Function 3 Args - + +//===----------------------------------------------------------------------===// +// 3 Arguments +//===----------------------------------------------------------------------===// + @usableFromInline typealias AccessFn3 = @convention(thin) ( Request, - Metadata, - Metadata, - Metadata + UnsafeRawPointer, + UnsafeRawPointer, + UnsafeRawPointer ) -> Response - + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: any Any.Type, + _ arg1: any Any.Type, + _ arg2: any Any.Type + ) -> Metadata { + self(request, Metadata(arg0), Metadata(arg1), Metadata(arg2)) + } + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction( _ request: Request, _ arg0: Metadata, _ arg1: Metadata, _ arg2: Metadata + ) -> Metadata { + self(request, arg0.ptr, arg1.ptr, arg2.ptr) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: UnsafeRawPointer, + _ arg1: UnsafeRawPointer, + _ arg2: UnsafeRawPointer ) -> Metadata { let fn = unsafeBitCast(ptr, to: AccessFn3.self) - + return fn(request, arg0, arg1, arg2).metadata } - - // MARK: Access Function Many Args + +//===----------------------------------------------------------------------===// +// Many Arguments +//===----------------------------------------------------------------------===// @usableFromInline typealias AccessFnMany = @convention(thin) ( Request, - UnsafePointer + UnsafePointer ) -> Response - + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ args: [any Any.Type] + ) -> Metadata { + let fn = unsafeBitCast(ptr, to: AccessFnMany.self) + + return args.withUnsafeBufferPointer { + fn( + request, + UnsafePointer( + $0.baseAddress.unsafelyUnwrapped._rawValue + ) + ).metadata + } + } + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction( _ request: Request, @@ -113,8 +208,35 @@ extension Metadata { let fn = unsafeBitCast(ptr, to: AccessFnMany.self) return args.withUnsafeBufferPointer { - fn(request, $0.baseAddress.unsafelyUnwrapped).metadata + fn( + request, + UnsafePointer( + $0.baseAddress.unsafelyUnwrapped._rawValue + ) + ).metadata } } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ args: [UnsafeRawPointer] + ) -> Metadata { + args.withUnsafeBufferPointer { + self(request, $0) + } + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ args: UnsafeBufferPointer + ) -> Metadata { + let fn = unsafeBitCast(ptr, to: AccessFnMany.self) + + return fn(request, args.baseAddress.unsafelyUnwrapped).metadata + } } } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift index b515f41c420f7..2a9ef02330e52 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift @@ -25,6 +25,13 @@ public struct MangledTypeReference { @available(SwiftStdlib 5.9, *) extension MangledTypeReference { + @available(SwiftStdlib 5.9, *) + @inlinable + public var length: Int { + getSymbolicMangledNameLength(ptr) + } + + @available(SwiftStdlib 5.9, *) @inlinable var standardSubstitution: Any.Type? { let byte1 = ptr.loadUnaligned(fromByteOffset: 1, as: UInt8.self) diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift index de4b66dde8313..436fffe898b84 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift @@ -11,35 +11,41 @@ import Swift -@available(SwiftStdlib 5.9, *) -@inlinable -public func unsafeBitCast(_ x: T, to type: U.Type = U.self) -> U { +@_alwaysEmitIntoClient +func unsafeBitCast(_ x: T, to type: U.Type = U.self) -> U { Swift.unsafeBitCast(x, to: type) } extension UnsafePointer { - @inlinable + @_alwaysEmitIntoClient var raw: UnsafeRawPointer { UnsafeRawPointer(self) } } extension UnsafeMutablePointer { - @inlinable + @_alwaysEmitIntoClient var raw: UnsafeMutableRawPointer { UnsafeMutableRawPointer(self) } } +extension UnsafeBufferPointer { + @_alwaysEmitIntoClient + var raw: UnsafeRawBufferPointer { + UnsafeRawBufferPointer(self) + } +} + extension UnsafeRawPointer { - @inlinable + @_alwaysEmitIntoClient var bitPattern: UInt64 { UInt64(truncatingIfNeeded: UInt(bitPattern: self)) } } extension UInt64 { - @inlinable + @_alwaysEmitIntoClient var rawPointer: UnsafeRawPointer { let pointer = UnsafeRawPointer(bitPattern: UInt(truncatingIfNeeded: self)) @@ -48,15 +54,14 @@ extension UInt64 { } extension UnsafeRawPointer { - @inlinable + @_alwaysEmitIntoClient var mutable: UnsafeMutableRawPointer { UnsafeMutableRawPointer(mutating: self) } } extension UnsafeRawPointer { - @inlinable - @inline(__always) + @_alwaysEmitIntoClient var binaryString: String { // let length = strlen(UnsafePointer(_rawValue)) // @@ -71,12 +76,13 @@ extension UnsafeRawPointer { } extension UnsafeRawPointer { - @inlinable + @_alwaysEmitIntoClient func offset(of count: Int) -> UnsafeRawPointer { advanced(by: count * MemoryLayout.size) } } +@_alwaysEmitIntoClient func getSymbolicMangledNameLength(_ base: UnsafeRawPointer) -> Int { var end = base while let current = Optional(end.load(as: UInt8.self)), current != 0 { diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift index e08642a80e153..c6cebeecca055 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift @@ -55,3 +55,18 @@ public struct RelativeIndirectablePointerIntPair< } } } + +extension UnsafeRawPointer { + @available(SwiftStdlib 5.9, *) + @inlinable + public func relativeIndirectablePointerIntPairAddress( + as type: T.Type, + and type2: U.Type + ) -> UnsafeRawPointer { + let relativePointer = RelativeIndirectablePointerIntPair( + offset: loadUnaligned(as: Int32.self) + ) + + return relativePointer.address(from: self) + } +} diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 36eb28cdb3791..571cc7be884b7 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -1269,6 +1269,13 @@ static bool isSubclass(const Metadata *subclass, const Metadata *superclass) { }); } +SWIFT_CC(swift) +SWIFT_RUNTIME_STDLIB_SPI +bool swift::_swift_class_isSubclass(const Metadata *subclass, + const Metadata *superclass) { + return isSubclass(subclass, superclass); +} + llvm::Optional swift::_checkGenericRequirements( llvm::ArrayRef requirements, llvm::SmallVectorImpl &extraArguments, diff --git a/test/stdlib/Reflection/PartialType.swift b/test/stdlib/Reflection/PartialType.swift new file mode 100644 index 0000000000000..1a99e915d818d --- /dev/null +++ b/test/stdlib/Reflection/PartialType.swift @@ -0,0 +1,225 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest +import _Runtime +import Reflection + +let suite = TestSuite("PartialType") + +//===----------------------------------------------------------------------===// +// Basic +//===----------------------------------------------------------------------===// + +struct TypeWhoDoesNotConformToHashable {} + +if #available(SwiftStdlib 5.9, *) { + suite.test("basic creation") { + let intPTy = Type(Int.self).partial! + let int = intPTy.create() + expectNotNil(int) + expectEqual(int!.swiftType, Int.self) + + let arrPTy = Type([Void].self).partial! + let intArr = arrPTy.create(with: Int.self) + expectNotNil(intArr) + expectEqual(intArr!.swiftType, [Int].self) + + let dictPTy = Type([AnyHashable: Any].self).partial! + let goodDict = dictPTy.create(with: String.self, Int.self) + expectNotNil(goodDict) + expectEqual(goodDict!.swiftType, [String: Int].self) + + let badDict = dictPTy.create(with: TypeWhoDoesNotConformToHashable.self, Int.self) + expectNil(badDict) + } +} + +//===----------------------------------------------------------------------===// +// Protocol conformance constraints +//===----------------------------------------------------------------------===// + +protocol MyProto {} + +extension Int: MyProto {} +extension Bool: MyProto {} + +struct ProtoGeneric {} + +struct ProtoGeneric2 where T.Element: Hashable {} + +if #available(SwiftStdlib 5.9, *) { + suite.test("protocol conformance constraint creation") { + let protoGenericPTy = Type(ProtoGeneric.self).partial! + + let a = protoGenericPTy.create(with: Int.self) + expectNotNil(a) + expectEqual(a!.swiftType, ProtoGeneric.self) + + let b = protoGenericPTy.create(with: String.self) + expectNil(b) + + let protoGeneric2PTy = Type(ProtoGeneric2<[String]>.self).partial! + + let c = protoGeneric2PTy.create(with: [Int].self) + expectNotNil(c) + expectEqual(c!.swiftType, ProtoGeneric2<[Int]>.self) + + let d = protoGeneric2PTy.create(with: [TypeWhoDoesNotConformToHashable].self) + expectNil(d) + } +} + +//===----------------------------------------------------------------------===// +// Same type constraints +//===----------------------------------------------------------------------===// + +struct SameTypeGeneric1 {} + +extension SameTypeGeneric1 where T == String { + struct Nested {} +} + +struct SameTypeGeneric2 where T.Element == Int {} + +struct SameTypeGeneric3 {} + +extension SameTypeGeneric3 where T == U? { + struct Nested {} +} + +if #available(SwiftStdlib 5.9, *) { + suite.test("same type constraint creation") { + // 1. Concrete same types + + let nestedPTy = Type(SameTypeGeneric1.Nested.self).partial! + + let a = nestedPTy.create() + expectNotNil(a) + expectEqual(a!.swiftType, SameTypeGeneric1.Nested.self) + + let b = nestedPTy.create(with: Int.self) + expectNil(b) + + let c = nestedPTy.create(with: String.self) + expectNil(c) + + // 2. Associated type same types + + let stg2PTy = Type(SameTypeGeneric2>.self).partial! + + let d = stg2PTy.create(with: [Int].self) + expectNotNil(d) + expectEqual(d!.swiftType, SameTypeGeneric2<[Int]>.self) + + let e = stg2PTy.create(with: Bool.self) + expectNil(e) + + let f = stg2PTy.create(with: [String].self) + expectNil(f) + + // 3. Generic same types + + let nested2PTy = Type(SameTypeGeneric3.Nested.self).partial! + + let g = nested2PTy.create(with: Int.self) + expectNotNil(g) + expectEqual(g!.swiftType, SameTypeGeneric3.Nested.self) + + let h = nested2PTy.create() + expectNil(h) + + let i = nested2PTy.create(with: Int?.self) + expectNotNil(i) + expectEqual(i!.swiftType, SameTypeGeneric3.Nested.self) + } +} + +//===----------------------------------------------------------------------===// +// Layout constraints +//===----------------------------------------------------------------------===// + +class LayoutClass1 {} +class LayoutClass2 {} + +struct LayoutGeneric {} + +if #available(SwiftStdlib 5.9, *) { + suite.test("layout constraint creation") { + let layoutGenericPTy = Type(LayoutGeneric.self).partial! + + let a = layoutGenericPTy.create(with: Int.self) + expectNil(a) + + let b = layoutGenericPTy.create(with: [Int].self) + expectNil(b) + + let c = layoutGenericPTy.create() + expectNil(c) + + let d = layoutGenericPTy.create(with: LayoutClass2.self) + expectNotNil(d) + expectEqual(d!.swiftType, LayoutGeneric.self) + } +} + +//===----------------------------------------------------------------------===// +// Base class constraints +//===----------------------------------------------------------------------===// + +class UnrelatedClass {} + +class BaseClass1 {} +class SubClass1: BaseClass1 {} +struct BaseClassGeneric1 {} + +class BaseClass2 {} +class SubClass2: BaseClass2 {} +struct BaseClassGeneric2> {} +struct Weird {} + +extension Weird where T: Equatable { + class SubSubClass2: BaseClass2 {} +} + +if #available(SwiftStdlib 5.9, *) { + suite.test("base class constraint creation") { + let one = Type(BaseClassGeneric1.self).partial! + + let a = one.create(with: Int.self) + expectNil(a) + + let b = one.create(with: SubClass1.self) + expectNotNil(b) + expectEqual(b!.swiftType, BaseClassGeneric1.self) + + let c = one.create(with: BaseClass1.self) + expectNotNil(c) + expectEqual(c!.swiftType, BaseClassGeneric1.self) + + let two = Type(BaseClassGeneric2>.self).partial! + + let d = two.create(with: Int.self) + expectNil(d) + + let e = two.create(with: BaseClass2.self) + expectNil(e) + + let f = two.create(with: BaseClass2.self) + expectNotNil(f) + expectEqual(f!.swiftType, BaseClassGeneric2>.self) + + let g = two.create(with: SubClass2.self) + expectNotNil(g) + expectEqual(g!.swiftType, BaseClassGeneric2.self) + + let h = two.create(with: Weird.SubSubClass2.self) + expectNil(h) + + let i = two.create(with: Weird.SubSubClass2.self) + expectNotNil(i) + expectEqual(i!.swiftType, BaseClassGeneric2.SubSubClass2>.self) + } +} + +runAllTests()