swiftlang / swift

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

[SR-12768] Typechecking @Published with a nested property wrapper leads to poor diagnostic #55213

Open swift-ci opened 4 years ago

swift-ci commented 4 years ago
Previous ID SR-12768
Radar rdar://problem/77881309
Original Reporter noahsark769 (JIRA User)
Type Bug
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | Compiler | |Labels | Bug, DiagnosticsQoI, PropertyWrappers, TypeChecker | |Assignee | None | |Priority | Medium | md5: 63bf602a60242938e94b3c2e998c7f8e

Issue Description:

(Swift 5.2.2, Xcode 11.4.1)

$ swift --version
Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51)
Target: x86_64-apple-darwin19.3.0

Using Published (from Foundation) nested with another property wrapper reliably causes a segfault. The minimal reproduction example I've managed to find is:

import Foundation
@propertyWrapperstruct Wrapper {
    var wrappedValue: String {
        get { "" }
        set { }
    }
    init(wrappedValue: String) {
        // do nothing
    }
}

class Test {
    @Published
    @Wrapper
    var string: String = ""
}

This causes a segfault with the following stacktrace:

$ swift Test.swift
<unknown>:0: error: key path value type 'String' cannot be converted to contextual type 'Wrapper'
Stack dump:
0.  Program arguments: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -interpret Test.swift -enable-objc-interop -stack-check -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -color-diagnostics -module-name Test
1.  Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51)
2.  While evaluating request TypeCheckSourceFileRequest(source_file "Test.swift", 0)
3.  While evaluating request TypeCheckFunctionBodyUntilRequest(Test.(file).Test._@Test.swift:18:9, )
4.  While type-checking getter for string (at Test.swift:18:9)
5.  While evaluating request ParseAbstractFunctionBodyRequest(Test.(file).Test._@Test.swift:18:9)
0  swift                    0x000000010fb924ea PrintStackTraceSignalHandler(void*) + 42
1  swift                    0x000000010fb91cc0 SignalHandler(int) + 352
2  libsystem_platform.dylib 0x00007fff6542842d _sigtramp + 29
3  swift                    0x000000010c528aaf unsigned int llvm::DenseMapInfo<swift::AnyRequest>::getHashValue<swift::AttachedPropertyWrappersRequest>(swift::AttachedPropertyWrappersRequest const&) + 63
4  swift                    0x000000010c599e26 synthesizeAccessorBody(swift::AbstractFunctionDecl*, void*) + 1702
5  swift                    0x000000010c7cd2c8 swift::SimpleRequest<swift::ParseAbstractFunctionBodyRequest, swift::BraceStmt* (swift::AbstractFunctionDecl*), (swift::CacheKind)2>::evaluateRequest(swift::ParseAbstractFunctionBodyRequest const&, swift::Evaluator&) + 168
6  swift                    0x000000010c8e4f5b swift::ParseAbstractFunctionBodyRequest::OutputType swift::evaluateOrDefault<swift::ParseAbstractFunctionBodyRequest>(swift::Evaluator&, swift::ParseAbstractFunctionBodyRequest, swift::ParseAbstractFunctionBodyRequest::OutputType) + 1211
7  swift                    0x000000010c587634 swift::SimpleRequest<swift::TypeCheckFunctionBodyUntilRequest, bool (swift::AbstractFunctionDecl*, swift::SourceLoc), (swift::CacheKind)1>::evaluateRequest(swift::TypeCheckFunctionBodyUntilRequest const&, swift::Evaluator&) + 756
8  swift                    0x000000010c58e6ee swift::TypeCheckFunctionBodyUntilRequest::OutputType swift::evaluateOrDefault<swift::TypeCheckFunctionBodyUntilRequest>(swift::Evaluator&, swift::TypeCheckFunctionBodyUntilRequest, swift::TypeCheckFunctionBodyUntilRequest::OutputType) + 718
9  swift                    0x000000010c5bd24e swift::TypeCheckSourceFileRequest::evaluate(swift::Evaluator&, swift::SourceFile*, unsigned int) const + 6270
10 swift                    0x000000010c58c6c7 swift::SimpleRequest<swift::TypeCheckSourceFileRequest, bool (swift::SourceFile*, unsigned int), (swift::CacheKind)2>::evaluateRequest(swift::TypeCheckSourceFileRequest const&, swift::Evaluator&) + 23
11 swift                    0x000000010c5bab4e swift::performTypeChecking(swift::SourceFile&, unsigned int) + 1006
12 swift                    0x000000010bbeebcb swift::CompilerInstance::performSemaUpTo(swift::SourceFile::ASTStage_t) + 5915
13 swift                    0x000000010b8dad99 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 10281
14 swift                    0x000000010b85bf53 main + 1283
15 libdyld.dylib            0x00007fff6522f7fd start + 1
16 libdyld.dylib            0x000000000000000b start + 2598176783
Segmentation fault: 11

Please let me know if I can provide more info! Happy to discuss further

swift-ci commented 4 years ago

Comment by Noah Gilmore (JIRA)

Interestingly, this seems to be localized to the Foundation version of Published. The following compiles just fine for me:

@propertyWrapper
struct Published<Value> {
  var wrappedValue: Value

  init(wrappedValue: Value) {
    self.wrappedValue = wrappedValue
  }
}

@propertyWrapper
struct Wrapper {
    var wrappedValue: String {
        get { "" }
        set { }
    }    init(wrappedValue: String) {
        // do nothing
    }
}

class Test {
    @Published
    @Wrapper
    var string: String = ""
}
swift-ci commented 4 years ago

Comment by Owen Voorhees (JIRA)

For anyone looking into this further, I was able to reproduce this without the Foundation import by adding an _enclosingInstance subscript to the outer wrapper:

@propertyWrapper struct Wrapper {
    var wrappedValue: String {
        get { "" }
        set { }
    }
    init(wrappedValue: String) {
        // do nothing
    }
}

@propertyWrapper struct Published<T> {
    var storage: T?
    var wrappedValue: T {
        get { storage! }
        set { }
    }
    init(wrappedValue: T) {
        // do nothing
    }

    static subscript<EnclosingSelf>(
                _enclosingInstance observed: EnclosingSelf,
                wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, T>,
                storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
            ) -> T {
            get {
                observed[keyPath: storageKeyPath].storage!
            }
            set {
                observed[keyPath: storageKeyPath].storage! = newValue
            }
        }
}

class Test {
    @Published
    @Wrapper
    var string: String = ""
}
LucianoPAlmeida commented 4 years ago

cc @hborla@DougGregor

theblixguy commented 4 years ago

On master, I get an error: key path value type 'String' cannot be converted to contextual type 'Wrapper', although it has no source location and the code no longer crashes. So, I'd say the crash is fixed, but the diagnostic could probably be improved.

typesanitizer commented 3 years ago

We still have the poor diagnostic on main

@swift-ci create