Skip to content

Commit 65b4172

Browse files
authored
Merge pull request swiftlang#13270 from DougGregor/se-0143-better-fail-than-crash-4.1
[4.1] [Runtime] Always fail to find a conditional conformance at runtime.
2 parents 8b05b0b + 44bf9d1 commit 65b4172

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)