diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index e53deac6390e4..763469260fb69 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -1046,6 +1046,18 @@ void swift_initRawStructMetadata(StructMetadata *self, const TypeLayout *likeType, int32_t count); +/// Check if the given generic arguments are valid inputs for the generic type +/// context and if so call the metadata access function and return the metadata. +/// +/// Note: This expects the caller to heap allocate all pack pointers within the +/// generic arguments via 'swift_allocateMetadataPack'. +SWIFT_RUNTIME_STDLIB_SPI +SWIFT_CC(swift) +const Metadata *_swift_instantiateCheckedGenericMetadata( + const TypeContextDescriptor *context, + const void * const *genericArgs, + size_t genericArgsSize); + #pragma clang diagnostic pop } // end namespace swift diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 6c63fccd8cd25..32232a3860207 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -2830,6 +2830,41 @@ swift_getOpaqueTypeConformance(const void * const *arguments, arguments, static_cast(descriptor), index); } +SWIFT_RUNTIME_STDLIB_SPI +SWIFT_CC(swift) +const Metadata *swift::_swift_instantiateCheckedGenericMetadata( + const TypeContextDescriptor *context, + const void * const *genericArgs, + size_t genericArgsSize) { + context = swift_auth_data_non_address( + context, SpecialPointerAuthDiscriminators::ContextDescriptor); + + if (!context->isGeneric()) { + return nullptr; + } + + DemanglerForRuntimeTypeResolution> demangler; + + llvm::ArrayRef genericArgsRef( + reinterpret_cast(genericArgs), genericArgsSize); + llvm::SmallVector genericParamCounts; + llvm::SmallVector allGenericArgs; + + auto result = _gatherGenericParameters(context, genericArgsRef, + /* parent */ nullptr, + genericParamCounts, allGenericArgs, + demangler); + + // _gatherGenericParameters returns llvm::None on success. + if (result.hasValue()) { + return nullptr; + } + + auto accessFunction = context->getAccessFunction(); + + return accessFunction(MetadataState::Complete, allGenericArgs).Value; +} + #if SWIFT_OBJC_INTEROP // Return the ObjC class for the given type name. diff --git a/test/Runtime/check_create_type.swift b/test/Runtime/check_create_type.swift new file mode 100644 index 0000000000000..147a36249de5a --- /dev/null +++ b/test/Runtime/check_create_type.swift @@ -0,0 +1,129 @@ +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking) +// REQUIRES: executable_test + +// UNSUPPORTED: CPU=arm64e +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import StdlibUnittest + +let testSuite = TestSuite("CheckedCreateType") + +struct Variadic { + struct Nested {} +} + +@_silgen_name("swift_allocateMetadataPack") +func allocateMetadataPack( + _ packPointer: UnsafeRawPointer, + _ packCount: UInt +) -> UnsafeRawPointer + +@_silgen_name("_swift_instantiateCheckedGenericMetadata") +func _instantiateCheckedGenericMetadata( + _ descriptor: UnsafeRawPointer, + _ genericArgs: UnsafeRawPointer, + _ genericArgsSize: UInt +) -> Any.Type? + +func metaPointer(_ x: Any.Type) -> UnsafeRawPointer { + unsafeBitCast(x, to: UnsafeRawPointer.self) +} + +testSuite.test("_swift_checkedCreateType non-variadic") { + let dictMeta = unsafeBitCast( + [Int: Int].self as Any.Type, + to: UnsafeRawPointer.self + ) + let dictDesc = dictMeta.load( + fromByteOffset: MemoryLayout.size, + as: UnsafeRawPointer.self + ) + + let dictGenericArgs: [Any.Type] = [String.self, Double.self] + + dictGenericArgs.withUnsafeBufferPointer { + let newDict = _instantiateCheckedGenericMetadata( + dictDesc, + UnsafeRawPointer($0.baseAddress!), + UInt($0.count) + ) + + expectTrue(newDict == [String: Double].self) + } +} + +testSuite.test("_swift_checkedCreateType variadic") { + let variMeta = unsafeBitCast( + Variadic< >.self as Any.Type, + to: UnsafeRawPointer.self + ) + let variDesc = variMeta.load( + fromByteOffset: MemoryLayout.size, + as: UnsafeRawPointer.self + ) + + let variPack: [Any.Type] = [Int.self, Int8.self, UInt8.self] + + variPack.withUnsafeBufferPointer { pack in + let packPointer = allocateMetadataPack( + UnsafeRawPointer(pack.baseAddress!), + UInt(pack.count) + ) + let genericArgs = [packPointer] + + genericArgs.withUnsafeBufferPointer { genericArgs in + let newVari = _instantiateCheckedGenericMetadata( + variDesc, + UnsafeRawPointer(genericArgs.baseAddress!), + UInt(genericArgs.count) + ) + + expectTrue(newVari == Variadic.self) + } + } +} + +testSuite.test("_swift_checkedCreateType variadic nested with requirements") { + let nestedMeta = unsafeBitCast( + Variadic< >.Nested<()>.self as Any.Type, + to: UnsafeRawPointer.self + ) + let nestedDesc = nestedMeta.load( + fromByteOffset: MemoryLayout.size, + as: UnsafeRawPointer.self + ) + + let variPack: [Any.Type] = [String.self, [Int].self, UInt64.self] + + let nestedPack: [Any.Type] = [Int.self, Substring.self, Bool.self] + + nestedPack.withUnsafeBufferPointer { nestedPack in + variPack.withUnsafeBufferPointer { variPack in + let nestedGenericArgs = [ + allocateMetadataPack( + UnsafeRawPointer(variPack.baseAddress!), + UInt(variPack.count) + ), + metaPointer(Int16.self), + allocateMetadataPack( + UnsafeRawPointer(nestedPack.baseAddress!), + UInt(nestedPack.count) + ) + ] + + nestedGenericArgs.withUnsafeBufferPointer { nestedGenericArgs in + + let newNested = _instantiateCheckedGenericMetadata( + nestedDesc, + UnsafeRawPointer(nestedGenericArgs.baseAddress!), + UInt(nestedGenericArgs.count) + ) + + expectTrue(newNested == Variadic.Nested.self) + } + } + } +} + +runAllTests()