Skip to content

[SR-11138] Nested property wrappers result in too strict 'mutability' keywords. #53534

@mortenbekditlevsen

Description

@mortenbekditlevsen
Previous ID SR-11138
Radar rdar://problem/53407949
Original Reporter @mortenbekditlevsen
Type Bug
Status Closed
Resolution Done
Environment

Swift 5.1 prerelease from Xcode 11 beta 3.

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

md5: 276670fd883a17c2a90bf7d0fb6d93f4

Issue Description:

With the Property Wrapper feature, the synthesized setter and getter are marked as mutating or nonmutating based on the corresponding keywords on the getter and setter of the wrappedValue.

For nested property wrappers this currently appears to be decided solely on the outermost wrapper - and this behavior could be improved.

Imagine the property wrappers A and B and the following nesting:

@A @B var c: Int

If A is a value type and has a mutating setter, and B is a reference type (with a nonmutating setter), this will currently (in Xcode 11 beta 3) generate a mutating setter for c.

But the synthesized setter is:

set { _c.wrappedValue.wrappedValue = newValue }

In other words - only the getter of A is called while just the setter on reference type B is called.

Since B is a reference type in this example nothing on _c is being mutated, so the setter might as well have been generated as nonmutating.

For a discussion about this please see the Swift Forums thread: Question about nested property wrappers and mutability

My own humble attempt at an 'algorithm' for deciding the mutability keywords. I hope that I am not mixing things up:

For a chain of nested property wrappers, the mutating keyword on the synthesized getter can be calculated as follows:

Consider the wrappers from the outermost to the innermost in order:

If a wrapper with a mutating getter appears before one with a nonmutating setter, then the synthesized getter must be mutating - ** otherwise it can be nonmutating.

Similarly for setters:

Consider the wrappers from the outermost to (but not including) the innermost:

If a wrapper with a mutating getter appears before one with a nonmutating setter OR if the innermost wrapper has a mutating setter, then the synthesized setter must be mutating. Otherwise it can be nonmutating.

Examples:

@A @b @C @d @e var f: Int

If A and B have nonmutating getters and C is a reference type (and thus has a nonmutating setter), then even though D and E had mutating getters and setters, both getter and setter of _f can be nonmutating. The mutating getter of D would not matter due to the nonmutating setter of C.

But if A, B, and C had nonmutating getters and mutating setters, then if D had a mutating getter, then both the getter and setter of _f should be mutating.

If A, B, C, D and E all had nonmutating getters and mutating setters, then _f would have a nonmutating getter and a mutating setter.

Metadata

Metadata

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