Skip to content

[6.0] IRGen: Don't encode conditional requirements to Copyable as normal conformance requirement. #74605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/Requirement.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ class Requirement {
/// 'T : C' can be satisfied; however, if 'T' already has an unrelated
/// superclass requirement, 'T : C' cannot be satisfied.
bool canBeSatisfied() const;

/// True if the requirement states a conformance to an invertible protocol
/// that is implied by default (such as `Copyable` or `Escapable`.
bool isInvertibleProtocolRequirement() const;

/// Linear order on requirements in a generic signature.
int compare(const Requirement &other) const;
Expand Down
4 changes: 1 addition & 3 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1282,9 +1282,7 @@ void GenericSignatureImpl::getRequirementsWithInverses(

// Filter out explicit conformances to invertible protocols.
for (auto req : getRequirements()) {
if (req.getKind() == RequirementKind::Conformance &&
req.getFirstType()->is<GenericTypeParamType>() &&
req.getProtocolDecl()->getInvertibleProtocolKind()) {
if (req.isInvertibleProtocolRequirement()) {
continue;
}

Expand Down
6 changes: 6 additions & 0 deletions lib/AST/Requirement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ bool Requirement::canBeSatisfied() const {
llvm_unreachable("Bad requirement kind");
}

bool Requirement::isInvertibleProtocolRequirement() const {
return getKind() == RequirementKind::Conformance
&& getFirstType()->is<GenericTypeParamType>()
&& getProtocolDecl()->getInvertibleProtocolKind();
}

/// Determine the canonical ordering of requirements.
static unsigned getRequirementKindOrder(RequirementKind kind) {
switch (kind) {
Expand Down
20 changes: 13 additions & 7 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2118,24 +2118,30 @@ namespace {

// Compute the inverse requirements from the generic signature where the
// conformance occurs.
SmallVector<Requirement, 2> scratchReqs;
SmallVector<Requirement, 2> condReqs;
SmallVector<InverseRequirement, 2> inverses;
if (auto genericSig =
normal->getDeclContext()->getGenericSignatureOfContext()) {
genericSig->getRequirementsWithInverses(scratchReqs, inverses);
scratchReqs.clear();
genericSig->getRequirementsWithInverses(condReqs, inverses);
}
condReqs.clear();

for (auto condReq : normal->getConditionalRequirements()) {
// We don't need to collect conditional requirements for invertible
// protocol requirements here, since they are encoded in the inverse
// list above.
if (!condReq.isInvertibleProtocolRequirement()) {
condReqs.push_back(condReq);
}
}

auto condReqs = normal->getConditionalRequirements();
if (condReqs.empty()) {
// For a protocol P that conforms to another protocol, introduce a
// conditional requirement for that P's Self: P. This aligns with
// SILWitnessTable::enumerateWitnessTableConditionalConformances().
if (auto selfProto = normal->getDeclContext()->getSelfProtocolDecl()) {
auto selfType = selfProto->getSelfInterfaceType()->getCanonicalType();
scratchReqs.emplace_back(RequirementKind::Conformance, selfType,
condReqs.emplace_back(RequirementKind::Conformance, selfType,
selfProto->getDeclaredInterfaceType());
condReqs = scratchReqs;
}

if (condReqs.empty() && inverses.empty())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %target-swift-frontend -emit-ir %s | %FileCheck --check-prefix=IR %s
// RUN: %target-run-simple-swift | %FileCheck --check-prefix=EXEC %s

// REQUIRES: executable_test

// The conformance descriptor for Optional: P ought to be encoded as if it
// were an unconditional conformance. We check this by checking that the
// global variable type is `%swift.protocol_conformance_descriptor`, indicating
// that there is no tail matter encoding conditional or inverted requirements.

// IR-LABEL: @"$sxSg4main1PABMc" ={{.*}} constant %swift.protocol_conformance_descriptor {

protocol P /*<implied> : Copyable*/ {
func p()
}

extension Optional: P /*<implied> where T: Copyable */ {
func p() {
print("conforming optional \(self)")
}
}

@inline(never)
func cast<T>(value: T) -> (any P)? {
return value as? any P
}

func main() {
// EXEC: going to test
print("going to test")

// EXEC-NEXT: conforming optional Optional("test")
let x: String? = "test"
if let p = cast(value: x) {
p.p()
} else {
print("not a P")
}

// EXEC-NEXT: done testing
print("done testing")
}
main()