Compiler crash with protocol and associated type

The following code results in a segmentation fault and crashes the copmpiler:

struct Action {
    var intVar: Int
    var strVar: String
}

protocol TestDelegate: AnyObject {
    associatedtype ActionType
    var actions: [ActionType] { get set }
}

class TestDelegateImpl: TestDelegate {
    typealias ActionType = Action
    var actions: [Action] = []
}

class TestViewController {
    var testDelegate: (any TestDelegate)?
    func testFunc() {
        testDelegate?.actions.removeAll()
    }
}

let tVC = TestViewController()
tVC.testDelegate = TestDelegateImpl()
tVC.testFunc()

The stack dump:

...
1.	Apple Swift version 5.7.1 (swiftlang-5.7.1.135.3 clang-1400.0.29.51)
2.	Compiling with the current language version
3.	While evaluating request TypeCheckSourceFileRequest(source_file "Contents.swift")
4.	While evaluating request TypeCheckFunctionBodyRequest(Contents.(file).TestViewController.testFunc()@Contents.swift:18:10)
5.	While type-checking statement at [Contents.swift:18:21 - line:20:5] RangeText="{
        testDelegate?.actions.removeAll()
    "
6.	While type-checking expression at [Contents.swift:19:9 - line:19:41] RangeText="testDelegate?.actions.removeAll("
7.	While type-checking-target starting at Contents.swift:19:31
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           0x00000001078135b0 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x00000001078125b4 llvm::sys::RunSignalHandlers() + 112
2  swift-frontend           0x0000000107813c34 SignalHandler(int) + 344
3  libsystem_platform.dylib 0x00000001a29542a4 _sigtramp + 56
4  swift-frontend           0x0000000103939b38 (anonymous namespace)::ExprRewriter::coerceToType(swift::Expr*, swift::Type, swift::constraints::ConstraintLocatorBuilder, llvm::Optional<swift::Pattern*>) + 16868
5  swift-frontend           0x00000001039388c8 (anonymous namespace)::ExprRewriter::coerceToType(swift::Expr*, swift::Type, swift::constraints::ConstraintLocatorBuilder, llvm::Optional<swift::Pattern*>) + 12148
6  swift-frontend           0x000000010395c5e8 (anonymous namespace)::ExprRewriter::closeExistential(swift::Expr*&, swift::constraints::ConstraintLocatorBuilder, bool) + 340
7  swift-frontend           0x00000001039547c0 (anonymous namespace)::ExprRewriter::buildMemberRef(swift::Expr*, swift::SourceLoc, swift::constraints::SelectedOverload, swift::DeclNameLoc, swift::constraints::ConstraintLocatorBuilder, swift::constraints::ConstraintLocatorBuilder, bool, swift::AccessSemantics) + 11204
8  swift-frontend           0x0000000103941f50 swift::ASTVisitor<(anonymous namespace)::ExprRewriter, swift::Expr*, void, void, void, void, void>::visit(swift::Expr*) + 8156
9  swift-frontend           0x000000010393cd58 (anonymous namespace)::ExprWalker::walkToExprPost(swift::Expr*) + 24
10 swift-frontend           0x0000000103f944e8 swift::ASTVisitor<(anonymous namespace)::Traversal, swift::Expr*, swift::Stmt*, bool, swift::Pattern*, bool, void>::visit(swift::Expr*) + 268
11 swift-frontend           0x0000000103f94538 swift::ASTVisitor<(anonymous namespace)::Traversal, swift::Expr*, swift::Stmt*, bool, swift::Pattern*, bool, void>::visit(swift::Expr*) + 348
12 swift-frontend           0x0000000103f9445c swift::ASTVisitor<(anonymous namespace)::Traversal, swift::Expr*, swift::Stmt*, bool, swift::Pattern*, bool, void>::visit(swift::Expr*) + 128
13 swift-frontend           0x0000000103932a78 (anonymous namespace)::ExprWalker::rewriteTarget(swift::constraints::SolutionApplicationTarget) + 376
14 swift-frontend           0x00000001039325ec swift::constraints::ConstraintSystem::applySolution(swift::constraints::Solution&, swift::constraints::SolutionApplicationTarget) + 5852
15 swift-frontend           0x0000000103b9a104 swift::TypeChecker::typeCheckTarget(swift::constraints::SolutionApplicationTarget&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 688
16 swift-frontend           0x0000000103b99de4 swift::TypeChecker::typeCheckExpression(swift::constraints::SolutionApplicationTarget&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) + 400
17 swift-frontend           0x0000000103c74564 (anonymous namespace)::StmtChecker::typeCheckASTNode(swift::ASTNode&) + 300
18 swift-frontend           0x0000000103c77a64 swift::ASTVisitor<(anonymous namespace)::StmtChecker, void, swift::Stmt*, void, void, void, void>::visit(swift::Stmt*) + 328
19 swift-frontend           0x0000000103c76540 bool (anonymous namespace)::StmtChecker::typeCheckStmt<swift::BraceStmt>(swift::BraceStmt*&) + 300
20 swift-frontend           0x0000000103c7538c swift::TypeCheckFunctionBodyRequest::evaluate(swift::Evaluator&, swift::AbstractFunctionDecl*) const + 1276
21 swift-frontend           0x000000010406ee64 llvm::Expected<swift::TypeCheckFunctionBodyRequest::OutputType> swift::Evaluator::getResultUncached<swift::TypeCheckFunctionBodyRequest>(swift::TypeCheckFunctionBodyRequest const&) + 588
22 swift-frontend           0x0000000104001e64 swift::TypeCheckFunctionBodyRequest::OutputType swift::evaluateOrDefault<swift::TypeCheckFunctionBodyRequest>(swift::Evaluator&, swift::TypeCheckFunctionBodyRequest, swift::TypeCheckFunctionBodyRequest::OutputType) + 116
23 swift-frontend           0x0000000103cb1db0 swift::TypeCheckSourceFileRequest::evaluate(swift::Evaluator&, swift::SourceFile*) const + 792
24 swift-frontend           0x0000000103cb4960 llvm::Expected<swift::TypeCheckSourceFileRequest::OutputType> swift::Evaluator::getResultUncached<swift::TypeCheckSourceFileRequest>(swift::TypeCheckSourceFileRequest const&) + 576
25 swift-frontend           0x0000000103cb1a44 swift::performTypeChecking(swift::SourceFile&) + 120
26 swift-frontend           0x0000000102ed2b8c bool llvm::function_ref<bool (swift::SourceFile&)>::callback_fn<swift::CompilerInstance::performSema()::$_6>(long, swift::SourceFile&) + 16
27 swift-frontend           0x0000000102ecd300 swift::CompilerInstance::forEachFileToTypeCheck(llvm::function_ref<bool (swift::SourceFile&)>) + 288
28 swift-frontend           0x0000000102ecd1a4 swift::CompilerInstance::performSema() + 148
29 swift-frontend           0x0000000102e602ac swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 4364
30 swift-frontend           0x0000000102e013c0 swift::mainEntry(int, char const**) + 3940
31 dyld                     0x00000001a25fbe50 start + 2544
zsh: segmentation fault  swift Contents.swift

Anyone experienced this, or do I overlook somethinig? I would expect this should work, but it seems as it has problems with type checking testDelegate?.actions.removeAll().

This code is crashing the compiler. The compiler should never crash; it should either work or generate a meaningful diagnostic. So, issues like this are automatically bugworthy. For general advice on how to file bugs, see Bug Reporting: How and Why?. However, in this case I recommend that you file a bug with the Swift project itself.

Please post your bug number, just for the record.

Having said that…

I would expect this should work

This is more complex than you might think. For the moment, let’s rewrite your testFunc() method like so:

func testFunc() {
    if let d = self.testDelegate {
        let a = d.actions
        print(a)
    }
}

In this snippet the type of d is any TestDelegate; so far so good. But what’s the type of a? I guess you could argue that it should be [Any], but that’s quite a stretch. I think this crash is caused by the compiler being unable to answer that question.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks. I have filed a bug on the Swift project: https://github.com/apple/swift/issues/62219

The Feedback ID: FB11811647

This is more complex than you might think. For the moment, let’s rewrite your testFunc() method like so:

Thank you for the code, but this does result in the same crash for me. I would have expected that the type of d is (any TestDelegate) therefore it will identify actions as being of type [ActionType]. And then in the actual implementation it would get resolved into [Action]. Shouldn't I expect this automatic deduction? Or am I doing something conceptually wrong?

I have filed a bug on the Swift project

Ta.

therefore it will identify actions as being of type [ActionType].

ActionType isn’t a type. It’s the name of the associated type for your protocol.

And then in the actual implementation it would get resolved into [Action].

That can’t happen because you’re using any TestDelegate, which means that the actual type can vary at runtime. Take your example and clone the TestDelegateImpl type as TestDelegateImpl2 and give it a different associated type, Action2. You can now, on the fly, switch the testDelegate property and get different types for a.

Or am I doing something conceptually wrong?

I think the big conceptual bit you’re missing here relates to any. By using any TestDelegate, you’re saying that the client gets to choose the type and they can choose any type they want as long as it conforms to TestDelegate. The TestDelegate conformance means that it must have an associated type but you don’t put any constraints on that associated type. It could literally be any type. This should be OK because you don’t have any requirements about that type. The actions property is an array and you only call array methods on it. However, array methods require a concrete type. For that to work there would have to be some sort of boxing and unboxing, from Array<Action> to Array<Any>. Swift generally doesn’t do that sort of thing.

As to how you fix that, I think you need to think more clearly about the requirements for actions. If the only thing you need to do is remove all the actions, it’d be better to replace actions with a clearActions() method.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for the explanation. With clearing the actions I succeeded with your proposal. However I wanted to add some actions to the array too, but then I hit the wall again - I guess for similar reasons, which makes me wonder if I'm on the right track now.

Member 'addElement' cannot be used on value of type 'any P'; consider using a generic constraint instead

Here is my latest code snippet for this (as someone at the swift repo replied as well): https://github.com/apple/swift/issues/62219#issuecomment-1326163588

Maybe in general: Is it possible to have a generic protocol with associated type, which is based on another protocol that uses another associated type?

Is it possible to have a generic protocol with associated type, which is based on another protocol that uses another associated type?

Sure. But when you start pulling these stunts you have to start thinking like the compiler. So this is not “Does this logically make sense?” but rather “What can the compiler prove?”

In the example from your GitHub issue, the failure is because the compiler has no idea that the AType for testClass.pImpl is ActionTypeImpl because it’s traversing through an any P.

I think it might be worth your while taking a step back and learning about Swift generics in general. There’s lots of good info about that topic out there on the ’net. Just make sure you look at recent stuff, because there were big changes this year. The one I specifically recommend is WWDC 2022 Session 110352 Embrace Swift generics.

Once you’re done, consider asking your follow-up questions over on Swift Forums > Using Swift. The folks over there know more about this than I do, and there are more of them (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Compiler crash with protocol and associated type
 
 
Q