Merge pull request #13270 from DougGregor/se-0143-better-fail-than-crash-4.1
[4.1] [Runtime] Always fail to find a conditional conformance at runtime.
diff --git a/docs/ABI/TypeMetadata.rst b/docs/ABI/TypeMetadata.rst
index e765d8d..c44aa71 100644
--- a/docs/ABI/TypeMetadata.rst
+++ b/docs/ABI/TypeMetadata.rst
@@ -407,3 +407,29 @@
* **Bit 31** is set by the Objective-C runtime when it has done its
initialization of the protocol record. It is unused by the Swift runtime.
+
+Protocol Conformance Records
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A *protocol conformance record* states that a given type conforms to a
+particular protocol. Protocol conformance records are emitted into their own
+section, which is scanned by the Swift runtime when needed (e.g., in response to
+a `swift_conformsToProtocol()` query). Each protocol conformance record
+contains:
+
+- The `protocol descriptor`_ describing the protocol of the conformance.
+- A reference to the metadata for the **conforming type**, whose form is
+ determined by the **protocol conformance flags** described below.
+- The **witness table field** that provides access to the witness table
+ describing the conformance itself; the form of this field is determined by the
+ **protocol conformance flags** described below.
+- The **protocol conformance flags** is a 32-bit field comprised of:
+ * **Bits 0-3** contain the type metadata record kind, which indicates how
+ the **conforming type** field is encoded.
+ * **Bits 4-5** contain the kind of witness table. The value can be one of:
+ 0) The **witness table field** is a reference to a witness table.
+ 1) The **witness table field** is a reference to a **witness table
+ accessor** function for an unconditional conformance.
+ 2) The **witness table field** is a reference to a **witness table
+ accessor** function for a conditional conformance.
+
diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h
index 69027e2..fdd3a02 100644
--- a/include/swift/ABI/MetadataValues.h
+++ b/include/swift/ABI/MetadataValues.h
@@ -212,6 +212,10 @@
/// A function pointer that can be called to access the protocol witness
/// table.
WitnessTableAccessor,
+ /// A function pointer that can be called to access the protocol witness
+ /// table whose conformance is conditional on additional requirements that
+ /// must first be evaluated and then provided to the accessor function.
+ ConditionalWitnessTableAccessor,
};
// Type metadata record discriminant
@@ -245,7 +249,7 @@
struct ProtocolConformanceFlags : public TypeMetadataRecordFlags {
private:
enum : int_type {
- ConformanceKindMask = 0x00000010U,
+ ConformanceKindMask = 0x00000030U,
ConformanceKindShift = 4,
};
diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h
index eb786f2..22b647b 100644
--- a/include/swift/Runtime/Metadata.h
+++ b/include/swift/Runtime/Metadata.h
@@ -2454,8 +2454,7 @@
RelativeDirectPointer<const WitnessTable> WitnessTable;
/// A function that produces the witness table given an instance of the
- /// type. The function may return null if a specific instance does not
- /// conform to the protocol.
+ /// type.
RelativeDirectPointer<WitnessTableAccessorFn> WitnessTableAccessor;
};
@@ -2560,6 +2559,7 @@
break;
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
+ case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
assert(false && "not witness table");
}
return WitnessTable;
@@ -2568,6 +2568,7 @@
WitnessTableAccessorFn *getWitnessTableAccessor() const {
switch (Flags.getConformanceKind()) {
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
+ case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
break;
case ProtocolConformanceReferenceKind::WitnessTable:
diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp
index 057d50a..5ce97a6 100644
--- a/lib/IRGen/GenDecl.cpp
+++ b/lib/IRGen/GenDecl.cpp
@@ -2339,7 +2339,8 @@
auto flags = typeEntity.flags;
llvm::Constant *witnessTableVar;
if (!isResilient(conformance->getProtocol(),
- ResilienceExpansion::Maximal)) {
+ ResilienceExpansion::Maximal) &&
+ conformance->getConditionalRequirements().empty()) {
flags = flags.withConformanceKind(
ProtocolConformanceReferenceKind::WitnessTable);
@@ -2348,8 +2349,13 @@
// it.
witnessTableVar = getAddrOfWitnessTable(conformance);
} else {
- flags = flags.withConformanceKind(
- ProtocolConformanceReferenceKind::WitnessTableAccessor);
+ if (conformance->getConditionalRequirements().empty()) {
+ flags = flags.withConformanceKind(
+ ProtocolConformanceReferenceKind::WitnessTableAccessor);
+ } else {
+ flags = flags.withConformanceKind(
+ ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor);
+ }
witnessTableVar = getAddrOfWitnessTableAccessFunction(
conformance, ForDefinition);
diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp
index a028917..dfd4710 100644
--- a/stdlib/public/runtime/ProtocolConformance.cpp
+++ b/stdlib/public/runtime/ProtocolConformance.cpp
@@ -84,6 +84,10 @@
printf("witness table accessor %s\n",
symbolName((const void *)(uintptr_t)getWitnessTableAccessor()));
break;
+ case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
+ printf("conditional witness table accessor %s\n",
+ symbolName((const void *)(uintptr_t)getWitnessTableAccessor()));
+ break;
}
}
#endif
@@ -136,8 +140,12 @@
return getStaticWitnessTable();
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
- // FIXME: this needs information about conditional conformances.
return getWitnessTableAccessor()(type, nullptr, 0);
+
+ case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
+ // FIXME: this needs to query the conditional requirements to form the
+ // array of witness tables to pass along to the accessor.
+ return nullptr;
}
swift_runtime_unreachable(
@@ -558,6 +566,7 @@
break;
case ProtocolConformanceReferenceKind::WitnessTableAccessor:
+ case ProtocolConformanceReferenceKind::ConditionalWitnessTableAccessor:
// If the record provides a dependent witness table accessor,
// cache the result for the instantiated type metadata.
C.cacheSuccess(type, P, record.getWitnessTable(type));
diff --git a/test/IRGen/protocol_conformance_records.swift b/test/IRGen/protocol_conformance_records.swift
index ebf0168..2d4fec9 100644
--- a/test/IRGen/protocol_conformance_records.swift
+++ b/test/IRGen/protocol_conformance_records.swift
@@ -1,5 +1,5 @@
-// RUN: %target-swift-frontend -primary-file %s -emit-ir -enable-resilience -enable-source-import -I %S/../Inputs | %FileCheck %s
-// RUN: %target-swift-frontend %s -emit-ir -num-threads 8 -enable-resilience -enable-source-import -I %S/../Inputs | %FileCheck %s
+// RUN: %target-swift-frontend -enable-experimental-conditional-conformances -primary-file %s -emit-ir -enable-resilience -enable-source-import -I %S/../Inputs | %FileCheck %s
+// RUN: %target-swift-frontend -enable-experimental-conditional-conformances %s -emit-ir -num-threads 8 -enable-resilience -enable-source-import -I %S/../Inputs | %FileCheck %s
import resilient_struct
@@ -78,10 +78,25 @@
// -- flags 0x04: unique direct metadata
// CHECK-SAME: i32 1
// CHECK-SAME: }
-// CHECK-SAME: ]
extension Size: Runcible {
public func runce() {}
}
// TODO: conformances that need lazy initialization
+public protocol Spoon { }
+
+// Conditional conformances
+// CHECK: %swift.protocol_conformance {
+// -- protocol descriptor
+// CHECK-SAME: [[SPOON:@_T028protocol_conformance_records5SpoonMp]]
+// -- nominal type descriptor
+// CHECK-SAME: @_T028protocol_conformance_records17NativeGenericTypeVMn
+// -- witness table accessor
+// CHECK-SAME: @_T028protocol_conformance_records17NativeGenericTypeVyxGAA5SpoonA2aERzlWa
+// -- flags 0x04: unique nominal type descriptor + conditional accessor
+// CHECK-SAME: i32 36
+// CHECK-SAME: }
+extension NativeGenericType : Spoon where T: Spoon {
+ public func runce() {}
+}
diff --git a/test/Inputs/conditional_conformance_basic_conformances.swift b/test/Inputs/conditional_conformance_basic_conformances.swift
index 0a839a3..58f355e 100644
--- a/test/Inputs/conditional_conformance_basic_conformances.swift
+++ b/test/Inputs/conditional_conformance_basic_conformances.swift
@@ -320,3 +320,6 @@
// CHECK-NEXT: unreachable
// CHECK-NEXT: }
+func dynamicCastToP1(_ value: Any) -> P1? {
+ return value as? P1
+}
diff --git a/test/Interpreter/conditional_conformances.swift b/test/Interpreter/conditional_conformances.swift
index a90f295..0981767 100644
--- a/test/Interpreter/conditional_conformances.swift
+++ b/test/Interpreter/conditional_conformances.swift
@@ -19,6 +19,9 @@
double_generic_concrete(IsP2.self)
double_concrete_concrete()
+assert(dynamicCastToP1(Single<IsP3>()) == nil)
+assert(dynamicCastToP1(Single<IsP2>()) == nil) // FIXME: incorrect result!
+
#elseif with_assoc
generic_generic(IsAlsoP2.self, IsP3.self)
generic_concrete(IsAlsoP2.self)