Skip to content

IDE: Fix my own lazyness in SynthesizedExtensionAnalyzer::Implementation::isApplicable() #76955

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
merged 1 commit into from
Oct 26, 2024
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
96 changes: 42 additions & 54 deletions lib/IDE/IDETypeChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,35 +191,32 @@ struct SynthesizedExtensionAnalyzer::Implementation {

struct ExtensionMergeInfo {
struct Requirement {
Type First;
Type Second;
RequirementKind Kind;
CanType CanFirst;
CanType CanSecond;

bool operator< (const Requirement& Rhs) const {
if (Kind != Rhs.Kind)
return Kind < Rhs.Kind;
else if (CanFirst != Rhs.CanFirst)
return CanFirst < Rhs.CanFirst;
else
return CanSecond < Rhs.CanSecond;
swift::Requirement Req;

bool operator<(const Requirement& Rhs) const {
if (auto result = unsigned(Req.getKind()) - unsigned(Rhs.Req.getKind())) {
return result < 0;
} else if (!Req.getFirstType()->isEqual(Rhs.Req.getFirstType())) {
return (Req.getFirstType()->getCanonicalType() <
Rhs.Req.getFirstType()->getCanonicalType());
} else if (Req.getKind() != RequirementKind::Layout) {
return (Req.getSecondType()->getCanonicalType() <
Rhs.Req.getSecondType()->getCanonicalType());
}

return false;
}

bool operator== (const Requirement& Rhs) const {
return (!(*this < Rhs)) && (!(Rhs < *this));
return Req.getCanonical() == Rhs.Req.getCanonical();
}
};

bool Unmergable;
unsigned InheritsCount;
std::set<Requirement> Requirements;
void addRequirement(swift::Requirement Req) {
auto First = Req.getFirstType();
auto CanFirst = First->getCanonicalType();
auto Second = Req.getSecondType();
auto CanSecond = Second->getCanonicalType();

Requirements.insert({First, Second, Req.getKind(), CanFirst, CanSecond});
Requirements.insert({Req});
}
bool operator== (const ExtensionMergeInfo& Another) const {
// Trivially unmergeable.
Expand Down Expand Up @@ -333,10 +330,6 @@ struct SynthesizedExtensionAnalyzer::Implementation {
ProtocolDecl *BaseProto = OwningExt->getInnermostDeclContext()
->getSelfProtocolDecl();
for (auto Req : Reqs) {
// FIXME: Don't skip layout requirements.
if (Req.getKind() == RequirementKind::Layout)
continue;

// Skip protocol's Self : <Protocol> requirement.
if (BaseProto &&
Req.getKind() == RequirementKind::Conformance &&
Expand All @@ -357,40 +350,35 @@ struct SynthesizedExtensionAnalyzer::Implementation {
}

assert(!Req.getFirstType()->hasArchetype());
assert(!Req.getSecondType()->hasArchetype());

auto SubstReq = Req.subst(
[&](Type type) -> Type {
if (type->isTypeParameter())
return Target->mapTypeIntoContext(type);

return type;
},
LookUpConformanceInModule());
if (Req.getKind() != RequirementKind::Layout)
assert(!Req.getSecondType()->hasArchetype());

auto *env = Target->getGenericEnvironment();
SmallVector<Requirement, 2> subReqs;
switch (SubstReq.checkRequirement(subReqs)) {
case CheckRequirementResult::Success:
break;

case CheckRequirementResult::ConditionalConformance:
// FIXME: Need to handle conditional requirements here!
break;

case CheckRequirementResult::PackRequirement:
// FIXME
assert(false && "Refactor this");
return true;

case CheckRequirementResult::SubstitutionFailure:
return true;

case CheckRequirementResult::RequirementFailure:
if (!SubstReq.canBeSatisfied())
subReqs.push_back(
Req.subst(
QueryInterfaceTypeSubstitutions(env),
LookUpConformanceInModule(),
SubstFlags::PreservePackExpansionLevel));

while (!subReqs.empty()) {
auto req = subReqs.pop_back_val();
switch (req.checkRequirement(subReqs)) {
case CheckRequirementResult::Success:
case CheckRequirementResult::PackRequirement:
case CheckRequirementResult::ConditionalConformance:
break;

case CheckRequirementResult::SubstitutionFailure:
return true;

MergeInfo.addRequirement(Req);
break;
case CheckRequirementResult::RequirementFailure:
if (!req.canBeSatisfied())
return true;

MergeInfo.addRequirement(Req);
break;
}
}
}
return false;
Expand Down
25 changes: 19 additions & 6 deletions test/IDE/print_synthesized_extensions.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module-path %t/print_synthesized_extensions.swiftmodule -emit-module-doc -emit-module-doc-path %t/print_synthesized_extensions.swiftdoc %s
// RUN: %target-swift-ide-test -print-module -annotate-print -synthesize-extension -print-interface -no-empty-line-between-members -module-to-print=print_synthesized_extensions -I %t -source-filename=%s > %t.syn.txt
// RUN: %target-swift-frontend -target %target-swift-5.9-abi-triple -emit-module-path %t/print_synthesized_extensions.swiftmodule -emit-module-doc -emit-module-doc-path %t/print_synthesized_extensions.swiftdoc %s
// RUN: %target-swift-ide-test -annotate-print -print-module -synthesize-extension -print-interface -no-empty-line-between-members -module-to-print=print_synthesized_extensions -I %t -source-filename=%s -target=%target-swift-5.9-abi-triple > %t.syn.txt
// RUN: %FileCheck %s -check-prefix=CHECK1 < %t.syn.txt
// RUN: %FileCheck %s -check-prefix=CHECK2 < %t.syn.txt
// RUN: %FileCheck %s -check-prefix=CHECK3 < %t.syn.txt
Expand All @@ -17,6 +17,7 @@
// RUN: %FileCheck %s -check-prefix=CHECK14 < %t.syn.txt
// RUN: %FileCheck %s -check-prefix=CHECK15 < %t.syn.txt
// RUN: %FileCheck %s -check-prefix=CHECK16 < %t.syn.txt
// RUN: %FileCheck %s -check-prefix=CHECK17 < %t.syn.txt

public protocol P1 {
associatedtype T1
Expand Down Expand Up @@ -312,8 +313,6 @@ extension S13 : P5 {
// CHECK11-NEXT: public func <loc>foo3()</loc></decl>
// CHECK11-NEXT: <decl:Func>/// This is picked
// CHECK11-NEXT: public func <loc>foo4()</loc></decl>
// CHECK11-NEXT: <decl:Func>/// This should not crash
// CHECK11-NEXT: public func <loc>foo5()</loc></decl>
// CHECK11-NEXT: }</synthesized>

// CHECK12: <decl:Protocol>public protocol <loc>P6</loc> {
Expand All @@ -338,8 +337,6 @@ extension S13 : P5 {
// CHECK14-NEXT: public func <loc>foo3()</loc></decl>
// CHECK14-NEXT: <decl:Func>/// This is picked
// CHECK14-NEXT: public func <loc>foo4()</loc></decl>
// CHECK14-NEXT: <decl:Func>/// This should not crash
// CHECK14-NEXT: public func <loc>foo5()</loc></decl>
// CHECK14-NEXT: }</synthesized>

// rdar://76868074: Make sure we print the extensions for C.
Expand Down Expand Up @@ -398,3 +395,19 @@ extension F : P8 {}
// CHECK16-NEXT: }</synthesized>

// CHECK16-NOT: <synthesized>extension <ref:Class>F</ref> where <ref:GenericTypeParam>T</ref> : <ref:module>print_synthesized_extensions</ref>.<ref:Class>E</ref> {


// Parameter packs
public protocol P14 {}

extension P14 {
public func foo<each T: Equatable>(_: repeat each T) {}
}

public struct S14<each T: Equatable> {}

extension S14 : P14 where repeat each T: Hashable {}

// CHECK17: <synthesized>extension <ref:Struct>S14</ref> {
// CHECK17-NEXT: <decl:Func>public func <loc>foo<each <ref:GenericTypeParam>T</ref>>(<decl:Param>_: repeat each <ref:GenericTypeParam>T</ref></decl>)</loc> where Pack{repeat each <ref:GenericTypeParam>T</ref>} : <ref:Protocol>Equatable</ref></decl>
// CHECK17-NEXT: }</synthesized>