Skip to content

Commit 44bf9d1

Browse files
committed
[Runtime] Always fail to find a conditional conformance at runtime.
Proper evaluation of conditional conformances at runtime (e.g., as part of dynamic casting) is too large to tackle in the Swift 4.1 timeframe. For now, record that a conformance is conditional in the protocol conformance record, and always return "does not conform" to such types. Fixes rdar://problem/35761301. (cherry picked from commit 4f8edeb)
1 parent fc37967 commit 44bf9d1

File tree

8 files changed

+77
-10
lines changed

8 files changed

+77
-10
lines changed

docs/ABI/TypeMetadata.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,29 @@ Objective-C ``Protocol`` objects. The layout is as follows:
407407
* **Bit 31** is set by the Objective-C runtime when it has done its
408408
initialization of the protocol record. It is unused by the Swift runtime.
409409

410+
411+
Protocol Conformance Records
412+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
413+
414+
A *protocol conformance record* states that a given type conforms to a
415+
particular protocol. Protocol conformance records are emitted into their own
416+
section, which is scanned by the Swift runtime when needed (e.g., in response to
417+
a `swift_conformsToProtocol()` query). Each protocol conformance record
418+
contains:
419+
420+
- The `protocol descriptor`_ describing the protocol of the conformance.
421+
- A reference to the metadata for the **conforming type**, whose form is
422+
determined by the **protocol conformance flags** described below.
423+
- The **witness table field** that provides access to the witness table
424+
describing the conformance itself; the form of this field is determined by the
425+
**protocol conformance flags** described below.
426+
- The **protocol conformance flags** is a 32-bit field comprised of:
427+
* **Bits 0-3** contain the type metadata record kind, which indicates how
428+
the **conforming type** field is encoded.
429+
* **Bits 4-5** contain the kind of witness table. The value can be one of:
430+
0) The **witness table field** is a reference to a witness table.
431+
1) The **witness table field** is a reference to a **witness table
432+
accessor** function for an unconditional conformance.
433+
2) The **witness table field** is a reference to a **witness table
434+
accessor** function for a conditional conformance.
435+

include/swift/ABI/MetadataValues.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ enum class ProtocolConformanceReferenceKind : unsigned {
212212
/// A function pointer that can be called to access the protocol witness
213213
/// table.
214214
WitnessTableAccessor,
215+
/// A function pointer that can be called to access the protocol witness
216+
/// table whose conformance is conditional on additional requirements that
217+
/// must first be evaluated and then provided to the accessor function.
218+
ConditionalWitnessTableAccessor,
215219
};
216220

217221
// Type metadata record discriminant
@@ -245,7 +249,7 @@ struct TypeMetadataRecordFlags {
245249
struct ProtocolConformanceFlags : public TypeMetadataRecordFlags {
246250
private:
247251
enum : int_type {
248-
ConformanceKindMask = 0x00000010U,
252+
ConformanceKindMask = 0x00000030U,
249253
ConformanceKindShift = 4,
250254
};
251255

include/swift/Runtime/Metadata.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,8 +2454,7 @@ struct TargetProtocolConformanceRecord {
24542454
RelativeDirectPointer<const WitnessTable> WitnessTable;
24552455

24562456
/// A function that produces the witness table given an instance of the
2457-
/// type. The function may return null if a specific instance does not
2458-
/// conform to the protocol.
2457+
/// type.
24592458
RelativeDirectPointer<WitnessTableAccessorFn> WitnessTableAccessor;
24602459
};
24612460

@@ -2560,6 +2559,7 @@ struct TargetProtocolConformanceRecord {
25602559
break;
25612560

25622561
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
2562+
case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
25632563
assert(false && "not witness table");
25642564
}
25652565
return WitnessTable;
@@ -2568,6 +2568,7 @@ struct TargetProtocolConformanceRecord {
25682568
WitnessTableAccessorFn *getWitnessTableAccessor() const {
25692569
switch (Flags.getConformanceKind()) {
25702570
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
2571+
case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
25712572
break;
25722573

25732574
case ProtocolConformanceReferenceKind::WitnessTable:

lib/IRGen/GenDecl.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,7 +2339,8 @@ llvm::Constant *IRGenModule::emitProtocolConformances() {
23392339
auto flags = typeEntity.flags;
23402340
llvm::Constant *witnessTableVar;
23412341
if (!isResilient(conformance->getProtocol(),
2342-
ResilienceExpansion::Maximal)) {
2342+
ResilienceExpansion::Maximal) &&
2343+
conformance->getConditionalRequirements().empty()) {
23432344
flags = flags.withConformanceKind(
23442345
ProtocolConformanceReferenceKind::WitnessTable);
23452346

@@ -2348,8 +2349,13 @@ llvm::Constant *IRGenModule::emitProtocolConformances() {
23482349
// it.
23492350
witnessTableVar = getAddrOfWitnessTable(conformance);
23502351
} else {
2351-
flags = flags.withConformanceKind(
2352-
ProtocolConformanceReferenceKind::WitnessTableAccessor);
2352+
if (conformance->getConditionalRequirements().empty()) {
2353+
flags = flags.withConformanceKind(
2354+
ProtocolConformanceReferenceKind::WitnessTableAccessor);
2355+
} else {
2356+
flags = flags.withConformanceKind(
2357+
ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor);
2358+
}
23532359

23542360
witnessTableVar = getAddrOfWitnessTableAccessFunction(
23552361
conformance, ForDefinition);

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ template<> void ProtocolConformanceRecord::dump() const {
8484
printf("witness table accessor %s\n",
8585
symbolName((const void *)(uintptr_t)getWitnessTableAccessor()));
8686
break;
87+
case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
88+
printf("conditional witness table accessor %s\n",
89+
symbolName((const void *)(uintptr_t)getWitnessTableAccessor()));
90+
break;
8791
}
8892
}
8993
#endif
@@ -136,8 +140,12 @@ const {
136140
return getStaticWitnessTable();
137141

138142
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
139-
// FIXME: this needs information about conditional conformances.
140143
return getWitnessTableAccessor()(type, nullptr, 0);
144+
145+
case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
146+
// FIXME: this needs to query the conditional requirements to form the
147+
// array of witness tables to pass along to the accessor.
148+
return nullptr;
141149
}
142150

143151
swift_runtime_unreachable(
@@ -558,6 +566,7 @@ swift::swift_conformsToProtocol(const Metadata * const type,
558566
break;
559567

560568
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
569+
case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
561570
// If the record provides a dependent witness table accessor,
562571
// cache the result for the instantiated type metadata.
563572
C.cacheSuccess(type, P, record.getWitnessTable(type));

test/IRGen/protocol_conformance_records.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-resilience -enable-source-import -I %S/../Inputs | %FileCheck %s
2-
// RUN: %target-swift-frontend %s -emit-ir -num-threads 8 -enable-resilience -enable-source-import -I %S/../Inputs | %FileCheck %s
1+
// RUN: %target-swift-frontend -enable-experimental-conditional-conformances -primary-file %s -emit-ir -enable-resilience -enable-source-import -I %S/../Inputs | %FileCheck %s
2+
// RUN: %target-swift-frontend -enable-experimental-conditional-conformances %s -emit-ir -num-threads 8 -enable-resilience -enable-source-import -I %S/../Inputs | %FileCheck %s
33

44
import resilient_struct
55

@@ -78,10 +78,25 @@ extension Int: Runcible {
7878
// -- flags 0x04: unique direct metadata
7979
// CHECK-SAME: i32 1
8080
// CHECK-SAME: }
81-
// CHECK-SAME: ]
8281

8382
extension Size: Runcible {
8483
public func runce() {}
8584
}
8685

8786
// TODO: conformances that need lazy initialization
87+
public protocol Spoon { }
88+
89+
// Conditional conformances
90+
// CHECK: %swift.protocol_conformance {
91+
// -- protocol descriptor
92+
// CHECK-SAME: [[SPOON:@_T028protocol_conformance_records5SpoonMp]]
93+
// -- nominal type descriptor
94+
// CHECK-SAME: @_T028protocol_conformance_records17NativeGenericTypeVMn
95+
// -- witness table accessor
96+
// CHECK-SAME: @_T028protocol_conformance_records17NativeGenericTypeVyxGAA5SpoonA2aERzlWa
97+
// -- flags 0x04: unique nominal type descriptor + conditional accessor
98+
// CHECK-SAME: i32 36
99+
// CHECK-SAME: }
100+
extension NativeGenericType : Spoon where T: Spoon {
101+
public func runce() {}
102+
}

test/Inputs/conditional_conformance_basic_conformances.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,6 @@ public func double_concrete_concrete() {
320320
// CHECK-NEXT: unreachable
321321
// CHECK-NEXT: }
322322

323+
func dynamicCastToP1(_ value: Any) -> P1? {
324+
return value as? P1
325+
}

test/Interpreter/conditional_conformances.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ double_generic_generic(IsP2.self, IsP3.self)
1919
double_generic_concrete(IsP2.self)
2020
double_concrete_concrete()
2121

22+
assert(dynamicCastToP1(Single<IsP3>()) == nil)
23+
assert(dynamicCastToP1(Single<IsP2>()) == nil) // FIXME: incorrect result!
24+
2225
#elseif with_assoc
2326
generic_generic(IsAlsoP2.self, IsP3.self)
2427
generic_concrete(IsAlsoP2.self)

0 commit comments

Comments
 (0)