Skip to content

Commit 6b2b28a

Browse files
authored
Merge pull request swiftlang#83565 from hamishknight/synth
[IDE] Use LookUpConformanceInModule for synthesized extension requirement substitution
2 parents 1b5e69b + d4f2e2b commit 6b2b28a

File tree

4 files changed

+648
-650
lines changed

4 files changed

+648
-650
lines changed

lib/IDE/IDETypeChecking.cpp

Lines changed: 44 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -277,22 +277,17 @@ struct SynthesizedExtensionAnalyzer::Implementation {
277277
using MergeGroupVector = std::vector<ExtensionMergeGroup>;
278278

279279
NominalTypeDecl *Target;
280-
Type BaseType;
281280
DeclContext *DC;
282281
bool IncludeUnconditional;
283282
PrintOptions Options;
284283
MergeGroupVector AllGroups;
285284
ExtensionInfoMap InfoMap;
286285

287-
Implementation(NominalTypeDecl *Target,
288-
bool IncludeUnconditional,
289-
PrintOptions &&Options):
290-
Target(Target),
291-
BaseType(Target->getDeclaredInterfaceType()),
292-
DC(Target),
293-
IncludeUnconditional(IncludeUnconditional),
294-
Options(std::move(Options)), AllGroups(MergeGroupVector()),
295-
InfoMap(collectSynthesizedExtensionInfo(AllGroups)) {}
286+
Implementation(NominalTypeDecl *Target, bool IncludeUnconditional,
287+
PrintOptions &&Options)
288+
: Target(Target), DC(Target), IncludeUnconditional(IncludeUnconditional),
289+
Options(std::move(Options)), AllGroups(MergeGroupVector()),
290+
InfoMap(collectSynthesizedExtensionInfo(AllGroups)) {}
296291

297292
unsigned countInherits(ExtensionDecl *ED) {
298293
SmallVector<InheritedEntry, 4> Results;
@@ -316,19 +311,24 @@ struct SynthesizedExtensionAnalyzer::Implementation {
316311
// extension SomeType: SomeProtocol where T: SomeProtocol {}. The former is
317312
// Ext and the latter is EnablingExt/Conf. Either of these can be
318313
// conditional in ways that need to be considered when merging.
319-
auto conformanceIsConditional =
320-
Conf && !Conf->getConditionalRequirements().empty();
321-
if (!Ext->isConstrainedExtension() && !conformanceIsConditional) {
314+
auto isConditionalEnablingExt =
315+
Conf && EnablingExt && !Conf->getConditionalRequirements().empty();
316+
if (!Ext->isConstrainedExtension() && !isConditionalEnablingExt) {
322317
if (IncludeUnconditional)
323318
Result.Ext = Ext;
324319
return {Result, MergeInfo};
325320
}
326321

327-
auto handleRequirements = [&](SubstitutionMap subMap,
328-
ExtensionDecl *OwningExt,
322+
auto handleRequirements = [&](ExtensionDecl *OwningExt,
329323
ArrayRef<Requirement> Reqs) {
330-
ProtocolDecl *BaseProto = OwningExt->getInnermostDeclContext()
331-
->getSelfProtocolDecl();
324+
ProtocolDecl *BaseProto = OwningExt->getSelfProtocolDecl();
325+
// Substitute the base conforming type into a protocol's generic signature
326+
// if needed.
327+
SubstitutionMap subMap;
328+
if (Conf && BaseProto) {
329+
subMap = SubstitutionMap::getProtocolSubstitutions(
330+
ProtocolConformanceRef(Conf));
331+
}
332332
for (auto Req : Reqs) {
333333
// Skip protocol's Self : <Protocol> requirement.
334334
if (BaseProto &&
@@ -337,10 +337,13 @@ struct SynthesizedExtensionAnalyzer::Implementation {
337337
Req.getProtocolDecl() == BaseProto)
338338
continue;
339339

340-
if (!BaseType->isExistentialType()) {
340+
if (subMap) {
341341
// Apply any substitutions we need to map the requirements from a
342-
// a protocol extension to an extension on the conforming type.
343-
Req = Req.subst(subMap);
342+
// a protocol extension to an extension on the conforming type. We
343+
// need to lookup conformances outside of the substitution map since
344+
// the extension may introduce new conformance constraints.
345+
Req = Req.subst(QuerySubstitutionMap{subMap},
346+
LookUpConformanceInModule());
344347
if (Req.hasError()) {
345348
// Substitution with interface type bases can only fail
346349
// if a concrete type fails to conform to a protocol.
@@ -353,6 +356,14 @@ struct SynthesizedExtensionAnalyzer::Implementation {
353356
if (Req.getKind() != RequirementKind::Layout)
354357
assert(!Req.getSecondType()->hasArchetype());
355358

359+
// FIXME: This doesn't correctly handle conformance requirements, e.g:
360+
//
361+
// extension P where X: Q, X.Y == Int {}
362+
//
363+
// Since the archetype we have for `X` doesn't necessarily have a
364+
// conformance to `Q` in the conforming type's generic environment. This
365+
// results in a substitution failure for `X.Y`.
366+
// https://github.com/swiftlang/swift/issues/83564
356367
auto *env = Target->getGenericEnvironment();
357368
SmallVector<Requirement, 2> subReqs;
358369
subReqs.push_back(
@@ -385,30 +396,14 @@ struct SynthesizedExtensionAnalyzer::Implementation {
385396
};
386397

387398
if (Ext->isConstrainedExtension()) {
388-
// Get the substitutions from the generic signature of
389-
// the extension to the interface types of the base type's
390-
// declaration.
391-
SubstitutionMap subMap;
392-
if (!BaseType->isExistentialType()) {
393-
if (auto *NTD = Ext->getExtendedNominal())
394-
subMap = BaseType->getContextSubstitutionMap(NTD);
395-
}
396-
397399
assert(Ext->getGenericSignature() && "No generic signature.");
398400
auto GenericSig = Ext->getGenericSignature();
399-
if (handleRequirements(subMap, Ext, GenericSig.getRequirements()))
401+
if (handleRequirements(Ext, GenericSig.getRequirements()))
400402
return {Result, MergeInfo};
401403
}
402404

403-
if (Conf) {
404-
SubstitutionMap subMap;
405-
if (!BaseType->isExistentialType()) {
406-
if (auto *NTD = EnablingExt->getExtendedNominal())
407-
subMap = BaseType->getContextSubstitutionMap(NTD);
408-
}
409-
if (handleRequirements(subMap,
410-
EnablingExt,
411-
Conf->getConditionalRequirements()))
405+
if (isConditionalEnablingExt) {
406+
if (handleRequirements(EnablingExt, Conf->getConditionalRequirements()))
412407
return {Result, MergeInfo};
413408
}
414409

@@ -479,7 +474,6 @@ struct SynthesizedExtensionAnalyzer::Implementation {
479474

480475
ExtensionInfoMap InfoMap;
481476
ExtensionMergeInfoMap MergeInfoMap;
482-
std::vector<NominalTypeDecl*> Unhandled;
483477

484478
auto handleExtension = [&](ExtensionDecl *E, bool Synthesized,
485479
ExtensionDecl *EnablingE,
@@ -500,31 +494,17 @@ struct SynthesizedExtensionAnalyzer::Implementation {
500494
}
501495
};
502496

503-
// We want to visit the protocols of any normal conformances we see, but
504-
// we have to avoid doing this to self-conformances or we can end up with
505-
// a cycle. Otherwise this is cycle-proof on valid code.
506-
// We also want to ignore inherited conformances. Members from these will
507-
// be included in the class they were inherited from.
508-
auto addConformance = [&](ProtocolConformance *Conf) {
509-
if (isa<InheritedProtocolConformance>(Conf))
510-
return;
511-
auto RootConf = Conf->getRootConformance();
512-
if (isa<NormalProtocolConformance>(RootConf))
513-
Unhandled.push_back(RootConf->getProtocol());
514-
};
497+
for (auto *LocalConf : Target->getLocalConformances()) {
498+
if (isa<InheritedProtocolConformance>(LocalConf))
499+
continue;
515500

516-
for (auto *Conf : Target->getLocalConformances()) {
517-
addConformance(Conf);
518-
}
519-
while (!Unhandled.empty()) {
520-
NominalTypeDecl* Back = Unhandled.back();
521-
Unhandled.pop_back();
522-
for (ExtensionDecl *E : Back->getExtensions()) {
523-
handleExtension(E, true, nullptr, nullptr);
524-
}
525-
for (auto *Conf : Back->getLocalConformances()) {
526-
addConformance(Conf);
527-
}
501+
auto RootConf = LocalConf->getRootConformance();
502+
auto *Conf = dyn_cast<NormalProtocolConformance>(RootConf);
503+
if (!Conf)
504+
continue;
505+
506+
for (auto *E : Conf->getProtocol()->getExtensions())
507+
handleExtension(E, true, nullptr, Conf);
528508
}
529509

530510
// Merge with actual extensions.

test/IDE/print_synthesized_extensions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,9 @@ extension S13 : P5 {
241241
}
242242

243243
// CHECK1: <synthesized>extension <ref:Struct>S1</ref> where <ref:GenericTypeParam>T</ref> : <ref:module>print_synthesized_extensions</ref>.<ref:Protocol>P2</ref> {
244-
// CHECK1-NEXT: <decl:Func>public func <loc>p2member()</loc></decl>
245244
// CHECK1-NEXT: <decl:Func>public func <loc>ef1(<decl:Param>t: <ref:GenericTypeParam>T</ref></decl>)</loc></decl>
246245
// CHECK1-NEXT: <decl:Func>public func <loc>ef2(<decl:Param>t: <ref:module>print_synthesized_extensions</ref>.<ref:Struct>S2</ref></decl>)</loc></decl>
246+
// CHECK1-NEXT: <decl:Func>public func <loc>p2member()</loc></decl>
247247
// CHECK1-NEXT: }</synthesized>
248248

249249
// CHECK2: <synthesized>extension <ref:Struct>S1</ref> where <ref:GenericTypeParam>T</ref> : <ref:module>print_synthesized_extensions</ref>.<ref:Protocol>P3</ref> {
@@ -410,4 +410,4 @@ extension S14 : P14 where repeat each T: Hashable {}
410410

411411
// CHECK17: <synthesized>extension <ref:Struct>S14</ref> {
412412
// 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>
413-
// CHECK17-NEXT: }</synthesized>
413+
// CHECK17-NEXT: }</synthesized>

test/IDE/print_synthesized_extensions_generics.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,21 @@ extension G : P2 {}
8585
// CHECK: extension P2 where Self.T.T : print_synthesized_extensions_generics.A {
8686
// CHECK-NEXT: func blah()
8787
// CHECK-NEXT: }
88+
89+
public protocol P3 {
90+
associatedtype A
91+
}
92+
public protocol P4 {
93+
associatedtype B: P3
94+
}
95+
96+
public struct S1<A> {}
97+
98+
// Make sure we don't crash. Unfortunately we don't yet correctly output these
99+
// though.
100+
extension S1: P4 where A: P4, A.B.A == A {
101+
public typealias B = A.B
102+
}
103+
extension P3 where A: P4, A.B.A == A {
104+
public func foo() {}
105+
}

0 commit comments

Comments
 (0)