Skip to content

Commit b8779f7

Browse files
committed
Fix getDirectlyInheritedNominalTypeDecls
getDirectlyInheritedNominalTypeDecls looks for inherited protocols in two places: the actual inheritance clause of a declaration and the trailing where clause. This works fine for normal declarations, but deserialized protocols that have Self constraints have no trailing where clause and appear to have no super-protocol bounds. This means clients can potentially disagree about the structure of the protocol, and any symbols derived from it. The test case demonstrates this problem directly: We build a hollowed-out SwiftUI preview provider then try to dynamically replace a requirement in a Self-constrained protocol extension. The SwiftUI module sees the where clause, but when we go to deserialize the module in the "Preview" the protocol extension sees the wrong inheritance bounds and mis-mangles the call to Self.view(for:ofType). The fix is to ask the requirement signature for Self requirements that would normally appear on a trailing where clause. Resolves rdar://57150777
1 parent 448d9df commit b8779f7

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

include/swift/AST/TypeCheckRequests.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,8 @@ class RequirementSignatureRequest :
334334
friend SimpleRequest;
335335

336336
// Evaluation.
337-
llvm::Expected<ArrayRef<Requirement>> evaluate(Evaluator &evaluator, ProtocolDecl *proto) const;
337+
llvm::Expected<ArrayRef<Requirement>> evaluate(Evaluator &evaluator,
338+
ProtocolDecl *proto) const;
338339

339340
public:
340341
// Separate caching.

lib/AST/NameLookup.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,6 +2435,27 @@ swift::getDirectlyInheritedNominalTypeDecls(
24352435
// FIXME: Refactor SelfBoundsFromWhereClauseRequest to dig out
24362436
// the source location.
24372437
SourceLoc loc = SourceLoc();
2438+
2439+
// For a deserialized protocol, the where clause isn't going to tell us
2440+
// anything. Ask the requirement signature instead.
2441+
if (protoDecl->wasDeserialized()) {
2442+
auto protoSelfTy = protoDecl->getSelfInterfaceType();
2443+
for (auto &req : protoDecl->getRequirementSignature()) {
2444+
// Dig out a conformance requirement...
2445+
if (req.getKind() != RequirementKind::Conformance)
2446+
continue;
2447+
2448+
// constraining Self.
2449+
if (!req.getFirstType()->isEqual(protoSelfTy))
2450+
continue;
2451+
2452+
result.emplace_back(
2453+
loc, req.getSecondType()->castTo<ProtocolType>()->getDecl());
2454+
}
2455+
return result;
2456+
}
2457+
2458+
// Else we have access to this information on the where clause.
24382459
auto selfBounds = getSelfBoundsFromWhereClause(decl);
24392460
anyObject |= selfBounds.anyObject;
24402461

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
protocol View {
2+
associatedtype Body: View
3+
4+
var body: Self.Body { get }
5+
}
6+
7+
extension Never: View {
8+
typealias Body = Never
9+
10+
var body: Self.Body { fatalError() }
11+
}
12+
13+
struct AnyView: View {
14+
var body: Never { fatalError() }
15+
}
16+
17+
protocol ViewModelRenderable {
18+
var view: AnyView { get }
19+
}
20+
21+
extension ViewModelRenderable where Self: SectionModel {
22+
static func view<Model, MyView: SectionViewModelView>(for model: Model, ofType: MyView.Type) -> AnyView where Model == MyView.BodyViewModel {
23+
fatalError()
24+
}
25+
}
26+
27+
protocol SectionViewModelView where Self: View {
28+
associatedtype BodyViewModel: SectionModel
29+
30+
init(bodyViewModel: BodyViewModel)
31+
}
32+
33+
public protocol SectionModel: Codable {
34+
var sectionName: String { get }
35+
}
36+
37+
extension SectionModel {
38+
public var sectionName: String {
39+
"Hello world!"
40+
}
41+
}
42+
43+
struct NewUserModel: SectionModel {
44+
}
45+
46+
extension NewUserModel: ViewModelRenderable {
47+
var view: AnyView { Self.view(for: self, ofType: NewUserView.self) }
48+
}
49+
50+
struct NewUserView: SectionViewModelView {
51+
var bodyViewModel: NewUserModel = .init()
52+
53+
var body: Never {
54+
fatalError()
55+
}
56+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift-dylib(%t/%target-library-name(TestModuleLinking)) -module-name TestModuleLinking -emit-module -emit-module-path %t/TestModuleLinking.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_protocol_self_orig.swift -Xfrontend -enable-private-imports -Xfrontend -enable-implicit-dynamic
3+
// RUN: %target-build-swift -I%t -L%t -lTestModuleLinking -o %t/main %target-rpath(%t) %s -swift-version 5
4+
// RUN: %target-codesign %t/main %t/%target-library-name(TestModuleLinking)
5+
// RUN: %target-run %t/main %t/%target-library-name(TestModuleLinking) %t/%target-library-name(TestModuleLinking)
6+
7+
// N.B. We're not actually executing anything here - all we care about is
8+
// if the linker is content.
9+
10+
// REQUIRES: executable_test
11+
12+
// UNSUPPORTED: swift_test_mode_optimize_none_with_implicit_dynamic
13+
14+
@_private(sourceFile: "dynamic_replacement_protocol_self_orig.swift") import TestModuleLinking
15+
16+
extension NewUserModel {
17+
@_dynamicReplacement(for: view) private var __preview__view: AnyView {
18+
Self.view(for: self, ofType: NewUserView.self)
19+
}
20+
}
21+
22+
typealias NewUserModel = TestModuleLinking.NewUserModel

0 commit comments

Comments
 (0)