swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.28k stars 10.33k forks source link

[SR-12574] Property wrappers do not recognize initializers that have conditional generic constraints or are declared in extensions #55019

Open pcantrell opened 4 years ago

pcantrell commented 4 years ago
Previous ID SR-12574
Radar rdar://problem/62202293
Original Reporter @pcantrell
Type Bug
Environment Apple Swift version 5.2 (swiftlang-1103.0.32.1 clang-1103.0.32.29) Target: x86_64-apple-darwin19.3.0 Version 11.4 (11E146) macOS 10.15.3
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 1 | |Component/s | Compiler | |Labels | Bug, PropertyWrappers | |Assignee | None | |Priority | Medium | md5: 9c31aa722f3f4952019fbaba0d95acbd

Issue Description:

Here is a property wrapper that provides a no-args initializer, and a class that uses it:

@propertyWrapper
struct Wrappity<Content> {
    var wrappedValue: Content?
    var extraData: Content?

    init(wrappedValue: Content?, extraData: Content?) {
        self.wrappedValue = wrappedValue
        self.extraData = extraData
    }

    init() {
        self.init(wrappedValue: nil, extraData: nil)
    }
}

class ThingerContainer {
    @Wrappity  // using no-args initializer here
    var thinger: Int?
}

That code works.

However, suppose we want to generalize Wrappity so that it can also wrap non-optional types, and conditionally provide that no-args initializer only if Content is optional:

@propertyWrapper
struct Wrappity<Content> {
    var wrappedValue: Content
    var extraData: Content

    init(wrappedValue: Content, extraData: Content) {
        self.wrappedValue = wrappedValue
        self.extraData = extraData
    }

    init<U>() where Content == Optional<U> {
        self.init(wrappedValue: nil, extraData: nil)
    }
}

class ThingerContainer {
    @Wrappity
    var thinger: Int?  // should cause conditional init to match
}

That code fails to compile:

/tmp/ext.swift:16:7: error: class 'ThingerContainer' has no initializers
class ThingerContainer {
      ^
/tmp/ext.swift:18:9: note: stored property 'thinger' without initial value prevents synthesized initializers
    var thinger: Int?
        ^

Similarly, and I expect relatedly, compilation fails with an identical error if we merely move the no-args initializer from the first version into an extension:

@propertyWrapper
struct Wrappity<Content> {
    var wrappedValue: Content?
    var extraData: Content?

    init(wrappedValue: Content?, extraData: Content?) {
        self.wrappedValue = wrappedValue
        self.extraData = extraData
    }
}

extension Wrappity {  // no other changes
    init() {
        self.init(wrappedValue: nil, extraData: nil)
    }
}

class ThingerContainer {
    @Wrappity
    var thinger: Int?
}

I would (perhaps naively) expect all three versions to compile.

beccadax commented 4 years ago

@swift-ci create

lukel97 commented 2 years ago

Ran into this myself here, would be open to taking a look at it. Are there any pointers on where I should start?