diff --git a/include/swift/ABI/KeyPath.h b/include/swift/ABI/KeyPath.h index c7f0c46f83a59..4c912462d57be 100644 --- a/include/swift/ABI/KeyPath.h +++ b/include/swift/ABI/KeyPath.h @@ -82,65 +82,78 @@ class KeyPathComponentHeader { offset; } + static constexpr uint32_t isLetBit(bool isLet) { + return isLet ? 0 : _SwiftKeyPathComponentHeader_StoredMutableFlag; + } + public: static constexpr bool offsetCanBeInline(unsigned offset) { return offset <= _SwiftKeyPathComponentHeader_MaximumOffsetPayload; } constexpr static KeyPathComponentHeader - forStructComponentWithInlineOffset(unsigned offset) { + forStructComponentWithInlineOffset(bool isLet, + unsigned offset) { return KeyPathComponentHeader( (_SwiftKeyPathComponentHeader_StructTag - << _SwiftKeyPathComponentHeader_DiscriminatorShift) - | validateInlineOffset(offset)); + << _SwiftKeyPathComponentHeader_DiscriminatorShift) + | validateInlineOffset(offset) + | isLetBit(isLet)); } constexpr static KeyPathComponentHeader - forStructComponentWithOutOfLineOffset() { + forStructComponentWithOutOfLineOffset(bool isLet) { return KeyPathComponentHeader( (_SwiftKeyPathComponentHeader_StructTag - << _SwiftKeyPathComponentHeader_DiscriminatorShift) - | _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload); + << _SwiftKeyPathComponentHeader_DiscriminatorShift) + | _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload + | isLetBit(isLet)); } constexpr static KeyPathComponentHeader - forStructComponentWithUnresolvedFieldOffset() { + forStructComponentWithUnresolvedFieldOffset(bool isLet) { return KeyPathComponentHeader( (_SwiftKeyPathComponentHeader_StructTag - << _SwiftKeyPathComponentHeader_DiscriminatorShift) - | _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload); + << _SwiftKeyPathComponentHeader_DiscriminatorShift) + | _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload + | isLetBit(isLet)); } constexpr static KeyPathComponentHeader - forClassComponentWithInlineOffset(unsigned offset) { + forClassComponentWithInlineOffset(bool isLet, + unsigned offset) { return KeyPathComponentHeader( (_SwiftKeyPathComponentHeader_ClassTag - << _SwiftKeyPathComponentHeader_DiscriminatorShift) - | validateInlineOffset(offset)); + << _SwiftKeyPathComponentHeader_DiscriminatorShift) + | validateInlineOffset(offset) + | isLetBit(isLet)); } constexpr static KeyPathComponentHeader - forClassComponentWithOutOfLineOffset() { + forClassComponentWithOutOfLineOffset(bool isLet) { return KeyPathComponentHeader( (_SwiftKeyPathComponentHeader_ClassTag - << _SwiftKeyPathComponentHeader_DiscriminatorShift) - | _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload); + << _SwiftKeyPathComponentHeader_DiscriminatorShift) + | _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload + | isLetBit(isLet)); } constexpr static KeyPathComponentHeader - forClassComponentWithUnresolvedFieldOffset() { + forClassComponentWithUnresolvedFieldOffset(bool isLet) { return KeyPathComponentHeader( (_SwiftKeyPathComponentHeader_ClassTag - << _SwiftKeyPathComponentHeader_DiscriminatorShift) - | _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload); + << _SwiftKeyPathComponentHeader_DiscriminatorShift) + | _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload + | isLetBit(isLet)); } constexpr static KeyPathComponentHeader - forClassComponentWithUnresolvedIndirectOffset() { + forClassComponentWithUnresolvedIndirectOffset(bool isLet) { return KeyPathComponentHeader( (_SwiftKeyPathComponentHeader_ClassTag - << _SwiftKeyPathComponentHeader_DiscriminatorShift) - | _SwiftKeyPathComponentHeader_UnresolvedIndirectOffsetPayload); + << _SwiftKeyPathComponentHeader_DiscriminatorShift) + | _SwiftKeyPathComponentHeader_UnresolvedIndirectOffsetPayload + | isLetBit(isLet)); } constexpr static KeyPathComponentHeader diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index e7410414f5168..e43b30aa3052a 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -766,6 +766,10 @@ void verifyMangledNameRoundtrip(const Metadata *metadata); SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API const TypeContextDescriptor *swift_getTypeContextDescriptor(const Metadata *type); +// Defined in KeyPath.swift in the standard library. +SWIFT_RUNTIME_EXPORT +const HeapObject *swift_getKeyPath(const void *pattern, const void *arguments); + } // end namespace swift #endif // SWIFT_RUNTIME_METADATA_H diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index fc9c389cfb7e3..322209b038694 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -773,22 +773,23 @@ emitKeyPathComponent(IRGenModule &IGM, case KeyPathPatternComponent::Kind::StoredProperty: { auto property = cast(component.getStoredPropertyDecl()); - auto addFixedOffset = [&](bool isStruct, llvm::Constant *offset) { + auto addFixedOffset = [&](bool isStruct, bool isLet, + llvm::Constant *offset) { if (auto offsetInt = dyn_cast_or_null(offset)) { auto offsetValue = offsetInt->getValue().getZExtValue(); if (KeyPathComponentHeader::offsetCanBeInline(offsetValue)) { auto header = isStruct ? KeyPathComponentHeader - ::forStructComponentWithInlineOffset(offsetValue) + ::forStructComponentWithInlineOffset(isLet, offsetValue) : KeyPathComponentHeader - ::forClassComponentWithInlineOffset(offsetValue); + ::forClassComponentWithInlineOffset(isLet, offsetValue); fields.addInt32(header.getData()); return; } } auto header = isStruct - ? KeyPathComponentHeader::forStructComponentWithOutOfLineOffset() - : KeyPathComponentHeader::forClassComponentWithOutOfLineOffset(); + ? KeyPathComponentHeader::forStructComponentWithOutOfLineOffset(isLet) + : KeyPathComponentHeader::forClassComponentWithOutOfLineOffset(isLet); fields.addInt32(header.getData()); fields.add(llvm::ConstantExpr::getTruncOrBitCast(offset, IGM.Int32Ty)); }; @@ -801,7 +802,7 @@ emitKeyPathComponent(IRGenModule &IGM, loweredBaseTy, property)) { // We have a known constant fixed offset. - addFixedOffset(/*struct*/ true, offset); + addFixedOffset(/*struct*/ true, property->isLet(), offset); break; } @@ -811,7 +812,7 @@ emitKeyPathComponent(IRGenModule &IGM, auto fieldOffset = metadataLayout.getStaticFieldOffset(property); auto header = KeyPathComponentHeader - ::forStructComponentWithUnresolvedFieldOffset(); + ::forStructComponentWithUnresolvedFieldOffset(property->isLet()); fields.addInt32(header.getData()); fields.addInt32(fieldOffset.getValue()); break; @@ -829,14 +830,14 @@ emitKeyPathComponent(IRGenModule &IGM, loweredBaseTy, property); assert(offset && "no constant offset for ConstantDirect field?!"); - addFixedOffset(/*struct*/ false, offset); + addFixedOffset(/*struct*/ false, property->isLet(), offset); break; } case FieldAccess::NonConstantDirect: { // A constant offset that's determined at class realization time. // We have to load the offset from a global ivar. auto header = KeyPathComponentHeader - ::forClassComponentWithUnresolvedIndirectOffset(); + ::forClassComponentWithUnresolvedIndirectOffset(property->isLet()); fields.addInt32(header.getData()); fields.addAlignmentPadding(IGM.getPointerAlignment()); auto offsetVar = IGM.getAddrOfFieldOffset(property, NotForDefinition); @@ -846,8 +847,8 @@ emitKeyPathComponent(IRGenModule &IGM, case FieldAccess::ConstantIndirect: { // An offset that depends on the instance's generic parameterization, // but whose field offset is at a known vtable offset. - auto header = - KeyPathComponentHeader::forClassComponentWithUnresolvedFieldOffset(); + auto header = KeyPathComponentHeader + ::forClassComponentWithUnresolvedFieldOffset(property->isLet()); fields.addInt32(header.getData()); auto fieldOffset = getClassFieldOffsetOffset(IGM, diff --git a/stdlib/public/SwiftShims/KeyPath.h b/stdlib/public/SwiftShims/KeyPath.h index e5b755ae4f5e8..78cc9703fe6cc 100644 --- a/stdlib/public/SwiftShims/KeyPath.h +++ b/stdlib/public/SwiftShims/KeyPath.h @@ -59,17 +59,23 @@ static const __swift_uint32_t _SwiftKeyPathComponentHeader_ExternalTag static const __swift_uint32_t _SwiftKeyPathComponentHeader_TrivialPropertyDescriptorMarker = 0U; +static const __swift_uint32_t _SwiftKeyPathComponentHeader_StoredOffsetPayloadMask + = 0x007FFFFFU; + static const __swift_uint32_t _SwiftKeyPathComponentHeader_MaximumOffsetPayload - = 0x00FFFFFCU; + = 0x007FFFFCU; static const __swift_uint32_t _SwiftKeyPathComponentHeader_UnresolvedIndirectOffsetPayload - = 0x00FFFFFDU; + = 0x007FFFFDU; static const __swift_uint32_t _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload - = 0x00FFFFFEU; + = 0x007FFFFEU; static const __swift_uint32_t _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload - = 0x00FFFFFFU; + = 0x007FFFFFU; + +static const __swift_uint32_t _SwiftKeyPathComponentHeader_StoredMutableFlag + = 0x00800000U; static const __swift_uint32_t _SwiftKeyPathComponentHeader_OptionalChainPayload = 0; diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 8183db8da32c1..61cb407f92b42 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -758,6 +758,12 @@ internal struct RawKeyPathComponent { internal static var endOfReferencePrefixFlag: UInt32 { return _SwiftKeyPathComponentHeader_EndOfReferencePrefixFlag } + internal static var storedMutableFlag: UInt32 { + return _SwiftKeyPathComponentHeader_StoredMutableFlag + } + internal static var storedOffsetPayloadMask: UInt32 { + return _SwiftKeyPathComponentHeader_StoredOffsetPayloadMask + } internal static var outOfLineOffsetPayload: UInt32 { return _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload } @@ -771,6 +777,11 @@ internal struct RawKeyPathComponent { return _SwiftKeyPathComponentHeader_MaximumOffsetPayload } + internal var isStoredMutable: Bool { + _sanityCheck(kind == .struct || kind == .class) + return _value & Header.storedMutableFlag != 0 + } + internal static var computedMutatingFlag: UInt32 { return _SwiftKeyPathComponentHeader_ComputedMutatingFlag } @@ -852,6 +863,20 @@ internal struct RawKeyPathComponent { _value = _value & ~Header.payloadMask | newValue } } + internal var storedOffsetPayload: UInt32 { + get { + _sanityCheck(kind == .struct || kind == .class, + "not a stored component") + return _value & Header.storedOffsetPayloadMask + } + set { + _sanityCheck(kind == .struct || kind == .class, + "not a stored component") + _sanityCheck(newValue & Header.storedOffsetPayloadMask == newValue, + "payload too big") + _value = _value & ~Header.storedOffsetPayloadMask | newValue + } + } internal var endOfReferencePrefix: Bool { get { return _value & Header.endOfReferencePrefixFlag != 0 @@ -913,12 +938,12 @@ internal struct RawKeyPathComponent { internal func _componentBodySize(forPropertyDescriptor: Bool) -> Int { switch kind { case .struct, .class: - if payload == Header.unresolvedFieldOffsetPayload - || payload == Header.outOfLineOffsetPayload { + if storedOffsetPayload == Header.unresolvedFieldOffsetPayload + || storedOffsetPayload == Header.outOfLineOffsetPayload { // A 32-bit offset is stored in the body. return MemoryLayout.size } - if payload == Header.unresolvedIndirectOffsetPayload { + if storedOffsetPayload == Header.unresolvedIndirectOffsetPayload { // A pointer-aligned, pointer-sized pointer is stored in the body. return Header.pointerAlignmentSkew + MemoryLayout.size } @@ -959,7 +984,9 @@ internal struct RawKeyPathComponent { let ptrSize = MemoryLayout.size switch header.kind { case .struct, .class: - if header.payload == Header.payloadMask { return 4 } // overflowed + if header.storedOffsetPayload == Header.outOfLineOffsetPayload { + return 4 // overflowed + } return 0 case .external: // align to pointer + pointer to external descriptor @@ -993,13 +1020,13 @@ internal struct RawKeyPathComponent { "no offset for this kind") // An offset too large to fit inline is represented by a signal and stored // in the body. - if header.payload == Header.outOfLineOffsetPayload { + if header.storedOffsetPayload == Header.outOfLineOffsetPayload { // Offset overflowed into body _sanityCheck(body.count >= MemoryLayout.size, "component not big enough") return Int(body.load(as: UInt32.self)) } - return Int(header.payload) + return Int(header.storedOffsetPayload) } internal var _computedIDValue: Int { @@ -1165,7 +1192,7 @@ internal struct RawKeyPathComponent { switch header.kind { case .struct, .class: - if header.payload == Header.outOfLineOffsetPayload { + if header.storedOffsetPayload == Header.outOfLineOffsetPayload { let overflowOffset = body.load(as: UInt32.self) buffer.storeBytes(of: overflowOffset, toByteOffset: 4, as: UInt32.self) @@ -2069,7 +2096,10 @@ internal var keyPathObjectHeaderSize: Int { } // Runtime entry point to instantiate a key path object. -@_cdecl("swift_getKeyPath") +// Note that this has a compatibility override shim in the runtime so that +// future compilers can backward-deploy support for instantiating new key path +// pattern features. +@_cdecl("swift_getKeyPathImpl") public func _swift_getKeyPath(pattern: UnsafeMutableRawPointer, arguments: UnsafeRawPointer) -> UnsafeRawPointer { @@ -2259,6 +2289,17 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern( return true }()) + func setStoredCapability(for header: RawKeyPathComponent.Header) { + // Mutable class properties can be the root of a reference mutation. + // Mutable struct properties pass through the existing capability. + if header.isStoredMutable { + if header.kind == .class { capability = .reference } + } else { + // Immutable properties can only be read. + capability = .readOnly + } + } + func setComputedCapability(for header: RawKeyPathComponent.Header) { let settable = header.isComputedSettable let mutating = header.isComputedMutating @@ -2279,21 +2320,15 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern( } switch header.kind { - case .class: - // The rest of the key path could be reference-writable. - capability = .reference - fallthrough - case .struct: - // No effect on the capability. - // TODO: we should dynamically prevent "let" properties from being - // reassigned. + case .class, .struct: + setStoredCapability(for: header) // Check the final instantiated size of the offset. - if header.payload == RawKeyPathComponent.Header.unresolvedFieldOffsetPayload - || header.payload == RawKeyPathComponent.Header.outOfLineOffsetPayload { + if header.storedOffsetPayload == RawKeyPathComponent.Header.unresolvedFieldOffsetPayload + || header.storedOffsetPayload == RawKeyPathComponent.Header.outOfLineOffsetPayload { _ = buffer.pop(UInt32.self) } - if header.payload == RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload { + if header.storedOffsetPayload == RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload { _ = buffer.pop(Int.self) // On 64-bit systems the pointer to the ivar offset variable is // pointer-sized and -aligned, but the resulting offset ought to be @@ -2346,13 +2381,9 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern( // Measure the instantiated size of the external component. let newComponentSize: Int switch descriptorHeader.kind { - case .class: - // A stored class property is reference-writable. - // TODO: we should dynamically prevent "let" properties from being - // reassigned (in both the struct and class cases). - capability = .reference - fallthrough - case .struct: + case .class, .struct: + setStoredCapability(for: descriptorHeader) + // Discard the local candidate. _ = buffer.popRaw(size: localCandidateSize, alignment: Int32.self) @@ -2360,7 +2391,7 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern( // The final component will be a stored component with just an offset. // If the offset requires resolution, then it'll be stored out of // line after the header. - if descriptorHeader.payload + if descriptorHeader.storedOffsetPayload > RawKeyPathComponent.Header.maximumOffsetPayload { newComponentSize = MemoryLayout.size + MemoryLayout.size @@ -2614,7 +2645,7 @@ internal func _instantiateKeyPathBuffer( func tryToResolveOffset(header: RawKeyPathComponent.Header, getOutOfLineOffset: () -> UInt32) { - if header.payload == RawKeyPathComponent.Header.unresolvedFieldOffsetPayload { + if header.storedOffsetPayload == RawKeyPathComponent.Header.unresolvedFieldOffsetPayload { // Look up offset in type metadata. The value in the pattern is the // offset within the metadata object. let metadataPtr = unsafeBitCast(base, to: UnsafeRawPointer.self) @@ -2631,20 +2662,20 @@ internal func _instantiateKeyPathBuffer( // Rewrite the header for a resolved offset. var newHeader = header - newHeader.payload = RawKeyPathComponent.Header.outOfLineOffsetPayload + newHeader.storedOffsetPayload = RawKeyPathComponent.Header.outOfLineOffsetPayload pushDest(newHeader) pushDest(offset) return } - if header.payload == RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload { + if header.storedOffsetPayload == RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload { // Look up offset in the indirectly-referenced variable we have a // pointer. let offsetVar = patternBuffer.pop(UnsafeRawPointer.self) let offsetValue = UInt32(offsetVar.load(as: UInt.self)) // Rewrite the header for a resolved offset. var newHeader = header - newHeader.payload = RawKeyPathComponent.Header.outOfLineOffsetPayload + newHeader.storedOffsetPayload = RawKeyPathComponent.Header.outOfLineOffsetPayload pushDest(newHeader) pushDest(offsetValue) return @@ -2652,7 +2683,7 @@ internal func _instantiateKeyPathBuffer( // Otherwise, just transfer the pre-resolved component. pushDest(header) - if header.payload == RawKeyPathComponent.Header.outOfLineOffsetPayload { + if header.storedOffsetPayload == RawKeyPathComponent.Header.outOfLineOffsetPayload { let offset = getOutOfLineOffset() //patternBuffer.pop(UInt32.self) pushDest(offset) } @@ -2747,9 +2778,11 @@ internal func _instantiateKeyPathBuffer( tryToResolveOffset(header: header, getOutOfLineOffset: { patternBuffer.pop(UInt32.self) }) case .class: - // Crossing a class can end the reference prefix, and makes the following - // key path potentially reference-writable. - endOfReferencePrefixComponent = previousComponentAddr + // Accessing a mutable class property can end the reference prefix, and + // makes the following key path potentially reference-writable. + if header.isStoredMutable { + endOfReferencePrefixComponent = previousComponentAddr + } // The offset may need to be resolved dynamically. tryToResolveOffset(header: header, getOutOfLineOffset: { patternBuffer.pop(UInt32.self) }) @@ -2799,9 +2832,11 @@ internal func _instantiateKeyPathBuffer( // descriptor. switch descriptorHeader.kind { case .class: - // Crossing a class can end the reference prefix, and makes the following - // key path potentially reference-writable. - endOfReferencePrefixComponent = previousComponentAddr + // Accessing a mutable class property can end the reference prefix, + // and makes the following key path potentially reference-writable. + if descriptorHeader.isStoredMutable { + endOfReferencePrefixComponent = previousComponentAddr + } fallthrough case .struct: diff --git a/stdlib/public/runtime/CompatibilityOverride.def b/stdlib/public/runtime/CompatibilityOverride.def index c2cadb4cd9960..1df6232e215e8 100644 --- a/stdlib/public/runtime/CompatibilityOverride.def +++ b/stdlib/public/runtime/CompatibilityOverride.def @@ -17,16 +17,21 @@ /// #define OVERRIDE(name, ret, attrs, namespace, typedArgs, namedArgs) /// Provides information about an overridable function. -/// - name is the name of the function, without any leading swift_ or namespace. +/// - name is the name of the function, without any leading swift_ or +/// namespace. /// - ret is the return type of the function. /// - attrs is the attributes, if any, applied to the function definition. -/// - namespace is the namespace, if any, the function is in, including a trailing :: -/// - typedArgs is the argument list, including types, surrounded by parentheses -/// - namedArgs is the list of argument names, with no types, surrounded by parentheses +/// - namespace is the namespace, if any, the function is in, including a +/// trailing :: +/// - typedArgs is the argument list, including types, surrounded by +/// parentheses +/// - namedArgs is the list of argument names, with no types, surrounded by +/// parentheses /// -/// The entries are organized by group. A user may define OVERRIDE to get all entries, -/// or define one or more of OVERRIDE_METADATALOOKUP, OVERRIDE_CASTING, OVERRIDE_OBJC, -/// OVERRIDE_FOREIGN, or OVERRIDE_PROTOCOLCONFORMANCE to get only those entries. +/// The entries are organized by group. A user may define OVERRIDE to get all +/// entries, or define one or more of OVERRIDE_METADATALOOKUP, OVERRIDE_CASTING, +/// OVERRIDE_OBJC, OVERRIDE_FOREIGN, OVERRIDE_PROTOCOLCONFORMANCE, +/// and OVERRIDE_KEYPATH to get only those entries. // NOTE: this file is used to build the definition of OverrideSection in // CompatibilityOverride.cpp, which is part of the ABI. Do not move or remove entries @@ -38,6 +43,7 @@ # define OVERRIDE_OBJC OVERRIDE # define OVERRIDE_FOREIGN OVERRIDE # define OVERRIDE_PROTOCOLCONFORMANCE OVERRIDE +# define OVERRIDE_KEYPATH OVERRIDE #else # ifndef OVERRIDE_METADATALOOKUP # define OVERRIDE_METADATALOOKUP(...) @@ -54,6 +60,9 @@ # ifndef OVERRIDE_PROTOCOLCONFORMANCE # define OVERRIDE_PROTOCOLCONFORMANCE(...) # endif +# ifndef OVERRIDE_KEYPATH +# define OVERRIDE_KEYPATH(...) +# endif #endif OVERRIDE_METADATALOOKUP(getTypeByMangledName, const Metadata *, @@ -127,6 +136,10 @@ OVERRIDE_PROTOCOLCONFORMANCE(conformsToProtocol, const WitnessTable *, , swift:: const ProtocolDescriptor *protocol), (type, protocol)) +OVERRIDE_KEYPATH(getKeyPath, const HeapObject *, , swift::, + (const void *pattern, const void *arguments), + (pattern, arguments)) + #if SWIFT_OBJC_INTEROP OVERRIDE_OBJC(dynamicCastObjCClass, const void *, , swift::, @@ -169,3 +182,4 @@ OVERRIDE_FOREIGN(dynamicCastForeignClassUnconditional, const void *, , swift::, #undef OVERRIDE_OBJC #undef OVERRIDE_FOREIGN #undef OVERRIDE_PROTOCOLCONFORMANCE +#undef OVERRIDE_KEYPATH diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 19773a4b6a99f..0d51e8a053b1e 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -46,6 +46,7 @@ #endif #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" +#include "CompatibilityOverride.h" #include "ErrorObject.h" #include "ExistentialMetadataImpl.h" #include "swift/Runtime/Debug.h" @@ -4239,3 +4240,14 @@ void swift::verifyMangledNameRoundtrip(const Metadata *metadata) { const TypeContextDescriptor *swift::swift_getTypeContextDescriptor(const Metadata *type) { return type->getTypeContextDescriptor(); } + +// Emit compatibility override shims for keypath runtime functionality. The +// implementation of these functions is in the standard library in +// KeyPath.swift. + +SWIFT_RUNTIME_STDLIB_SPI +const HeapObject *swift_getKeyPathImpl(const void *pattern, + const void *arguments); + +#define OVERRIDE_KEYPATH COMPATIBILITY_OVERRIDE +#include "CompatibilityOverride.def" diff --git a/test/IRGen/keypaths.sil b/test/IRGen/keypaths.sil index a1039f3d086d9..62e5a8ae12f8e 100644 --- a/test/IRGen/keypaths.sil +++ b/test/IRGen/keypaths.sil @@ -45,8 +45,8 @@ sil_vtable C {} // -- instantiable in-line, size 4 // CHECK-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, -// -- offset of S.x -// CHECK-SAME: }> +// -- offset of S.x, mutable +// CHECK-SAME: }> // -- %b: S.y // CHECK: [[KP_B:@keypath.*]] = private global <{ {{.*}} }> <{ @@ -56,7 +56,7 @@ sil_vtable C {} // -- instantiable in-line, size 4 // CHECK-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, -// -- offset of S.y +// -- offset of S.y, immutable // CHECK-32-SAME: }> // CHECK-64-SAME: }> @@ -68,9 +68,9 @@ sil_vtable C {} // -- instantiable in-line, size 4 // CHECK-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, -// -- offset of S.z -// CHECK-32-SAME: }> -// CHECK-64-SAME: }> +// -- offset of S.z, mutable +// CHECK-32-SAME: }> +// CHECK-64-SAME: }> // -- %d: C.x // CHECK: [[KP_D:@keypath.*]] = private global <{ {{.*}} }> <{ @@ -80,9 +80,9 @@ sil_vtable C {} // -- instantiable in-line, size 4 // CHECK-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, -// -- 0x0300_0000 (class) + offset of C.x -// CHECK-32-SAME: }> -// CHECK-64-SAME: }> +// -- 0x0300_0000 (class) + mutable + offset of C.x +// CHECK-32-SAME: }> +// CHECK-64-SAME: }> // -- %e: C.y // CHECK: [[KP_E:@keypath.*]] = private global <{ {{.*}} }> <{ @@ -92,7 +92,7 @@ sil_vtable C {} // -- instantiable in-line, size 4 // CHECK-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, -// -- 0x0300_0000 (class) + offset of C.y +// -- 0x0300_0000 (class) + immutable + offset of C.y // CHECK-32-SAME: }> // CHECK-64-SAME: }> @@ -104,9 +104,9 @@ sil_vtable C {} // -- instantiable in-line, size 4 // CHECK-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, -// -- 0x0300_0000 (class) + offset of C.z -// CHECK-32-SAME: }> -// CHECK-64-SAME: }> +// -- 0x0300_0000 (class) + mutable offset of C.z +// CHECK-32-SAME: }> +// CHECK-64-SAME: }> // -- %g: S.z.x // CHECK: [[KP_G:@keypath.*]] = private global <{ {{.*}} }> <{ @@ -119,13 +119,13 @@ sil_vtable C {} // CHECK-64-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, // -- offset of S.z -// CHECK-32-SAME: , -// CHECK-64-SAME: , +// CHECK-32-SAME: , +// CHECK-64-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, // CHECK: %swift.type* (i8*)* // -- 0x0300_0000 (class) + offset of C.x -// CHECK-32-SAME: }> -// CHECK-64-SAME: }> +// CHECK-32-SAME: }> +// CHECK-64-SAME: }> // -- %h: C.z.x // CHECK: [[KP_H:@keypath.*]] = private global <{ {{.*}} }> <{ @@ -138,12 +138,12 @@ sil_vtable C {} // CHECK-64-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, // -- 0x0300_0000 (class) + offset of C.z -// CHECK-32-SAME: , -// CHECK-64-SAME: , +// CHECK-32-SAME: , +// CHECK-64-SAME: , // CHECK-64-SAME: [4 x i8] zeroinitializer, // CHECK: %swift.type* (i8*)* // -- offset of S.x -// CHECK-SAME: }> +// CHECK-SAME: }> // -- %k: computed // CHECK: [[KP_K:@keypath.*]] = private global <{ {{.*}} }> <{ @@ -204,7 +204,7 @@ sil_vtable C {} // -- size 8 // CHECK-SAME: i32 8, // CHECK-64-SAME: [4 x i8] zeroinitializer, -// -- struct with runtime-resolved offset +// -- struct with runtime-resolved offset, mutable // CHECK-SAME: , // CHECK-32-SAME: i32 16 }> // CHECK-64-SAME: i32 32 }> diff --git a/test/IRGen/keypaths_objc.sil b/test/IRGen/keypaths_objc.sil index 8371fd5eeaf50..5685b363284c7 100644 --- a/test/IRGen/keypaths_objc.sil +++ b/test/IRGen/keypaths_objc.sil @@ -18,12 +18,12 @@ sil_vtable C {} sil @x_get : $@convention(thin) (@in_guaranteed C) -> @out NSString // CHECK: [[KEYPATH_A:@keypath.*]] = private global -// -- 0x0200_0002: computed, get-only, indirect identifier +// -- computed, get-only, indirect identifier // CHECK-SAME: , // CHECK-SAME: i8** @"\01L_selector(x)" // CHECK: [[KEYPATH_B:@keypath.*]] = private global -// -- 0x03fffffd: class stored property with indirect offset +// -- class mutable stored property with indirect offset // CHECK-SAME: , // CHECK-SAME: @"$S13keypaths_objc1CC6storedSivpWvd" diff --git a/test/IRGen/property_descriptor.sil b/test/IRGen/property_descriptor.sil index dca86eeaf950b..2e23ab4337a94 100644 --- a/test/IRGen/property_descriptor.sil +++ b/test/IRGen/property_descriptor.sil @@ -39,17 +39,15 @@ public struct ExternalReabstractions { @sil_stored public var reabstracted: () -> () { get set } } -// -- 0xff_fffe - struct property, offset resolved from field offset vector in metadata +// -- struct property, offset resolved from field offset vector in metadata // CHECK-64: @"$S19property_descriptor15ExternalGenericV2roxvpMV" = -// CHECK-64-SAME: <{ , i32 32 }>, align 8 // CHECK-32: @"$S19property_descriptor15ExternalGenericV2roxvpMV" = -// CHECK-32-SAME: <{ , i32 16 }>, align 4 +// CHECK-SAME: <{ , sil_property #ExternalGeneric.ro ( stored_property #ExternalGeneric.ro : $T) // CHECK-64: @"$S19property_descriptor15ExternalGenericV2rwxvpMV" = -// CHECK-64-SAME: <{ , i32 36 }>, align 8 // CHECK-32: @"$S19property_descriptor15ExternalGenericV2rwxvpMV" = -// CHECK-32-SAME: <{ , i32 20 }>, align 4 +// CHECK-SAME: <{ , sil_property #ExternalGeneric.rw ( stored_property #ExternalGeneric.rw : $T) diff --git a/test/stdlib/Inputs/KeyPathMultiModule_b.swift b/test/stdlib/Inputs/KeyPathMultiModule_b.swift index 4029725e096d2..3e68d9aa9f3d4 100644 --- a/test/stdlib/Inputs/KeyPathMultiModule_b.swift +++ b/test/stdlib/Inputs/KeyPathMultiModule_b.swift @@ -35,6 +35,8 @@ public struct A { get { return 0 } set { } } + + public let immutable: Int = 1738 } public struct B { @@ -88,6 +90,10 @@ public func A_z_keypath() -> KeyPath { return \A.z } +public func A_immutable_keypath() -> KeyPath { + return \A.immutable +} + public func A_subscript_withGeneric_keypath(index: T) -> KeyPath { return \A.[withGeneric: index] @@ -197,6 +203,8 @@ open class ResilientRoot { open var storedA = "a" open var storedB = "b" + public let storedLet = "c" + open var virtual: String { get { return "foo" } set { } @@ -237,6 +245,9 @@ public func ResilientRoot_storedA_keypath() -> KeyPath { public func ResilientRoot_storedB_keypath() -> KeyPath { return \ResilientRoot.storedB } +public func ResilientRoot_storedLet_keypath() -> KeyPath { + return \ResilientRoot.storedLet +} public func ResilientRoot_virtual_keypath() -> KeyPath { return \ResilientRoot.virtual } diff --git a/test/stdlib/KeyPath.swift b/test/stdlib/KeyPath.swift index a5530c884a1df..36597c1d3fbda 100644 --- a/test/stdlib/KeyPath.swift +++ b/test/stdlib/KeyPath.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -g %s -o %t/a.out +// RUN: %target-build-swift -swift-version 5 -g %s -o %t/a.out // RUN: %target-codesign %t/a.out // RUN: %target-run %t/a.out // REQUIRES: executable_test @@ -12,6 +12,8 @@ final class C { var x: Int var y: LifetimeTracked? var z: T + let immutable: String + private(set) var secretlyMutable: String var computed: T { get { @@ -26,6 +28,8 @@ final class C { self.x = x self.y = y self.z = z + self.immutable = "\(x) \(y) \(z)" + self.secretlyMutable = immutable } } @@ -33,10 +37,14 @@ struct Point: Equatable { var x: Double var y: Double var trackLifetime = LifetimeTracked(123) + let hypotenuse: Double + private(set) var secretlyMutableHypotenuse: Double init(x: Double, y: Double) { self.x = x self.y = y + hypotenuse = x*x + y*y + secretlyMutableHypotenuse = x*x + y*y } static func ==(a: Point, b: Point) -> Bool { @@ -693,6 +701,13 @@ keyPath.test("offsets") { expectNil(NOPLayout.offset(of: \NonOffsetableProperties.z)) } +keyPath.test("let-ness") { + expectNil(\C.immutable as? ReferenceWritableKeyPath) + expectNotNil(\C.secretlyMutable as? ReferenceWritableKeyPath) + expectNil(\Point.hypotenuse as? WritableKeyPath) + expectNotNil(\Point.secretlyMutableHypotenuse as? WritableKeyPath) +} + // SR-6096 protocol Protocol6096 {} @@ -713,5 +728,6 @@ keyPath.test("optional chaining component that needs generic instantiation") { Value6096().doSomething() } + runAllTests() diff --git a/test/stdlib/KeyPathMultiModule.swift b/test/stdlib/KeyPathMultiModule.swift index d86cdd97d7daf..1d5b6e8dd786c 100644 --- a/test/stdlib/KeyPathMultiModule.swift +++ b/test/stdlib/KeyPathMultiModule.swift @@ -52,6 +52,7 @@ keyPathMultiModule.test("identity across multiple modules") { expectEqualWithHashes(A_x_keypath(), \A.x) expectEqualWithHashes(A_y_keypath(), \A.y) expectEqualWithHashes(A_z_keypath(), \A.z) + expectEqualWithHashes(A_immutable_keypath(), \A.immutable) expectEqualWithHashes(A_subscript_withGeneric_keypath(index: 0), \A.[withGeneric: 0]) expectEqualWithHashes(A_subscript_withGeneric_keypath(index: "butt"), \A.[withGeneric: "butt"]) @@ -156,6 +157,8 @@ keyPathMultiModule.test("identity across multiple modules") { \ResilientRoot.storedA) expectEqualWithHashes(ResilientRoot_storedB_keypath(), \ResilientRoot.storedB) + expectEqualWithHashes(ResilientRoot_storedLet_keypath(), + \ResilientRoot.storedLet) expectEqualWithHashes(ResilientRoot_virtual_keypath(), \ResilientRoot.virtual) expectEqualWithHashes(ResilientRoot_virtualRO_keypath(), diff --git a/unittests/runtime/Stdlib.cpp b/unittests/runtime/Stdlib.cpp index 04ecf72963603..739c507c3c452 100644 --- a/unittests/runtime/Stdlib.cpp +++ b/unittests/runtime/Stdlib.cpp @@ -252,3 +252,10 @@ const long long $Ss12_ClassMirrorVs01_B0sWP[1] = {0}; // type metadata accessor for Swift._ClassMirror SWIFT_RUNTIME_STDLIB_INTERNAL const long long $Ss12_ClassMirrorVMa[1] = {0}; + +// KeyPath + +SWIFT_RUNTIME_STDLIB_SPI +const HeapObject *swift_getKeyPathImpl(const void *p, const void *a) { + abort(); +}