diff --git a/Sources/Runtime/Metadata/ClassMetadata.swift b/Sources/Runtime/Metadata/ClassMetadata.swift index 18bf831..bc5439f 100644 --- a/Sources/Runtime/Metadata/ClassMetadata.swift +++ b/Sources/Runtime/Metadata/ClassMetadata.swift @@ -28,6 +28,11 @@ struct AnyClassMetadata { pointer = unsafeBitCast(type, to: UnsafeMutablePointer.self) } + @_disfavoredOverload + init(type: T.Type) { + pointer = unsafeBitCast(type, to: UnsafeMutablePointer.self) + } + func asClassMetadata() -> ClassMetadata? { guard pointer.pointee.isSwiftClass else { return nil diff --git a/Sources/Runtime/Metadata/Metadata.swift b/Sources/Runtime/Metadata/Metadata.swift index fc0f8b2..d5ec84b 100644 --- a/Sources/Runtime/Metadata/Metadata.swift +++ b/Sources/Runtime/Metadata/Metadata.swift @@ -20,6 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +@_disfavoredOverload +func metadataPointer(type: T.Type) -> UnsafeMutablePointer { + return unsafeBitCast(type, to: UnsafeMutablePointer.self) +} + func metadataPointer(type: Any.Type) -> UnsafeMutablePointer { return unsafeBitCast(type, to: UnsafeMutablePointer.self) } diff --git a/Sources/Runtime/Metadata/MetadataType.swift b/Sources/Runtime/Metadata/MetadataType.swift index d365cfc..f877798 100644 --- a/Sources/Runtime/Metadata/MetadataType.swift +++ b/Sources/Runtime/Metadata/MetadataType.swift @@ -28,6 +28,9 @@ protocol MetadataInfo { var stride: Int { get } init(type: Any.Type) + + @_disfavoredOverload + init(type: T.Type) } protocol MetadataType: MetadataInfo, TypeInfoConvertible { @@ -45,6 +48,11 @@ extension MetadataType { self = Self(pointer: unsafeBitCast(type, to: UnsafeMutablePointer.self)) } + @_disfavoredOverload + init(type: T.Type) { + self = Self(pointer: unsafeBitCast(type, to: UnsafeMutablePointer.self)) + } + var type: Any.Type { return unsafeBitCast(pointer, to: Any.Type.self) } diff --git a/Sources/Runtime/Models/Errors.swift b/Sources/Runtime/Models/Errors.swift index 5cf9d05..9b2db8b 100644 --- a/Sources/Runtime/Models/Errors.swift +++ b/Sources/Runtime/Models/Errors.swift @@ -26,4 +26,5 @@ enum RuntimeError: Error { case noPropertyNamed(name: String) case unableToBuildType(type: Any.Type) case errorGettingValue(name: String, type: Any.Type) + case unsupportedNoncopyableType } diff --git a/Sources/Runtime/Models/Kind.swift b/Sources/Runtime/Models/Kind.swift index 8b780be..ed9912b 100644 --- a/Sources/Runtime/Models/Kind.swift +++ b/Sources/Runtime/Models/Kind.swift @@ -75,6 +75,12 @@ public enum Kind { self.init(flag: pointer.pointee) } + @_disfavoredOverload + init(type: T.Type) { + let pointer = metadataPointer(type: type) + self.init(flag: pointer.pointee) + } + struct Flags { static let kindIsNonHeap = 0x200 static let kindIsRuntimePrivate = 0x100 diff --git a/Sources/Runtime/Models/TypeInfo.swift b/Sources/Runtime/Models/TypeInfo.swift index 6f78e5c..2b6fab7 100644 --- a/Sources/Runtime/Models/TypeInfo.swift +++ b/Sources/Runtime/Models/TypeInfo.swift @@ -58,6 +58,21 @@ public struct TypeInfo { } } +@_disfavoredOverload +public func typeInfo(of type: T.Type) throws -> TypeInfo { + let kind = Kind(type: type) + var typeInfoConvertible: TypeInfoConvertible + switch kind { + case .struct: + typeInfoConvertible = StructMetadata(type: type) + case .enum, .optional: + typeInfoConvertible = EnumMetadata(type: type) + default: + throw RuntimeError.unsupportedNoncopyableType + } + return typeInfoConvertible.toTypeInfo() +} + public func typeInfo(of type: Any.Type) throws -> TypeInfo { let kind = Kind(type: type) diff --git a/Tests/RuntimeTests/MetadataTests.swift b/Tests/RuntimeTests/MetadataTests.swift index de50d93..7d9c42c 100644 --- a/Tests/RuntimeTests/MetadataTests.swift +++ b/Tests/RuntimeTests/MetadataTests.swift @@ -242,6 +242,33 @@ class MetadataTests: XCTestCase { XCTAssert(info.cases[0].name == "some") XCTAssert(info.cases[1].name == "none") } + + func testNonCopyableStruct() throws { + let info = try typeInfo(of: NoncopyableCountry.self) + let name = try info.property(named: "name") + XCTAssert(name.name == "name") + XCTAssert(name.type == String.self) + let population = try info.property(named: "population") + XCTAssert(population.name == "population") + XCTAssert(population.type == Int.self) + XCTAssert(info.properties.count == 2) + } + + func testNonCopyableEnum() throws { + let info = try typeInfo(of: NoncopyableColor.self) + XCTAssert(info.cases[0].name == "red") + XCTAssert(info.cases[1].name == "blue") + XCTAssert(info.cases.count == 2) + } + + func testNonCopyableOptionals() throws { + let info1 = try typeInfo(of: NoncopyableCountry?.self) + XCTAssert(info1.cases[0].name == "some") + XCTAssert(info1.cases[1].name == "none") + let info2 = try typeInfo(of: NoncopyableColor?.self) + XCTAssert(info2.cases[0].name == "some") + XCTAssert(info2.cases[1].name == "none") + } } fileprivate enum MyEnum: Int { @@ -278,3 +305,13 @@ fileprivate struct MyStruct { var c: String var d: T } + +fileprivate enum NoncopyableColor: ~Copyable { + case red + case blue +} + +fileprivate struct NoncopyableCountry: ~Copyable { + var name: String + var population: Int +}