diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 48fc2fc2cb1ca..3bb863310358e 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -390,6 +390,11 @@ BUILTIN_SIL_OPERATION(AllocWithTailElems, "allocWithTailElems", Special) /// Projects the first tail-allocated element of type E from a class C. BUILTIN_SIL_OPERATION(ProjectTailElems, "projectTailElems", Special) +/// identityKeyPath : () -> WritableKeyPath +/// +/// Creates an identity key path object. (TODO: replace with proper syntax) +BUILTIN_SIL_OPERATION(IdentityKeyPath, "identityKeyPath", Special) + #undef BUILTIN_SIL_OPERATION // BUILTIN_RUNTIME_CALL - A call into a runtime function. diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 6c64ccada7e56..bc2b7d4fd5d63 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -583,8 +583,6 @@ ERROR(sil_keypath_computed_property_missing_part,none, ERROR(sil_keypath_external_missing_part,none, "keypath external component with indices needs an indices_equals and " "indices_hash function", ()) -ERROR(sil_keypath_no_components,none, - "keypath must have at least one component", ()) ERROR(sil_keypath_no_root,none, "keypath must have a root component declared",()) ERROR(sil_keypath_index_not_hashable,none, diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index f603ce814622a..548d30d447fa2 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -556,6 +556,20 @@ makeTuple(const Gs & ...elementGenerators) { }; } +template +static BuiltinGenericSignatureBuilder::LambdaGenerator +makeBoundGenericType(NominalTypeDecl *decl, + const Gs & ...argumentGenerators) { + return { + [=](BuiltinGenericSignatureBuilder &builder) -> Type { + Type args[] = { + argumentGenerators.build(builder)... + }; + return BoundGenericType::get(decl, Type(), args); + } + }; +} + template static BuiltinGenericSignatureBuilder::MetatypeGenerator makeMetatype(const T &object, Optional repr = None) { @@ -972,6 +986,15 @@ static ValueDecl *getTypeJoinMetaOperation(ASTContext &Context, Identifier Id) { return builder.build(Id); } +static ValueDecl *getIdentityKeyPathOperation(ASTContext &Context, + Identifier Id) { + BuiltinGenericSignatureBuilder builder(Context, 1); + auto arg = makeGenericParam(); + builder.setResult(makeBoundGenericType(Context.getWritableKeyPathDecl(), + arg, arg)); + return builder.build(Id); +} + static ValueDecl *getCanBeObjCClassOperation(ASTContext &Context, Identifier Id) { // T.Type -> Builtin.Int8 @@ -1865,6 +1888,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) { case BuiltinValueKind::TypeJoinMeta: return getTypeJoinMetaOperation(Context, Id); + + case BuiltinValueKind::IdentityKeyPath: + return getIdentityKeyPathOperation(Context, Id); } llvm_unreachable("bad builtin value!"); diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index 81745a90c6e70..90a3903f6c9d4 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -2884,8 +2884,6 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { return true; } - if (components.empty()) - P.diagnose(InstLoc.getSourceLoc(), diag::sil_keypath_no_components); if (rootType.isNull()) P.diagnose(InstLoc.getSourceLoc(), diag::sil_keypath_no_root); @@ -2943,8 +2941,13 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { if (patternEnv && patternEnv->getGenericSignature()) { canSig = patternEnv->getGenericSignature()->getCanonicalSignature(); } + CanType leafType; + if (!components.empty()) + leafType = components.back().getComponentType(); + else + leafType = rootType; auto pattern = KeyPathPattern::get(B.getModule(), canSig, - rootType, components.back().getComponentType(), + rootType, leafType, components, objcString); ResultVal = B.createKeyPath(InstLoc, pattern, subMap, operands, Ty); diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp index 15e2b8ce263e3..6581d8581b47d 100644 --- a/lib/SIL/SILOwnershipVerifier.cpp +++ b/lib/SIL/SILOwnershipVerifier.cpp @@ -1288,56 +1288,14 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, MustBeLive, Swift3ImplicitObjCEntrypoint) // Builtins that should be lowered to SIL instructions so we should never see // them. -#define BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(ID) \ +#define BUILTIN_SIL_OPERATION(ID, NAME, CATEGORY) \ OwnershipUseCheckerResult \ OwnershipCompatibilityBuiltinUseChecker::visit##ID(BuiltinInst *BI, \ StringRef Attr) { \ llvm_unreachable("Builtin should have been lowered to SIL instruction?!"); \ } -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Retain) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Release) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Autorelease) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(TryPin) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Unpin) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Load) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(LoadRaw) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(LoadInvariant) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Take) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Destroy) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Assign) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Init) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CastToNativeObject) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(UnsafeCastToNativeObject) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CastFromNativeObject) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CastToBridgeObject) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS( - CastReferenceFromBridgeObject) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS( - CastBitPatternFromBridgeObject) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(ClassifyBridgeObject) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(ValueToBridgeObject) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(BridgeToRawPointer) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(BridgeFromRawPointer) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CastReference) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(ReinterpretCast) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AddressOf) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AddressOfBorrow) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(GepRaw) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(Gep) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(GetTailAddr) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(PerformInstantaneousReadAccess) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(BeginUnpairedModifyAccess) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(EndUnpairedAccess) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(CondFail) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(FixLifetime) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(IsUnique) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(IsUniqueOrPinned) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(IsUnique_native) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(IsUniqueOrPinned_native) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(BindMemory) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(AllocWithTailElems) -BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS(ProjectTailElems) -#undef BUILTINS_THAT_SHOULD_HAVE_BEEN_LOWERED_TO_SILINSTS +#define BUILTIN(X, Y, Z) +#include "swift/AST/Builtins.def" OwnershipUseCheckerResult OwnershipCompatibilityUseChecker::visitBuiltinInst(BuiltinInst *BI) { diff --git a/lib/SIL/ValueOwnershipKindClassifier.cpp b/lib/SIL/ValueOwnershipKindClassifier.cpp index 9c150503c8474..c848a691d9c7e 100644 --- a/lib/SIL/ValueOwnershipKindClassifier.cpp +++ b/lib/SIL/ValueOwnershipKindClassifier.cpp @@ -397,8 +397,6 @@ struct ValueOwnershipKindBuiltinVisitor } \ return ValueOwnershipKind::OWNERSHIP; \ } -CONSTANT_OWNERSHIP_BUILTIN(Owned, Take) -CONSTANT_OWNERSHIP_BUILTIN(Owned, TryPin) // This returns a value at +1 that is destroyed strictly /after/ the // UnsafeGuaranteedEnd. This provides the guarantee that we want. CONSTANT_OWNERSHIP_BUILTIN(Owned, UnsafeGuaranteed) @@ -443,9 +441,6 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, ICMP_ULE) CONSTANT_OWNERSHIP_BUILTIN(Trivial, ICMP_ULT) CONSTANT_OWNERSHIP_BUILTIN(Trivial, IntToPtr) CONSTANT_OWNERSHIP_BUILTIN(Trivial, LShr) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Load) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, LoadRaw) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, LoadInvariant) CONSTANT_OWNERSHIP_BUILTIN(Trivial, Mul) CONSTANT_OWNERSHIP_BUILTIN(Trivial, Or) CONSTANT_OWNERSHIP_BUILTIN(Trivial, PtrToInt) @@ -472,32 +467,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, ZExt) CONSTANT_OWNERSHIP_BUILTIN(Trivial, ZExtOrBitCast) CONSTANT_OWNERSHIP_BUILTIN(Trivial, FCMP_ORD) CONSTANT_OWNERSHIP_BUILTIN(Trivial, FCMP_UNO) -CONSTANT_OWNERSHIP_BUILTIN(Unowned, CastToNativeObject) -CONSTANT_OWNERSHIP_BUILTIN(Unowned, UnsafeCastToNativeObject) -CONSTANT_OWNERSHIP_BUILTIN(Unowned, CastFromNativeObject) -CONSTANT_OWNERSHIP_BUILTIN(Unowned, CastToBridgeObject) -CONSTANT_OWNERSHIP_BUILTIN(Unowned, CastReferenceFromBridgeObject) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, CastBitPatternFromBridgeObject) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, ClassifyBridgeObject) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, BridgeToRawPointer) -CONSTANT_OWNERSHIP_BUILTIN(Unowned, BridgeFromRawPointer) -CONSTANT_OWNERSHIP_BUILTIN(Unowned, CastReference) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, AddressOf) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, AddressOfBorrow) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, GepRaw) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Gep) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, GetTailAddr) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, PerformInstantaneousReadAccess) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, BeginUnpairedModifyAccess) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, EndUnpairedAccess) CONSTANT_OWNERSHIP_BUILTIN(Trivial, OnFastPath) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsUnique) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsUniqueOrPinned) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsUnique_native) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsUniqueOrPinned_native) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, BindMemory) -CONSTANT_OWNERSHIP_BUILTIN(Owned, AllocWithTailElems) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, ProjectTailElems) CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsOptionalType) CONSTANT_OWNERSHIP_BUILTIN(Trivial, Sizeof) CONSTANT_OWNERSHIP_BUILTIN(Trivial, Strideof) @@ -543,21 +513,11 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, UnexpectedError) CONSTANT_OWNERSHIP_BUILTIN(Trivial, ErrorInMain) CONSTANT_OWNERSHIP_BUILTIN(Trivial, DeallocRaw) CONSTANT_OWNERSHIP_BUILTIN(Trivial, Fence) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Retain) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Release) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, CondFail) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, FixLifetime) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Autorelease) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Unpin) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Destroy) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Assign) -CONSTANT_OWNERSHIP_BUILTIN(Trivial, Init) CONSTANT_OWNERSHIP_BUILTIN(Trivial, AtomicStore) CONSTANT_OWNERSHIP_BUILTIN(Trivial, Once) CONSTANT_OWNERSHIP_BUILTIN(Trivial, OnceWithContext) CONSTANT_OWNERSHIP_BUILTIN(Trivial, TSanInoutAccess) CONSTANT_OWNERSHIP_BUILTIN(Trivial, Swift3ImplicitObjCEntrypoint) -CONSTANT_OWNERSHIP_BUILTIN(Unowned, ValueToBridgeObject) #undef CONSTANT_OWNERSHIP_BUILTIN @@ -570,7 +530,6 @@ CONSTANT_OWNERSHIP_BUILTIN(Unowned, ValueToBridgeObject) } \ return ValueOwnershipKind::Unowned; \ } -UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(ReinterpretCast) UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(CmpXChg) UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(AtomicLoad) UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(ExtractElement) @@ -578,6 +537,15 @@ UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(InsertElement) UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT(ZeroInitializer) #undef UNOWNED_OR_TRIVIAL_DEPENDING_ON_RESULT +#define BUILTIN(X,Y,Z) +#define BUILTIN_SIL_OPERATION(ID, NAME, CATEGORY) \ + ValueOwnershipKind ValueOwnershipKindBuiltinVisitor::visit##ID( \ + BuiltinInst *BI, StringRef Attr) { \ + llvm_unreachable("builtin should have been lowered in SILGen"); \ + } + +#include "swift/AST/Builtins.def" + ValueOwnershipKind ValueOwnershipKindClassifier::visitBuiltinInst(BuiltinInst *BI) { // For now, just conservatively say builtins are None. We need to use a diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index 89def7c6a7892..fd5c4f03386f9 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -21,6 +21,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Builtins.h" #include "swift/AST/DiagnosticsSIL.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" #include "swift/AST/ReferenceCounting.h" #include "swift/SIL/SILArgument.h" @@ -1055,6 +1056,58 @@ static ManagedValue emitBuiltinProjectTailElems(SILGenFunction &SGF, return ManagedValue::forUnmanaged(result); } +static ManagedValue emitBuiltinIdentityKeyPath(SILGenFunction &SGF, + SILLocation loc, + SubstitutionMap subs, + ArrayRef args, + SGFContext C) { + assert(subs.getReplacementTypes().size() == 1 && + "identityKeyPath should have one substitution"); + assert(args.size() == 0 && + "identityKeyPath should have no args"); + + auto identityTy = subs.getReplacementTypes()[0]->getCanonicalType(); + + // The `self` key can be used for identity in Cocoa KVC as well. + StringRef objcString = SGF.getASTContext().LangOpts.EnableObjCInterop + ? "self" : ""; + + // The key path pattern has to capture some generic context if the type is + // dependent on this generic context. We only need the specific type, though, + // not the entire generic environment. + bool isDependent = identityTy->hasArchetype(); + CanType identityPatternTy = identityTy; + CanGenericSignature patternSig = nullptr; + SubstitutionMap patternSubs; + if (isDependent) { + auto param = GenericTypeParamType::get(0, 0, SGF.getASTContext()); + identityPatternTy = param->getCanonicalType(); + patternSig = GenericSignature::get(param, {})->getCanonicalSignature(); + patternSubs = SubstitutionMap::get(patternSig, + llvm::makeArrayRef((Type)identityTy), + {}); + } + + auto identityPattern = KeyPathPattern::get(SGF.SGM.M, + patternSig, + identityPatternTy, + identityPatternTy, + {}, + objcString); + + auto kpTy = BoundGenericType::get(SGF.getASTContext().getWritableKeyPathDecl(), + Type(), + {identityTy, identityTy}) + ->getCanonicalType(); + + auto keyPath = SGF.B.createKeyPath(loc, identityPattern, + patternSubs, + {}, + SILType::getPrimitiveObjectType(kpTy)); + return SGF.emitManagedRValueWithCleanup(keyPath); +} + + /// Specialized emitter for type traits. template diff --git a/stdlib/public/core/KeyPath.swift b/stdlib/public/core/KeyPath.swift index 61cb407f92b42..3b77da60845b9 100644 --- a/stdlib/public/core/KeyPath.swift +++ b/stdlib/public/core/KeyPath.swift @@ -57,6 +57,7 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { ObjectIdentifier(type(of: self)).hash(into: &hasher) return withBuffer { var buffer = $0 + if buffer.data.isEmpty { return } while true { let (component, type) = buffer.next() hasher.combine(component.value) @@ -88,6 +89,11 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { return false } + // Identity is equal to identity + if aBuffer.data.isEmpty { + return bBuffer.data.isEmpty + } + while true { let (aComponent, aType) = aBuffer.next() let (bComponent, bType) = bBuffer.next() @@ -153,6 +159,11 @@ public class AnyKeyPath: Hashable, _AppendKeyPath { internal var _storedInlineOffset: Int? { return withBuffer { var buffer = $0 + + // The identity key path is effectively a stored keypath of type Self + // at offset zero + if buffer.data.isEmpty { return 0 } + var offset = 0 while true { let (rawComponent, optNextType) = buffer.next() @@ -220,6 +231,9 @@ public class KeyPath: PartialKeyPath { var curBase: Any = root return withBuffer { var buffer = $0 + if buffer.data.isEmpty { + return unsafeBitCast(root, to: Value.self) + } while true { let (rawComponent, optNextType) = buffer.next() let valueType = optNextType ?? Value.self @@ -279,6 +293,13 @@ public class WritableKeyPath: KeyPath { _sanityCheck(!buffer.hasReferencePrefix, "WritableKeyPath should not have a reference prefix") + if buffer.data.isEmpty { + return ( + UnsafeMutablePointer( + mutating: p.assumingMemoryBound(to: Value.self)), + nil) + } + while true { let (rawComponent, optNextType) = buffer.next() let nextType = optNextType ?? Value.self @@ -306,7 +327,16 @@ public class WritableKeyPath: KeyPath { owner: keepAlive) } } +} +extension WritableKeyPath where Root == Value { + // FIXME: Replace with proper surface syntax + + /// Returns an identity key path that references the entire input value. + @inlinable + public static var _identity: WritableKeyPath { + return Builtin.identityKeyPath() + } } /// A key path that supports reading from and writing to the resulting value @@ -1941,6 +1971,16 @@ internal func _appendingKeyPaths< var rootBuffer = $0 return leaf.withBuffer { var leafBuffer = $0 + + // If either operand is the identity key path, then we should return + // the other operand back untouched. + if leafBuffer.data.isEmpty { + return unsafeDowncast(root, to: Result.self) + } + if rootBuffer.data.isEmpty { + return unsafeDowncast(leaf, to: Result.self) + } + // Reserve room for the appended KVC string, if both key paths are // KVC-compatible. let appendedKVCLength: Int, rootKVCLength: Int, leafKVCLength: Int @@ -2275,7 +2315,8 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern( var buffer = KeyPathBuffer(base: bufferPtr) var size = buffer.data.count + MemoryLayout.size - while true { + if !buffer.data.isEmpty { + while true { let header = buffer.pop(RawKeyPathComponent.Header.self) // Ensure that we pop an amount of data consistent with what @@ -2552,6 +2593,7 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern( // Pop the type accessor reference. _ = buffer.popRaw(size: MemoryLayout.size, alignment: Int.self) + } } _sanityCheck(buffer.data.isEmpty, "didn't read entire pattern") @@ -2628,7 +2670,8 @@ internal func _instantiateKeyPathBuffer( var base: Any.Type = rootType // Some pattern forms are pessimistically larger than what we need in the // instantiated key path. Keep track of this. - while true { + if !patternBuffer.data.isEmpty { + while true { let componentAddr = destData.baseAddress.unsafelyUnwrapped let header = patternBuffer.pop(RawKeyPathComponent.Header.self) @@ -2996,6 +3039,7 @@ internal func _instantiateKeyPathBuffer( base = unsafeBitCast(componentTyAccessor(arguments), to: Any.Type.self) pushDest(base) previousComponentAddr = componentAddr + } } // We should have traversed both buffers. diff --git a/test/IRGen/keypaths.sil b/test/IRGen/keypaths.sil index 62e5a8ae12f8e..34fb5a578b1b3 100644 --- a/test/IRGen/keypaths.sil +++ b/test/IRGen/keypaths.sil @@ -448,6 +448,14 @@ entry(%0 : @trivial $*A, %1 : @trivial $*B, %2 : @trivial $*A, %3 : @trivial $*B return undef : $() } +sil @identity : $@convention(thin) () -> () { +entry: + %v = keypath $WritableKeyPath, (root $A; objc "self") + %w = keypath $WritableKeyPath, (root $Int; objc "self") + + return undef : $() +} + sil @s_get : $@convention(thin) (@in_guaranteed A, UnsafeRawPointer) -> @out B sil @s_set : $@convention(thin) (@in_guaranteed B, @in_guaranteed A, UnsafeRawPointer) -> () sil @s_equals : $@convention(thin) (UnsafeRawPointer, UnsafeRawPointer) -> Bool diff --git a/test/SILGen/keypaths.swift b/test/SILGen/keypaths.swift index 892169cf6ec54..3b129a000fffb 100644 --- a/test/SILGen/keypaths.swift +++ b/test/SILGen/keypaths.swift @@ -1,4 +1,6 @@ -// RUN: %target-swift-emit-silgen -module-name keypaths %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -parse-stdlib -module-name keypaths %s | %FileCheck %s + +import Swift struct S { var x: T @@ -383,3 +385,14 @@ func subclass_generics, U: C, V/*: PoC*/>(_: T, _: U, _: V) { _ = \PoC.extension */ } + +// CHECK-LABEL: sil hidden @{{.*}}identity +func identity(_: T) { + // CHECK: keypath $WritableKeyPath, <τ_0_0> ({{.*}}root $τ_0_0) + let _: WritableKeyPath = Builtin.identityKeyPath() + // CHECK: keypath $WritableKeyPath, Array>, <τ_0_0> ({{.*}}root $τ_0_0) > + let _: WritableKeyPath<[T], [T]> = Builtin.identityKeyPath() + // CHECK: keypath $WritableKeyPath, ({{.*}}root $String) + let _: WritableKeyPath = Builtin.identityKeyPath() +} + diff --git a/test/stdlib/KeyPath.swift b/test/stdlib/KeyPath.swift index 36597c1d3fbda..f8bb9fe92e93b 100644 --- a/test/stdlib/KeyPath.swift +++ b/test/stdlib/KeyPath.swift @@ -684,6 +684,10 @@ struct NonOffsetableProperties { var z: Int { return 0 } } +func getIdentityKeyPathOfType(_: T.Type) -> KeyPath { + return WritableKeyPath._identity +} + keyPath.test("offsets") { let SLayout = MemoryLayout>.self expectNotNil(SLayout.offset(of: \S.x)) @@ -699,6 +703,43 @@ keyPath.test("offsets") { expectNil(NOPLayout.offset(of: \NonOffsetableProperties.x)) expectNil(NOPLayout.offset(of: \NonOffsetableProperties.y)) expectNil(NOPLayout.offset(of: \NonOffsetableProperties.z)) + + expectEqual(SLayout.offset(of: WritableKeyPath, S>._identity), + 0) + expectEqual(SLayout.offset(of: getIdentityKeyPathOfType(S.self)), 0) +} + +keyPath.test("identity key path") { + var x = LifetimeTracked(1738) + + let id = WritableKeyPath._identity + expectTrue(x === x[keyPath: id]) + + let newX = LifetimeTracked(679) + x[keyPath: id] = newX + expectTrue(x === newX) + + let id2 = getIdentityKeyPathOfType(LifetimeTracked.self) + expectEqual(id, id2) + expectEqual(id.hashValue, id2.hashValue) + expectNotNil(id2 as? WritableKeyPath) + + let id3 = id.appending(path: id2) + expectEqual(id, id3) + expectEqual(id.hashValue, id3.hashValue) + expectNotNil(id3 as? WritableKeyPath) + + let valueKey = \LifetimeTracked.value + let valueKey2 = id.appending(path: valueKey) + let valueKey3 = (valueKey as KeyPath).appending(path: WritableKeyPath._identity) + + expectEqual(valueKey, valueKey2) + expectEqual(valueKey.hashValue, valueKey2.hashValue) + expectEqual(valueKey, valueKey3) + expectEqual(valueKey.hashValue, valueKey3.hashValue) + + expectEqual(x[keyPath: valueKey2], 679) + expectEqual(x[keyPath: valueKey3], 679) } keyPath.test("let-ness") {