Class-constrained protocol extension has incorrect mutatingness

At the moment, a property in a class-constrained protocol extension, where the protocol does not impose a class-requirement, would have a mutating setter by default. For example:

protocol P {}
	
class A: P {
  var property: String = ""
}

extension P where Self: A {
  var wrappingProperty: String {
    get { return property }
    set { property = newValue }
  }
}
	
let instance = A()
instance.wrappingProperty = "" // Cannot assign to property: 'instance' is a 'let' constant

@Joe_Groff mentioned that this is a bug, because even though the protocol does not impose a class constraint, it's implied by the Self: A requirement in the extension, so wrappingProperty's setter should be non-mutating instead. This was fixed today (but it's being reverted now).

However, it's been bought to attention that the new behaviour can be source breaking in some cases. For example:

protocol P {
  var property: String { get set }
}

extension P where Self : AnyObject {
  var wrappingProperty: String {
    get { property }
    // This will now error and ask the user to explicitly 
    // mark the setter as mutating.
    set { property = newValue }
  }
}

What is the best way to handle this? Is the behaviour acceptable or does this change need to go through Swift Evolution? or should we leave this unfixed? cc @John_McCall @Douglas_Gregor

It's worth noting that what is potentially source breaking is the declaration site only and not any use sites.

Currently, wrappingProperty can't be set for any let constant; if the issue is fixed, then that's the case only when it wraps a property that can potentially reassign self.

I think the best way to handle this is to improve the diagnostic that's emitted where there is source breakage: explain to the user that the setter must be marked as mutating because property is a protocol requirement that can reassign self; if the protocol is under the user's control as well, offer both fix-its (mark the setter of wrappingProperty as mutating, or mark the protocol requirement property as nonmutating set).

2 Likes

Do we have examples of source breakages in the compat suite?
Also, this breakage example mentioned here - should it error, in fact? Isn't the extension being constrained to AnyObject imply mutating?

What exactly does P where Self mean?
Is P the protocol or the existential here?
If the latter, what does Self mean in context of the existential?


Just trying to understand these tiny things and complete my knowledge about the existential type:

The protocol.

2 Likes

I think this is unquestionably just a bug that should be fixed and doesn’t require evolution approval. Xiaodi’s point that such an implementation can’t actually be used is important.

3 Likes

Thanks! Could you raise this with the Core Team? Or is it not necessary? Once I get confirmation, I can go ahead and un-revert the patch and tweak the diagnostics.

Sure, I’ll raise it to the rest of the Core Team.

1 Like

Thanks!

Any update on this?

Thanks for the reminder. General agreement is that it's okay to treat this as a bug fix, so it doesn't need an evolution proposal.

5 Likes

Thanks! I’ll unrevert the fix.