@@ -625,23 +625,107 @@ internal enum KeyPathComponent: Hashable {
625625 }
626626}
627627
628+ // _semantics("optimize.sil.preserve_exclusivity" forces the compiler to
629+ // generate access markers regardless of the current build settings. This way,
630+ // user code that accesses keypaths are properly enforced even if the standard
631+ // library has exclusivity checking internally disabled. The semantic attribute
632+ // must be consistently applied to both the begin and end unpaired access
633+ // markers, otherwise the runtime will fail catastrophically and unpredictably.
634+ // This relies on Swift module serialization occurring before these _semantic
635+ // function are inlined, since inlining would unpredictably cause the attribute
636+ // to be dropped.
637+ //
638+ // An exclusivity violation will report the caller's stack frame location.
639+ // Runtime diagnostics will be improved by inlining this function. However, this
640+ // cannot be marked @transparent because it can't be inlined prior to
641+ // serialization.
642+ @inlinable
643+ @inline ( __always)
644+ @_semantics ( " optimize.sil.preserve_exclusivity " )
645+ func beginAccessHelper< T> ( _ address: Builtin . RawPointer , _ accessRecordPtr: Builtin . RawPointer , _ type: T . Type ) {
646+ Builtin . beginUnpairedModifyAccess ( address, accessRecordPtr, type)
647+ }
648+
649+ @inlinable
650+ @inline ( __always)
651+ @_semantics ( " optimize.sil.preserve_exclusivity " )
652+ func endAccessHelper( _ accessRecordPtr: Builtin . RawPointer ) {
653+ Builtin . endUnpairedAccess ( accessRecordPtr)
654+ }
655+
656+ @inlinable
657+ @inline ( __always)
658+ @_semantics ( " optimize.sil.preserve_exclusivity " )
659+ func instantaneousAccessHelper< T> ( _ address: Builtin . RawPointer , _ type: T . Type ) {
660+ Builtin . performInstantaneousReadAccess ( address, T . self)
661+ }
662+
628663// A class that maintains ownership of another object while a mutable projection
629- // into it is underway.
664+ // into it is underway. The lifetime of the instance of this class is also used
665+ // to begin and end exclusive 'modify' access to the projected address.
630666@_fixed_layout // FIXME(sil-serialize-all)
631667@usableFromInline // FIXME(sil-serialize-all)
632- internal final class ClassHolder {
668+ internal final class ClassHolder < ProjectionType> {
669+
670+ /// The type of the scratch record passed to the runtime to record
671+ /// accesses to guarantee exlcusive access.
672+ internal typealias AccessRecord = Builtin . UnsafeValueBuffer
673+
633674 @usableFromInline // FIXME(sil-serialize-all)
634- internal let previous : AnyObject ?
675+ internal var previous : AnyObject ?
635676 @usableFromInline // FIXME(sil-serialize-all)
636- internal let instance : AnyObject
677+ internal var instance : AnyObject
637678
638679 @inlinable // FIXME(sil-serialize-all)
639680 internal init ( previous: AnyObject ? , instance: AnyObject ) {
640681 self . previous = previous
641682 self . instance = instance
642683 }
684+
643685 @inlinable // FIXME(sil-serialize-all)
644- deinit { }
686+ internal final class func _create(
687+ previous: AnyObject ? ,
688+ instance: AnyObject ,
689+ accessingAddress address: UnsafeRawPointer ,
690+ type: ProjectionType . Type
691+ ) -> ClassHolder {
692+
693+ // Tail allocate the UnsafeValueBuffer used as the AccessRecord.
694+ // This avoids a second heap allocation since there is no source-level way to
695+ // initialize a Builtin.UnsafeValueBuffer type and thus we cannot have a
696+ // stored property of that type.
697+ let holder : ClassHolder = Builtin . allocWithTailElems_1 ( self ,
698+ 1 . _builtinWordValue,
699+ AccessRecord . self)
700+
701+ // Initialize the ClassHolder's instance variables. This is done via
702+ // withUnsafeMutablePointer(to:) because the instance was just allocated with
703+ // allocWithTailElems_1 and so we need to make sure to use an initialization
704+ // rather than an assignment.
705+ withUnsafeMutablePointer ( to: & holder. previous) {
706+ $0. initialize ( to: previous)
707+ }
708+
709+ withUnsafeMutablePointer ( to: & holder. instance) {
710+ $0. initialize ( to: instance)
711+ }
712+
713+ let accessRecordPtr = Builtin . projectTailElems ( holder, AccessRecord . self)
714+
715+ // Begin a 'modify' access to the address. This access is ended in
716+ // ClassHolder's deinitializer.
717+ beginAccessHelper ( address. _rawValue, accessRecordPtr, type)
718+
719+ return holder
720+ }
721+
722+ @inlinable // FIXME(sil-serialize-all)
723+ deinit {
724+ let accessRecordPtr = Builtin . projectTailElems ( self , AccessRecord . self)
725+
726+ // Ends the access begun in _create().
727+ endAccessHelper ( accessRecordPtr)
728+ }
645729}
646730
647731// A class that triggers writeback to a pointer when destroyed.
@@ -1185,7 +1269,14 @@ internal struct RawKeyPathComponent {
11851269 let baseObj = unsafeBitCast ( base, to: AnyObject . self)
11861270 let basePtr = UnsafeRawPointer ( Builtin . bridgeToRawPointer ( baseObj) )
11871271 defer { _fixLifetime ( baseObj) }
1188- return . continue( basePtr. advanced ( by: offset)
1272+
1273+ let offsetAddress = basePtr. advanced ( by: offset)
1274+
1275+ // Perform an instaneous record access on the address in order to
1276+ // ensure that the read will not conflict with an already in-progress
1277+ // 'modify' access.
1278+ instantaneousAccessHelper ( offsetAddress. _rawValue, NewValue . self)
1279+ return . continue( offsetAddress
11891280 . assumingMemoryBound ( to: NewValue . self)
11901281 . pointee)
11911282
@@ -1244,12 +1335,16 @@ internal struct RawKeyPathComponent {
12441335 // AnyObject memory can alias any class reference memory, so we can
12451336 // assume type here
12461337 let object = base. assumingMemoryBound ( to: AnyObject . self) . pointee
1247- // The base ought to be kept alive for the duration of the derived access
1248- keepAlive = keepAlive == nil
1249- ? object
1250- : ClassHolder ( previous: keepAlive, instance: object)
1251- return UnsafeRawPointer ( Builtin . bridgeToRawPointer ( object) )
1338+ let offsetAddress = UnsafeRawPointer ( Builtin . bridgeToRawPointer ( object) )
12521339 . advanced ( by: offset)
1340+
1341+ // Keep the base alive for the duration of the derived access and also
1342+ // enforce exclusive access to the address.
1343+ keepAlive = ClassHolder . _create ( previous: keepAlive, instance: object,
1344+ accessingAddress: offsetAddress,
1345+ type: NewValue . self)
1346+
1347+ return offsetAddress
12531348
12541349 case . mutatingGetSet( id: _, get: let rawGet, set: let rawSet,
12551350 argument: let argument) :
0 commit comments