Skip to content

Commit 8b65deb

Browse files
committed
IRGen: Handle protocol inheritance in witness_method lowering.
If the conformance in a witness_method instruction represented a derived protocol instead of the exact protocol of the desired method, we would crash. Handle this by drilling down to the exact conformance needed before forming a witness ref. Fixes rdar://problem/26633668.
1 parent 318abe0 commit 8b65deb

File tree

4 files changed

+50
-3
lines changed

4 files changed

+50
-3
lines changed

include/swift/AST/ProtocolConformanceRef.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ class ProtocolConformanceRef {
8282

8383
/// Return the protocol requirement.
8484
ProtocolDecl *getRequirement() const;
85+
86+
/// Get the inherited conformance corresponding to the given protocol.
87+
/// Returns `this` if `parent` is already the same as the protocol this
88+
/// conformance represents.
89+
ProtocolConformanceRef getInherited(ProtocolDecl *parent) const;
8590

8691
void dump() const;
8792
void dump(llvm::raw_ostream &out, unsigned indent = 0) const;

lib/AST/ProtocolConformance.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,30 @@ ProtocolDecl *ProtocolConformanceRef::getRequirement() const {
4949
}
5050
}
5151

52+
ProtocolConformanceRef
53+
ProtocolConformanceRef::getInherited(ProtocolDecl *parent) const {
54+
assert((getRequirement() == parent ||
55+
getRequirement()->inheritsFrom(parent)) &&
56+
"not a parent of this protocol");
57+
58+
if (parent == getRequirement())
59+
return *this;
60+
61+
// For an abstract requirement, simply produce a new abstract requirement
62+
// for the parent.
63+
if (isAbstract()) {
64+
return ProtocolConformanceRef(parent);
65+
}
66+
67+
// Navigate concrete conformances.
68+
if (isConcrete()) {
69+
return ProtocolConformanceRef(
70+
getConcrete()->getInheritedConformance(parent));
71+
}
72+
73+
llvm_unreachable("unhandled ProtocolConformanceRef");
74+
}
75+
5276
void *ProtocolConformance::operator new(size_t bytes, ASTContext &context,
5377
AllocationArena arena,
5478
unsigned alignment) {

lib/IRGen/GenProto.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,9 +2621,9 @@ irgen::emitWitnessMethodValue(IRGenFunction &IGF,
26212621
ProtocolConformanceRef conformance,
26222622
Explosion &out) {
26232623
auto fn = cast<AbstractFunctionDecl>(member.getDecl());
2624-
2625-
assert(cast<ProtocolDecl>(fn->getDeclContext())
2626-
== conformance.getRequirement());
2624+
auto fnProto = cast<ProtocolDecl>(fn->getDeclContext());
2625+
2626+
conformance = conformance.getInherited(fnProto);
26272627

26282628
// Find the witness table.
26292629
// FIXME conformance for concrete type
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-swift-frontend -emit-ir -verify %s
2+
3+
protocol BaseProtocol {
4+
static func run()
5+
}
6+
7+
protocol DerivedProtocol: BaseProtocol {
8+
}
9+
10+
struct Foo: DerivedProtocol {
11+
static func run() { }
12+
}
13+
14+
@inline(never)
15+
func test() {
16+
let t: DerivedProtocol.Type = Foo.self
17+
t.run()
18+
}

0 commit comments

Comments
 (0)