Skip to content

Commit 5afdb98

Browse files
authored
Merge pull request #72146 from slavapestov/ncgenerics-fixes-9
Non-copyable generics fixes, part 9
2 parents 123045f + 708586b commit 5afdb98

31 files changed

+215
-410
lines changed

include/swift/AST/ASTContext.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,15 +1302,17 @@ class ASTContext final {
13021302
/// specialized conformance from the generic conformance.
13031303
ProtocolConformance *
13041304
getSpecializedConformance(Type type,
1305-
RootProtocolConformance *generic,
1305+
NormalProtocolConformance *generic,
13061306
SubstitutionMap substitutions);
13071307

1308-
/// Produce an inherited conformance, for subclasses of a type
1309-
/// that already conforms to a protocol.
1308+
/// Produce an inherited conformance, which forwards all operations to a
1309+
/// base conformance, except for getType(), which must return some subclass
1310+
/// of the base conformance's conforming type. This models the case where a
1311+
/// subclass conforms to a protocol because its superclass already conforms.
13101312
///
13111313
/// \param type The type for which we are retrieving the conformance.
13121314
///
1313-
/// \param inherited The inherited conformance.
1315+
/// \param inherited A normal or specialized conformance.
13141316
ProtocolConformance *
13151317
getInheritedConformance(Type type, ProtocolConformance *inherited);
13161318

include/swift/AST/Decl.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3919,10 +3919,6 @@ class AssociatedTypeDecl : public TypeDecl {
39193919
TypeDecl::getOverriddenDecl());
39203920
}
39213921

3922-
/// Determine whether this type has ~<target>` stated as
3923-
/// one of its inherited types.
3924-
InverseMarking::Mark hasInverseMarking(InvertibleProtocolKind target) const;
3925-
39263922
/// Retrieve the set of associated types overridden by this associated
39273923
/// type.
39283924
llvm::TinyPtrVector<AssociatedTypeDecl *> getOverriddenDecls() const;
@@ -4223,7 +4219,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
42234219
///
42244220
/// This is used by deserialization of module files to report
42254221
/// conformances.
4226-
void registerProtocolConformance(ProtocolConformance *conformance,
4222+
void registerProtocolConformance(NormalProtocolConformance *conformance,
42274223
bool synthesized = false);
42284224

42294225
void setConformanceLoader(LazyMemberLoader *resolver, uint64_t contextData);
@@ -5258,10 +5254,6 @@ class ProtocolDecl final : public NominalTypeDecl {
52585254
/// Determine whether this protocol inherits from the given ("super")
52595255
/// protocol.
52605256
bool inheritsFrom(const ProtocolDecl *Super) const;
5261-
5262-
/// Determine whether this protocol has ~<target>` stated on
5263-
/// itself, one of its inherited types or `Self` requirements.
5264-
InverseMarking::Mark hasInverseMarking(InvertibleProtocolKind target) const;
52655257

52665258
SourceLoc getStartLoc() const { return ProtocolLoc; }
52675259
SourceRange getSourceRange() const {

include/swift/AST/ProtocolConformance.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,7 @@ class SelfProtocolConformance : public RootProtocolConformance {
880880
class SpecializedProtocolConformance : public ProtocolConformance,
881881
public llvm::FoldingSetNode {
882882
/// The generic conformance from which this conformance was derived.
883-
RootProtocolConformance *GenericConformance;
883+
NormalProtocolConformance *GenericConformance;
884884

885885
/// The substitutions applied to the generic conformance to produce this
886886
/// conformance.
@@ -899,15 +899,15 @@ class SpecializedProtocolConformance : public ProtocolConformance,
899899
friend class ASTContext;
900900

901901
SpecializedProtocolConformance(Type conformingType,
902-
RootProtocolConformance *genericConformance,
902+
NormalProtocolConformance *genericConformance,
903903
SubstitutionMap substitutions);
904904

905905
void computeConditionalRequirements() const;
906906

907907
public:
908908
/// Get the generic conformance from which this conformance was derived,
909909
/// if there is one.
910-
RootProtocolConformance *getGenericConformance() const {
910+
NormalProtocolConformance *getGenericConformance() const {
911911
return GenericConformance;
912912
}
913913

@@ -993,7 +993,7 @@ class SpecializedProtocolConformance : public ProtocolConformance,
993993
}
994994

995995
static void Profile(llvm::FoldingSetNodeID &ID, Type type,
996-
RootProtocolConformance *genericConformance,
996+
NormalProtocolConformance *genericConformance,
997997
SubstitutionMap subs) {
998998
ID.AddPointer(type.getPointer());
999999
ID.AddPointer(genericConformance);

lib/AST/ASTContext.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2600,7 +2600,7 @@ ASTContext::getBuiltinConformance(Type type, ProtocolDecl *protocol,
26002600
}
26012601

26022602
static bool collapseSpecializedConformance(Type type,
2603-
RootProtocolConformance *conformance,
2603+
NormalProtocolConformance *conformance,
26042604
SubstitutionMap substitutions) {
26052605
if (!conformance->getType()->isEqual(type))
26062606
return false;
@@ -2615,7 +2615,7 @@ static bool collapseSpecializedConformance(Type type,
26152615

26162616
ProtocolConformance *
26172617
ASTContext::getSpecializedConformance(Type type,
2618-
RootProtocolConformance *generic,
2618+
NormalProtocolConformance *generic,
26192619
SubstitutionMap substitutions) {
26202620
// If the specialization is a no-op, use the root conformance instead.
26212621
if (collapseSpecializedConformance(type, generic, substitutions)) {

lib/AST/ConformanceLookup.cpp

Lines changed: 32 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
#include "swift/AST/DiagnosticsSema.h"
3030
#include "swift/AST/ExistentialLayout.h"
3131
#include "swift/AST/GenericEnvironment.h"
32-
#include "swift/AST/InverseMarking.h"
3332
#include "swift/AST/NameLookup.h"
3433
#include "swift/AST/NameLookupRequests.h"
3534
#include "swift/AST/PackConformance.h"
@@ -403,39 +402,6 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
403402
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
404403
}
405404

406-
static ProtocolConformanceRef
407-
getBuiltinInvertibleProtocolConformance(NominalTypeDecl *nominal,
408-
Type type,
409-
ProtocolDecl *protocol) {
410-
assert(isa<ClassDecl>(nominal));
411-
ASTContext &ctx = protocol->getASTContext();
412-
413-
auto ip = protocol->getInvertibleProtocolKind();
414-
switch (*ip) {
415-
case InvertibleProtocolKind::Copyable:
416-
// If move-only classes is enabled, we'll check the markings.
417-
if (ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses)) {
418-
switch (nominal->hasInverseMarking(*ip).getKind()) {
419-
case InverseMarking::Kind::LegacyExplicit:
420-
case InverseMarking::Kind::Explicit:
421-
// An inverse ~Copyable prevents conformance.
422-
return ProtocolConformanceRef::forInvalid();
423-
424-
case InverseMarking::Kind::None:
425-
break;
426-
}
427-
}
428-
break;
429-
case InvertibleProtocolKind::Escapable:
430-
// Always conforms.
431-
break;
432-
}
433-
434-
return ProtocolConformanceRef(
435-
ctx.getBuiltinConformance(type, protocol,
436-
BuiltinConformanceKind::Synthesized));
437-
}
438-
439405
/// Synthesize a builtin type conformance to the given protocol, if
440406
/// appropriate.
441407
static ProtocolConformanceRef
@@ -625,13 +591,6 @@ LookupConformanceInModuleRequest::evaluate(
625591
if (!nominal || isa<ProtocolDecl>(nominal))
626592
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
627593

628-
// We specially avoid recording conformances to invertible protocols in a
629-
// class's conformance table. This prevents an evaluator cycle.
630-
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)
631-
&& isa<ClassDecl>(nominal)
632-
&& protocol->getInvertibleProtocolKind())
633-
return getBuiltinInvertibleProtocolConformance(nominal, type, protocol);
634-
635594
// Expand conformances added by extension macros.
636595
//
637596
// FIXME: This expansion should only be done if the
@@ -643,8 +602,12 @@ LookupConformanceInModuleRequest::evaluate(
643602
ExpandExtensionMacros{nominal},
644603
{ });
645604

646-
// Find the (unspecialized) conformance.
605+
// Find the root conformance in the nominal type declaration's
606+
// conformance lookup table.
647607
SmallVector<ProtocolConformance *, 2> conformances;
608+
609+
// If the conformance lookup table produced nothing, we try to derive the
610+
// conformance for a few special protocol kinds.
648611
if (!nominal->lookupConformance(protocol, conformances)) {
649612
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
650613
// Try to infer Sendable conformance.
@@ -677,31 +640,17 @@ LookupConformanceInModuleRequest::evaluate(
677640
} else {
678641
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
679642
}
680-
} else if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)
681-
|| protocol->isSpecificProtocol(KnownProtocolKind::Escapable)) {
682-
const auto kp = protocol->getKnownProtocolKind().value();
683-
684-
if (!ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)
685-
&& kp == KnownProtocolKind::Copyable) {
643+
} else if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
644+
if (!ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
686645
// Return an abstract conformance to maintain legacy compatability.
687646
// We only need to do this until we are properly dealing with or
688647
// omitting Copyable conformances in modules/interfaces.
689648

690649
if (nominal->canBeCopyable())
691650
return ProtocolConformanceRef(protocol);
692-
else
693-
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
694651
}
695652

696-
// Try to infer the conformance.
697-
ImplicitKnownProtocolConformanceRequest cvRequest{nominal, kp};
698-
if (auto conformance = evaluateOrDefault(
699-
ctx.evaluator, cvRequest, nullptr)) {
700-
conformances.clear();
701-
conformances.push_back(conformance);
702-
} else {
703-
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
704-
}
653+
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
705654
} else if (protocol->isSpecificProtocol(
706655
KnownProtocolKind::BitwiseCopyable)) {
707656
// Try to infer BitwiseCopyable conformance.
@@ -720,6 +669,8 @@ LookupConformanceInModuleRequest::evaluate(
720669
}
721670
}
722671

672+
// We should have at least one conformance by now, or we would have returned
673+
// above.
723674
assert(!conformances.empty());
724675

725676
// If we have multiple conformances, first try to filter out any that are
@@ -768,27 +719,32 @@ LookupConformanceInModuleRequest::evaluate(
768719
return ProtocolConformanceRef(conformance);
769720
}
770721

771-
// If the type is specialized, find the conformance for the generic type.
722+
// We now have a root conformance for the nominal's declared interface type.
723+
// If our type is specialized, apply a substitution map to the root
724+
// conformance.
772725
if (type->isSpecialized()) {
773-
// Figure out the type that's explicitly conforming to this protocol.
774-
Type explicitConformanceType = conformance->getType();
775-
DeclContext *explicitConformanceDC = conformance->getDeclContext();
776-
777-
// If the explicit conformance is associated with a type that is different
778-
// from the type we're checking, retrieve generic conformance.
779-
if (!explicitConformanceType->isEqual(type)) {
780-
// Gather the substitutions we need to map the generic conformance to
781-
// the specialized conformance.
782-
auto subMap = type->getContextSubstitutionMap(mod, explicitConformanceDC);
783-
784-
// Create the specialized conformance entry.
785-
auto result = ctx.getSpecializedConformance(type,
786-
cast<RootProtocolConformance>(conformance), subMap);
787-
return ProtocolConformanceRef(result);
726+
if (!conformance->getType()->isEqual(type)) {
727+
// We use a builtin conformance for unconditional Copyable and Escapable
728+
// conformances. Avoid building a substitution map and just return the
729+
// correct builtin conformance for the specialized type.
730+
if (auto *builtinConf = dyn_cast<BuiltinProtocolConformance>(conformance)) {
731+
return ProtocolConformanceRef(
732+
ctx.getBuiltinConformance(type, protocol,
733+
builtinConf->getBuiltinConformanceKind()));
734+
}
735+
736+
// Otherwise, we have a normal conformance, so we're going to build a
737+
// specialized conformance from the context substitution map of the
738+
// specialized type.
739+
auto *normalConf = cast<NormalProtocolConformance>(conformance);
740+
auto *conformanceDC = normalConf->getDeclContext();
741+
auto subMap = type->getContextSubstitutionMap(mod, conformanceDC);
742+
return ProtocolConformanceRef(
743+
ctx.getSpecializedConformance(type, normalConf, subMap));
788744
}
789745
}
790746

791-
// Record and return the simple conformance.
747+
// Return the root conformance.
792748
return ProtocolConformanceRef(conformance);
793749
}
794750

lib/AST/ConformanceLookupTable.cpp

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage,
174174
if (loader.first) {
175175
SmallVector<ProtocolConformance *, 2> conformances;
176176
loader.first->loadAllConformances(nominal, loader.second, conformances);
177-
loadAllConformances(nominal, conformances);
177+
registerProtocolConformances(nominal, conformances);
178178
}
179179

180180
nominalFunc(nominal);
@@ -202,7 +202,7 @@ void ConformanceLookupTable::forEachInStage(ConformanceStage stage,
202202
if (loader.first) {
203203
SmallVector<ProtocolConformance *, 2> conformances;
204204
loader.first->loadAllConformances(next, loader.second, conformances);
205-
loadAllConformances(next, conformances);
205+
registerProtocolConformances(next, conformances);
206206
for (auto conf : conformances) {
207207
protocols.push_back(
208208
{conf->getProtocol(), SourceLoc(), SourceLoc(), SourceLoc()});
@@ -295,8 +295,19 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
295295
forEachInStage(
296296
stage, nominal,
297297
[&](NominalTypeDecl *nominal) {
298-
addInheritedProtocols(nominal,
299-
ConformanceSource::forExplicit(nominal));
298+
auto source = ConformanceSource::forExplicit(nominal);
299+
300+
// Get all of the protocols in the inheritance clause.
301+
InvertibleProtocolSet inverses;
302+
bool anyObject = false;
303+
for (const auto &found :
304+
getDirectlyInheritedNominalTypeDecls(nominal, inverses, anyObject)) {
305+
if (auto proto = dyn_cast<ProtocolDecl>(found.Item)) {
306+
addProtocol(proto, found.Loc,
307+
source.withUncheckedLoc(found.uncheckedLoc)
308+
.withPreconcurrencyLoc(found.preconcurrencyLoc));
309+
}
310+
}
300311

301312
addMacroGeneratedProtocols(
302313
nominal, ConformanceSource::forUnexpandedMacro(nominal));
@@ -422,19 +433,17 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
422433
}
423434
}
424435

425-
void ConformanceLookupTable::loadAllConformances(
436+
void ConformanceLookupTable::registerProtocolConformances(
426437
DeclContext *dc,
427438
ArrayRef<ProtocolConformance*> conformances) {
428439
// If this declaration context came from source, there's nothing to
429440
// do here.
430-
if (dc->getParentSourceFile() ||
431-
dc->getParentModule()->isBuiltinModule()) {
432-
return;
433-
}
441+
assert(!dc->getParentSourceFile() &&
442+
!dc->getParentModule()->isBuiltinModule());
434443

435444
// Add entries for each loaded conformance.
436445
for (auto conformance : conformances) {
437-
registerProtocolConformance(conformance);
446+
registerProtocolConformance(dc, conformance);
438447
}
439448
}
440449

@@ -494,22 +503,6 @@ bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc,
494503
return true;
495504
}
496505

497-
void ConformanceLookupTable::addInheritedProtocols(
498-
llvm::PointerUnion<const TypeDecl *, const ExtensionDecl *> decl,
499-
ConformanceSource source) {
500-
// Find all of the protocols in the inheritance list.
501-
InvertibleProtocolSet inverses;
502-
bool anyObject = false;
503-
for (const auto &found :
504-
getDirectlyInheritedNominalTypeDecls(decl, inverses, anyObject)) {
505-
if (auto proto = dyn_cast<ProtocolDecl>(found.Item)) {
506-
addProtocol(proto, found.Loc,
507-
source.withUncheckedLoc(found.uncheckedLoc)
508-
.withPreconcurrencyLoc(found.preconcurrencyLoc));
509-
}
510-
}
511-
}
512-
513506
void ConformanceLookupTable::addMacroGeneratedProtocols(
514507
NominalTypeDecl *nominal, ConformanceSource source) {
515508
nominal->forEachAttachedMacro(
@@ -928,7 +921,22 @@ ConformanceLookupTable::getConformance(NominalTypeDecl *nominal,
928921
// Form the conformance.
929922
Type type = entry->getDeclContext()->getDeclaredInterfaceType();
930923
ASTContext &ctx = nominal->getASTContext();
931-
if (entry->getKind() == ConformanceEntryKind::Inherited) {
924+
925+
if (protocol->getInvertibleProtocolKind() &&
926+
entry->getDeclContext() == nominal &&
927+
(entry->getKind() == ConformanceEntryKind::Synthesized ||
928+
entry->getKind() == ConformanceEntryKind::Inherited)) {
929+
// Unconditional conformances to Copyable and Escapable are represented as
930+
// builtin conformances, which do not need to store a substitution map.
931+
//
932+
// This avoids an exponential blowup when constructing the context
933+
// substitution map for a type like G<G<G<G<...>>>>.
934+
Type conformingType = nominal->getSelfInterfaceType();
935+
936+
entry->Conformance = ctx.getBuiltinConformance(
937+
conformingType, protocol, BuiltinConformanceKind::Synthesized);
938+
939+
} else if (entry->getKind() == ConformanceEntryKind::Inherited) {
932940
// For an inherited conformance, the conforming nominal type will
933941
// be different from the nominal type.
934942
assert(conformingNominal != nominal && "Broken inherited conformance");
@@ -1024,10 +1032,9 @@ void ConformanceLookupTable::addSynthesizedConformance(
10241032
}
10251033

10261034
void ConformanceLookupTable::registerProtocolConformance(
1027-
ProtocolConformance *conformance,
1035+
DeclContext *dc, ProtocolConformance *conformance,
10281036
bool synthesized) {
10291037
auto protocol = conformance->getProtocol();
1030-
auto dc = conformance->getDeclContext();
10311038
auto nominal = dc->getSelfNominalTypeDecl();
10321039

10331040
// If there is an entry to update, do so.

0 commit comments

Comments
 (0)