Skip to content

[SR-13990] @dynamicMemberLookup has confusing behavior with lets #56385

@swift-ci

Description

@swift-ci
Previous ID SR-13990
Radar rdar://problem/72864707
Original Reporter benpious (JIRA User)
Type Bug
Environment

Version 12.2 (12B45b), Swift 5.3

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: c1e7328831e888c4bb625eef730911b5

Issue Description:

If I write a property wrapper like the following:

@dynamicMemberLookup
struct Passthrough<T> {
    
    var backing: T
    
    subscript<U>(dynamicMember member: WritableKeyPath<T, U>) -> U {
        get {
            backing[keyPath: member]
        }
        set {
            backing[keyPath: member] = newValue
        }
    }

}

Then use it in this manner:

struct S {
    let a: Int = 7
}

let l = Passthrough<S>(backing: S())
print(l.a)

It's reasonable (imo) to expect it to compile, since the `get` doesn't actually modify the struct. However, it fails with the error "Cannot assign to property: 'a' is a 'let' constant," which is confusing because no mutation is taking place.

It seems that the true error is that it is impossible to construct a `WriteableKeyPath` in the first place for a `let`, which I discovered while trying to write this bug report.

The only solution I've found to this issue so far is to write out the dynamicMemberLookup subscript again using `KeyPath` instead of `WriteableKeyPath`:

    subscript<U>(dynamicMember member: KeyPath<T, U>) -> U {
        backing[keyPath: member]
    } 

I think at minimum the correct approach to solve this issue should be documented, and the compiler should detect this case and suggest this solution.

Ideally, imo the compiler would, whenever possible, synthesize a version of the subscript getter that used KeyPath instead of WriteableKeyPath, or otherwise abstract this away.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA deviation from expected or documented behavior. Also: expected but undesirable behavior.compilerThe Swift compiler itself

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions