Hi community, I have run into a compiler error when implementing a feature, and then ran into a compiler crash while trying to fix the error. I am not sure how to resolve the problem, so I hope I can get some help here.
The feature basically is to have a protocol
requirement in ModuleA that requires any type conforms to this protocol
to provide an implementation of a specific @ViewBuilder function
that generates View
s. The concrete implementation will be deployed in the main App, and an instance of this concrete type will be assigned to ModuleA by the main App during runtime. Here is what it looks like:
Requirement in ModuleA
:
enum TestViewType: Hashable, Sendable {
case empty
case color
}
protocol TestViewCreatorProtocol {
associatedtype TestView: View
@ViewBuilder
func view(for viewType: TestViewType) -> TestView
}
Implementation in main App
:
struct TestViewCreator: TestViewCreatorProtocol {
@ViewBuilder
func view(for viewType: TestViewType) -> some View {
switch viewType {
case .empty:
EmptyView()
case .color:
Color.red
}
}
}
Usage in ModuleA
:
struct TestView: View {
let viewCreator: any TestViewCreatorProtocol
var body: some View {
self.viewCreator.view(for: .color)
}
}
At first, the compiler complains:
Type 'any View' cannot conform to 'View'
So I tried to open existential of the viewCreator
of any TestViewCreatorProtocol
to avoid the viewCreator.view(for:)
returns an any View
:
struct TestView: View {
let viewCreator: any TestViewCreatorProtocol
var body: some View {
+ _openExistential(self.viewCreator) {
+ $0.view(for: .color)
+ }
}
}
The previous warning goes away but the complier seems crashed this time:
1. Apple Swift version 6.1.2 (swiftlang-6.1.2.1.2 clang-1700.0.13.5)
2. Compiling with the current language version
3. While evaluating request ASTLoweringRequest(Lowering AST to SIL for file "/Users/z/Development/Playground/Sources/Run/main.swift")
4. While silgen emitFunction SIL function "@$s3Run8TestViewV4bodyQrvg".
for getter for body (at /Users/z/Development/Playground/Sources/Run/main.swift:31:9)
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 0x00000001066aee24 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1 swift-frontend 0x00000001066acc5c llvm::sys::RunSignalHandlers() + 112
2 swift-frontend 0x00000001066af460 SignalHandler(int) + 360
3 libsystem_platform.dylib 0x0000000189348624 _sigtramp + 56
4 swift-frontend 0x0000000101fe8354 swift::InFlightSubstitution::substType(swift::SubstitutableType*, unsigned int) + 48
5 swift-frontend 0x0000000101fede78 (anonymous namespace)::TypeSubstituter::transformGenericTypeParamType(swift::GenericTypeParamType*, swift::TypePosition) + 32
6 swift-frontend 0x0000000101fe9ad0 swift::TypeTransform<(anonymous namespace)::TypeSubstituter>::doIt(swift::Type, swift::TypePosition) + 2244
7 swift-frontend 0x0000000101fedca4 swift::TypeTransform<(anonymous namespace)::TypeSubstituter>::transformSubMap(swift::SubstitutionMap) + 120
8 swift-frontend 0x0000000101fe9800 swift::TypeTransform<(anonymous namespace)::TypeSubstituter>::doIt(swift::Type, swift::TypePosition) + 1524
9 swift-frontend 0x0000000101fe8d1c swift::Type::subst(swift::InFlightSubstitution&) const + 976
10 swift-frontend 0x0000000101fe82c8 swift::Type::subst(llvm::function_ref<swift::Type (swift::SubstitutableType*)>, llvm::function_ref<swift::ProtocolConformanceRef (swift::CanType, swift::Type, swift::ProtocolDecl*)>, swift::SubstOptions) const + 392
11 swift-frontend 0x0000000100e4e064 swift::Lowering::TypeConverter::getContextBoxTypeForCapture(swift::ValueDecl*, swift::CanType, swift::GenericEnvironment*, bool) + 256
12 swift-frontend 0x0000000100fd9988 swift::Lowering::SILGenFunction::emitLocalVariableWithCleanup(swift::VarDecl*, std::__1::optional<swift::MarkUninitializedInst::Kind>, unsigned int, bool) + 940
13 swift-frontend 0x0000000100fd8b28 swift::Lowering::SILGenFunction::emitInitializationForVarDecl(swift::VarDecl*, bool, bool) + 848
14 swift-frontend 0x0000000100fda2b0 swift::Lowering::SILGenFunction::emitPatternBinding(swift::PatternBindingDecl*, unsigned int, bool) + 412
15 swift-frontend 0x0000000100fe1840 swift::ASTVisitor<swift::Lowering::SILGenFunction, void, void, void, void, void, void>::visit(swift::Decl*) + 120
16 swift-frontend 0x00000001010a915c swift::ASTVisitor<(anonymous namespace)::StmtEmitter, void, void, void, void, void, void>::visit(swift::Stmt*) + 6324
17 swift-frontend 0x0000000101024b84 swift::Lowering::SILGenFunction::emitFunction(swift::FuncDecl*) + 496
18 swift-frontend 0x0000000100f6924c swift::Lowering::SILGenModule::emitFunctionDefinition(swift::SILDeclRef, swift::SILFunction*) + 7596
19 swift-frontend 0x0000000100f6a278 swift::Lowering::SILGenModule::emitOrDelayFunction(swift::SILDeclRef) + 232
20 swift-frontend 0x0000000100f67454 swift::Lowering::SILGenModule::emitFunction(swift::FuncDecl*) + 344
21 swift-frontend 0x00000001010c1fd0 (anonymous namespace)::SILGenType::visitFuncDecl(swift::FuncDecl*) + 32
22 swift-frontend 0x00000001010c21d0 (anonymous namespace)::SILGenType::visitAbstractStorageDecl(swift::AbstractStorageDecl*) + 248
23 swift-frontend 0x00000001010c1f40 (anonymous namespace)::SILGenType::visitVarDecl(swift::VarDecl*) + 464
24 swift-frontend 0x00000001010be824 (anonymous namespace)::SILGenType::emitType() + 456
25 swift-frontend 0x0000000100f67098 swift::ASTVisitor<swift::Lowering::SILGenModule, void, void, void, void, void, void>::visit(swift::Decl*) + 100
26 swift-frontend 0x0000000100f6dce0 swift::ASTLoweringRequest::evaluate(swift::Evaluator&, swift::ASTLoweringDescriptor) const + 1568
27 swift-frontend 0x00000001010a6f50 swift::SimpleRequest<swift::ASTLoweringRequest, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>> (swift::ASTLoweringDescriptor), (swift::RequestFlags)17>::evaluateRequest(swift::ASTLoweringRequest const&, swift::Evaluator&) + 208
28 swift-frontend 0x0000000100f7286c swift::ASTLoweringRequest::OutputType swift::Evaluator::getResultUncached<swift::ASTLoweringRequest, swift::ASTLoweringRequest::OutputType swift::evaluateOrFatal<swift::ASTLoweringRequest>(swift::Evaluator&, swift::ASTLoweringRequest)::'lambda'()>(swift::ASTLoweringRequest const&, swift::ASTLoweringRequest::OutputType swift::evaluateOrFatal<swift::ASTLoweringRequest>(swift::Evaluator&, swift::ASTLoweringRequest)::'lambda'()) + 728
29 swift-frontend 0x0000000100500064 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 968
30 swift-frontend 0x0000000100503654 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1764
31 swift-frontend 0x0000000100501fd8 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 3716
32 swift-frontend 0x00000001004860bc swift::mainEntry(int, char const**) + 5428
33 dyld 0x0000000188f6eb98 start + 6076
Am I using the _openExistential
wrong? What is the correct way to achieve the expected behavior?
Note:
I knew that using generics can solve the problem:
struct TestView<ViewCreator: TestViewCreatorProtocol>: View {
let viewCreator: ViewCreator
var body: some View {
self.viewCreator.view(for: .color)
}
}
But this TestView
example is just an oversimplified version of the actual feature in ModuleA
, so the generics actually does not apply here.
Update (07/09/2025)
Our current workaround is to erase the View type by making the func view(for viewType: TestViewType) -> TestView
return AnyView
, but we prefer not to use it.
I am trying to get rid of using AnyView
to avoid efficiency loss where the type-erasure causing the whole view subtree to be re-built and re-drawn unnecessarily when only the specific element wrapped in the AnyView
needs to.