Swift compiler generates incorrect code for dynamic member lookup when optimization is enabled

Originator:serieuxchat
Number:rdar://FB16674419 Date Originated:01-03-2025
Status:Open Resolved:
Product:Swift Compiler Product Version:swiftlang-6.0.3.1.10 clang-1600.0.30.1
Classification:Bug Reproducible:Always
 
// The following code demonstrates the problem with dynamic member lookup and optimization.
// Without optimization, the following code prints the following:
//
//    ==== Setter for keypath: \TempAPIContainerFields.foo
//    ==== Setter for keypath: \TempAPIContainerFields.bar
//    ==== Getter for keypath: \TempAPIContainerFields.foo.
//    ==== Getter for keypath: \TempAPIContainerFields.bar.
//    \TempAPIContainerFields.foo
//    \TempAPIContainerFields.bar
//
// But when optimization is set to -O2 or higher, accesses to tempAPIContainer.foo and tempAPIContainer.bar are no longer considered to be different,
// and the following output is produced:
//
//    ==== Setter for keypath: \TempAPIContainerFields.bar
//    ==== Setter for keypath: \TempAPIContainerFields.bar
//    ==== Getter for keypath: \TempAPIContainerFields.bar.
//    ==== Getter for keypath: \TempAPIContainerFields.bar.
//    \TempAPIContainerFields.bar
//    \TempAPIContainerFields.bar
//

// My assumption is that dynamic member lookup through .foo and .bar must never resolve to the same keypath,
// however it's the case when optimization is enabled.
// This is broken for both MacOS (15.3.1) and iOS (18+)
// I am using Xcode Version 16.2 (16C5032a)

import Foundation

public struct TempAPIContainerFields {
    public var foo: String.Type = String.self
    public var bar: String.Type = String.self
}

@dynamicMemberLookup public struct TempAPIContainer {
    
    public subscript<T>(dynamicMember keypath: KeyPath<TempAPIContainerFields, T.Type>) -> T {
        get {
            print("==== Getter for keypath: \(keypath).")
            if let castedKeyPath: T = "\(keypath)" as? T {
                return castedKeyPath
            }
            fatalError()
        }
        mutating set { // swiftlint:disable:this unused_setter_value
            print("==== Setter for keypath: \(keypath)")
        }
    }
}

var tempAPIContainer = TempAPIContainer()

let keyPath1 = \TempAPIContainerFields.foo
let keyPath2 = \TempAPIContainerFields.bar

tempAPIContainer.foo = "foo"
tempAPIContainer.bar = "bar"

let foo1 = tempAPIContainer.foo
let bar1 = tempAPIContainer.bar

print(foo1)
print(bar1)

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at feedbackassistant.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!