swiftlang / swift

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

Assertion Failure in swift-frontend using _openExistential #65233

Open schlossmichael opened 1 year ago

schlossmichael commented 1 year ago

Description

Trying to compile the following code results in the assertion failure (!type->hasArchetype() && "Archetype in interface type"), function cacheResult, file TypeCheckRequests.cpp, line 1050 :

import Combine
import Foundation

protocol Foo {}

protocol BatchableFoo: Foo {
    var key: String { get }
    func reduce(_ other: Self) -> Self
}

extension BatchableFoo {
    var key: String { String(describing: type(of: self)) }
}

struct Bar: BatchableFoo {
    let ids: [String]
    let source: String

    func reduce(_ other: Bar) -> Bar {
        .init(ids: ids + other.ids, source: source)
    }
}

struct Third: Foo {
    let source: String
}

class Batcher<Foo: BatchableFoo> {
    var foo: Foo

    init(foo: Foo) {
        self.foo = foo
    }

    func add(_ newFoo: Foo) {
        foo = foo.reduce(newFoo)
    }
}

class DoTheThing {
    var currentBatcher: (any BatchableFoo, Any)?
    let publisher: PassthroughSubject<Foo, Never> = .init()

    private func getCurrentBatcher<T: BatchableFoo>(_ type: T) -> Batcher<T>? {
        guard let currentBatcher else { return nil }
        return currentBatcher.1 as? Batcher<T>
    }

    func publish(_ foo: Foo) {
        if let currentBatcher {
            _openExistential(currentBatcher.0) { type in
                if let batcher = getCurrentBatcher(type.self) {
                    publisher.send(batcher.foo)
                    self.currentBatcher = nil
                }
            }
        }
    }

    func publish<BE: BatchableFoo>(_ batchableFoo: BE) {
        if let batcher = currentBatcher?.1 as? Batcher<BE> {
            if batcher.foo.key == batchableFoo.key {
                batcher.add(batchableFoo)
            } else {
                publisher.send(batcher.foo)
                currentBatcher = (batchableFoo, Batcher(foo: batchableFoo))
            }
        } else {
            currentBatcher = (batchableFoo, Batcher(foo: batchableFoo))
        }
    }
}

let _class = DoTheThing()

let cancellable = _class.publisher.sink { foo in
    print(foo)
}

print("1")
withExtendedLifetime(cancellable) {
    _class.publish(Bar(ids: ["a"], source: "list"))
    _class.publish(Bar(ids: ["b"], source: "list"))

    _class.publish(Third(source: "page"))

    print("hi")
}

RunLoop.main.run()

Steps to reproduce

  1. Compile the above code with swiftc against the macOS 13 SDK (/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.sdk)
  2. 💥 output:
<userName>:~/ $ /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2023-04-11-a.xctoolchain/usr/bin/swiftc /Users/<userName>/Desktop/MyPlayground.playground/Contents.swift -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.sdk                                                                                          [15:58:02]
error: compile command failed due to signal 6 (use -v to see invocation)
Assertion failed: (!type->hasArchetype() && "Archetype in interface type"), function cacheResult, file TypeCheckRequests.cpp, line 1050.
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
0.  Program arguments: /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2023-04-11-a.xctoolchain/usr/bin/swift-frontend -frontend -c -primary-file /Users/<userName>/Desktop/MyPlayground.playground/Contents.swift -target arm64-apple-macosx13.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.sdk -color-diagnostics -new-driver-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2023-04-11-a.xctoolchain/usr/bin/swift-driver -empty-abi-descriptor -plugin-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2023-04-11-a.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2023-04-11-a.xctoolchain/usr/local/lib/swift/host/plugins -resource-dir /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2023-04-11-a.xctoolchain/usr/lib/swift -module-name Contents -target-sdk-version 13.3 -target-sdk-name macosx13.3 -o /var/folders/b0/dwx50m3s2bb31gm5wq2wkwwh0000gq/T/TemporaryDirectory.NH4d2H/Contents-1.o
1.  Apple Swift version 5.9-dev (LLVM 8277d73ee656251, Swift 4de53fc2e7cee4d)
2.  Compiling with the current language version
3.  While evaluating request TypeCheckSourceFileRequest(source_file "/Users/<userName>/Desktop/MyPlayground.playground/Contents.swift")
4.  While evaluating request TypeCheckFunctionBodyRequest(Contents.(file).DoTheThing.publish@/Users/<userName>/Desktop/MyPlayground.playground/Contents.swift:49:10)
5.  While type-checking statement at [/Users/<userName>/Desktop/MyPlayground.playground/Contents.swift:49:30 - line:58:5] RangeText="{
        if let currentBatcher {
            _openExistential(currentBatcher.0) { type in
                if let batcher = getCurrentBatcher(type.self) {
                    publisher.send(batcher.foo)
                    self.currentBatcher = nil
                }
            }
        }
    "
6.  While type-checking statement at [/Users/<userName>/Desktop/MyPlayground.playground/Contents.swift:50:9 - line:57:9] RangeText="if let currentBatcher {
            _openExistential(currentBatcher.0) { type in
                if let batcher = getCurrentBatcher(type.self) {
                    publisher.send(batcher.foo)
                    self.currentBatcher = nil
                }
            }
        "
7.  While type-checking statement at [/Users/<userName>/Desktop/MyPlayground.playground/Contents.swift:50:31 - line:57:9] RangeText="{
            _openExistential(currentBatcher.0) { type in
                if let batcher = getCurrentBatcher(type.self) {
                    publisher.send(batcher.foo)
                    self.currentBatcher = nil
                }
            }
        "
8.  While type-checking expression at [/Users/<userName>/Desktop/MyPlayground.playground/Contents.swift:51:13 - line:56:13] RangeText="_openExistential(currentBatcher.0) { type in
                if let batcher = getCurrentBatcher(type.self) {
                    publisher.send(batcher.foo)
                    self.currentBatcher = nil
                }
            "
9.  While type-checking-target starting at /Users/<userName>/Desktop/MyPlayground.playground/Contents.swift:51:13
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  swift-frontend           0x000000010734db70 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x000000010734ce74 llvm::sys::RunSignalHandlers() + 128
2  swift-frontend           0x000000010734e1b0 SignalHandler(int) + 304
3  libsystem_platform.dylib 0x000000019f07ea84 _sigtramp + 56
4  libsystem_pthread.dylib  0x000000019f04fc28 pthread_kill + 288
5  libsystem_c.dylib        0x000000019ef5dae8 abort + 180
6  libsystem_c.dylib        0x000000019ef5ce44 err + 0
7  swift-frontend           0x00000001078651a8 swift::InterfaceTypeRequest::cacheResult(swift::Type) const (.cold.5) + 0
8  swift-frontend           0x0000000103caa2b8 swift::InterfaceTypeRequest::cacheResult(swift::Type) const + 160
9  swift-frontend           0x0000000103a1c090 swift::ValueDecl::setInterfaceType(swift::Type) + 40
10 swift-frontend           0x00000001036f4f54 swift::TypeChecker::coerceParameterListToType(swift::ParameterList*, swift::AnyFunctionType*) + 240
11 swift-frontend           0x00000001034ae388 swift::constraints::ConstraintSystem::applySolution(swift::constraints::Solution&, swift::AnyFunctionRef, swift::DeclContext*&, std::__1::function<llvm::Optional<swift::constraints::SyntacticElementTarget> (swift::constraints::SyntacticElementTarget)>) + 260
12 swift-frontend           0x000000010347e6f4 (anonymous namespace)::ExprWalker::rewriteFunction(swift::AnyFunctionRef) + 92
13 swift-frontend           0x000000010347ddc8 (anonymous namespace)::ExprWalker::walkToExprPre(swift::Expr*) + 104
14 swift-frontend           0x00000001039d3bd8 (anonymous namespace)::Traversal::doIt(swift::Expr*) + 40
15 swift-frontend           0x00000001039d7cd8 (anonymous namespace)::Traversal::visit(swift::ArgumentList*) + 84
16 swift-frontend           0x00000001039d44f4 (anonymous namespace)::Traversal::doIt(swift::ArgumentList*) + 108
17 swift-frontend           0x00000001039d4620 swift::ASTVisitor<(anonymous namespace)::Traversal, swift::Expr*, swift::Stmt*, bool, swift::Pattern*, bool, void>::visit(swift::Expr*) + 164
18 swift-frontend           0x00000001039d3c34 (anonymous namespace)::Traversal::doIt(swift::Expr*) + 132
19 swift-frontend           0x00000001039d3ba4 swift::Expr::walk(swift::ASTWalker&) + 32
20 swift-frontend           0x0000000103477d00 (anonymous namespace)::ExprWalker::rewriteTarget(swift::constraints::SyntacticElementTarget) + 472
21 swift-frontend           0x00000001034777e4 swift::constraints::ConstraintSystem::applySolution(swift::constraints::Solution&, swift::constraints::SyntacticElementTarget) + 712
22 swift-frontend           0x0000000103677a48 swift::TypeChecker::typeCheckTarget(swift::constraints::SyntacticElementTarget&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 376
23 swift-frontend           0x0000000103677878 swift::TypeChecker::typeCheckExpression(swift::constraints::SyntacticElementTarget&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 184
24 swift-frontend           0x0000000103677738 swift::TypeChecker::typeCheckExpression(swift::Expr*&, swift::DeclContext*, swift::constraints::ContextualTypeInfo, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 88
25 swift-frontend           0x0000000103750300 (anonymous namespace)::StmtChecker::typeCheckASTNode(swift::ASTNode&) + 236
26 swift-frontend           0x00000001037531f8 swift::ASTVisitor<(anonymous namespace)::StmtChecker, void, swift::Stmt*, void, void, void, void>::visit(swift::Stmt*) + 328
27 swift-frontend           0x000000010375307c bool (anonymous namespace)::StmtChecker::typeCheckStmt<swift::Stmt>(swift::Stmt*&) + 136
28 swift-frontend           0x0000000103753684 swift::ASTVisitor<(anonymous namespace)::StmtChecker, void, swift::Stmt*, void, void, void, void>::visit(swift::Stmt*) + 1492
29 swift-frontend           0x000000010375307c bool (anonymous namespace)::StmtChecker::typeCheckStmt<swift::Stmt>(swift::Stmt*&) + 136
30 swift-frontend           0x00000001037502b0 (anonymous namespace)::StmtChecker::typeCheckASTNode(swift::ASTNode&) + 156
31 swift-frontend           0x00000001037531f8 swift::ASTVisitor<(anonymous namespace)::StmtChecker, void, swift::Stmt*, void, void, void, void>::visit(swift::Stmt*) + 328
32 swift-frontend           0x00000001037518ac bool (anonymous namespace)::StmtChecker::typeCheckStmt<swift::BraceStmt>(swift::BraceStmt*&) + 136
33 swift-frontend           0x0000000103750f20 swift::TypeCheckFunctionBodyRequest::evaluate(swift::Evaluator&, swift::AbstractFunctionDecl*) const + 1116
34 swift-frontend           0x0000000103af3758 llvm::Expected<swift::TypeCheckFunctionBodyRequest::OutputType> swift::Evaluator::getResultUncached<swift::TypeCheckFunctionBodyRequest>(swift::TypeCheckFunctionBodyRequest const&) + 404
35 swift-frontend           0x0000000103af34dc llvm::Expected<swift::TypeCheckFunctionBodyRequest::OutputType> swift::Evaluator::getResultCached<swift::TypeCheckFunctionBodyRequest, (void*)0>(swift::TypeCheckFunctionBodyRequest const&) + 140
36 swift-frontend           0x0000000103a3e530 swift::TypeCheckFunctionBodyRequest::OutputType swift::evaluateOrDefault<swift::TypeCheckFunctionBodyRequest>(swift::Evaluator&, swift::TypeCheckFunctionBodyRequest, swift::TypeCheckFunctionBodyRequest::OutputType) + 52
37 swift-frontend           0x0000000103788e54 swift::TypeCheckSourceFileRequest::evaluate(swift::Evaluator&, swift::SourceFile*) const + 308
38 swift-frontend           0x000000010378b27c llvm::Expected<swift::TypeCheckSourceFileRequest::OutputType> swift::Evaluator::getResultUncached<swift::TypeCheckSourceFileRequest>(swift::TypeCheckSourceFileRequest const&) + 400
39 swift-frontend           0x000000010378b01c llvm::Expected<swift::TypeCheckSourceFileRequest::OutputType> swift::Evaluator::getResultCached<swift::TypeCheckSourceFileRequest, (void*)0>(swift::TypeCheckSourceFileRequest const&) + 124
40 swift-frontend           0x0000000103788c14 swift::TypeCheckSourceFileRequest::OutputType swift::evaluateOrDefault<swift::TypeCheckSourceFileRequest>(swift::Evaluator&, swift::TypeCheckSourceFileRequest, swift::TypeCheckSourceFileRequest::OutputType) + 44
41 swift-frontend           0x000000010282a8d0 bool llvm::function_ref<bool (swift::SourceFile&)>::callback_fn<swift::CompilerInstance::performSema()::$_7>(long, swift::SourceFile&) + 16
42 swift-frontend           0x000000010282588c swift::CompilerInstance::forEachFileToTypeCheck(llvm::function_ref<bool (swift::SourceFile&)>) + 76
43 swift-frontend           0x0000000102825820 swift::CompilerInstance::performSema() + 76
44 swift-frontend           0x00000001026a74b8 withSemanticAnalysis(swift::CompilerInstance&, swift::FrontendObserver*, llvm::function_ref<bool (swift::CompilerInstance&)>, bool) + 60
45 swift-frontend           0x000000010269a4d4 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 592
46 swift-frontend           0x0000000102699634 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 2840
47 swift-frontend           0x0000000102523efc swift::mainEntry(int, char const**) + 3304
48 dyld                     0x000000019ecf7f28 start + 2236
error: fatalError

Expected behavior

Code compiles and either finished successfully or outputs an error diagnostic

Environment

xedin commented 1 year ago

@jckarter As far as I remember it not currently possible to use closures with _openExistential, is that right? We really need a diagnostic for this...

AnthonyLatsis commented 1 year ago

The minimal reproducer is surprisingly small:

protocol P {}

func test(_ foo: any P) {
  _openExistential(foo) { _ in }
}