Skip to content

Commit 517901e

Browse files
committed
NCGenerics: break cycle with SuperclassTypeRequest
With NoncopyableGenerics, we get a cycle involving `SuperclassTypeRequest` with this program: public struct RawMarkupHeader {} final class RawMarkup: ManagedBuffer<RawMarkupHeader, RawMarkup> { } Because we generally don't support the following kind of relationship: class Base<T: P>: P {} class Derived: Base<Derived> {} This commit works around the root-cause, which is that Derived's synthesized conformance to Copyable gets superceded by the inherited one from Base. Instead of recording conformances in the ConformanceLookup table at all, create builtin conformances on the fly, since classes cannot be conditionally Copyable or Escapable.
1 parent ee5df55 commit 517901e

File tree

4 files changed

+82
-30
lines changed

4 files changed

+82
-30
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,19 @@ class BuiltinProtocolConformance final : public RootProtocolConformance {
11571157
return getBuiltinConformanceKind() == BuiltinConformanceKind::Missing;
11581158
}
11591159

1160+
bool isInvalid() const {
1161+
switch (getBuiltinConformanceKind()) {
1162+
case BuiltinConformanceKind::Synthesized:
1163+
return false;
1164+
case BuiltinConformanceKind::Missing:
1165+
return true;
1166+
}
1167+
}
1168+
1169+
SourceLoc getLoc() const {
1170+
return SourceLoc();
1171+
}
1172+
11601173
/// Get any requirements that must be satisfied for this conformance to apply.
11611174
llvm::Optional<ArrayRef<Requirement>>
11621175
getConditionalRequirementsIfAvailable() const {
@@ -1191,6 +1204,10 @@ class BuiltinProtocolConformance final : public RootProtocolConformance {
11911204
llvm_unreachable("builtin-conformances never have associated types");
11921205
}
11931206

1207+
bool hasWitness(ValueDecl *requirement) const {
1208+
llvm_unreachable("builtin-conformances never have requirement witnesses");
1209+
}
1210+
11941211
/// Retrieve the type witness and type decl (if one exists)
11951212
/// for the given associated type.
11961213
TypeWitnessAndDecl
@@ -1199,6 +1216,10 @@ class BuiltinProtocolConformance final : public RootProtocolConformance {
11991216
llvm_unreachable("builtin-conformances never have associated types");
12001217
}
12011218

1219+
Witness getWitness(ValueDecl *requirement) const {
1220+
llvm_unreachable("builtin-conformances never have requirement witnesses");
1221+
}
1222+
12021223
/// Given that the requirement signature of the protocol directly states
12031224
/// that the given dependent type must conform to the given protocol,
12041225
/// return its associated conformance.

lib/AST/ConformanceLookup.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/AST/DiagnosticsSema.h"
3030
#include "swift/AST/ExistentialLayout.h"
3131
#include "swift/AST/GenericEnvironment.h"
32+
#include "swift/AST/InverseMarking.h"
3233
#include "swift/AST/NameLookup.h"
3334
#include "swift/AST/NameLookupRequests.h"
3435
#include "swift/AST/PackConformance.h"
@@ -402,6 +403,41 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
402403
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
403404
}
404405

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+
auto marking = nominal->getMarking(*ip);
419+
switch (marking.getInverse().getKind()) {
420+
case InverseMarking::Kind::LegacyExplicit:
421+
case InverseMarking::Kind::Explicit:
422+
// An inverse ~Copyable prevents conformance.
423+
return ProtocolConformanceRef::forInvalid();
424+
425+
case InverseMarking::Kind::Inferred: // ignore "inferred" inverse marking
426+
case InverseMarking::Kind::None:
427+
break;
428+
}
429+
}
430+
break;
431+
case InvertibleProtocolKind::Escapable:
432+
// Always conforms.
433+
break;
434+
}
435+
436+
return ProtocolConformanceRef(
437+
ctx.getBuiltinConformance(type, protocol,
438+
BuiltinConformanceKind::Synthesized));
439+
}
440+
405441
/// Synthesize a builtin type conformance to the given protocol, if
406442
/// appropriate.
407443
static ProtocolConformanceRef
@@ -586,6 +622,13 @@ LookupConformanceInModuleRequest::evaluate(
586622
if (!nominal || isa<ProtocolDecl>(nominal))
587623
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
588624

625+
// We specially avoid recording conformances to invertible protocols in a
626+
// class's conformance table. This prevents an evaluator cycle.
627+
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)
628+
&& isa<ClassDecl>(nominal)
629+
&& protocol->getInvertibleProtocolKind())
630+
return getBuiltinInvertibleProtocolConformance(nominal, type, protocol);
631+
589632
// Expand conformances added by extension macros.
590633
//
591634
// FIXME: This expansion should only be done if the

lib/AST/ProtocolConformance.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,10 @@ switch (getKind()) { \
102102
return cast<NormalProtocolConformance>(this)->Method Args; \
103103
case ProtocolConformanceKind::Self: \
104104
return cast<SelfProtocolConformance>(this)->Method Args; \
105+
case ProtocolConformanceKind::Builtin: \
106+
return cast<BuiltinProtocolConformance>(this)->Method Args; \
105107
case ProtocolConformanceKind::Specialized: \
106108
case ProtocolConformanceKind::Inherited: \
107-
case ProtocolConformanceKind::Builtin: \
108109
llvm_unreachable("not a root conformance"); \
109110
} \
110111
llvm_unreachable("bad ProtocolConformanceKind");
@@ -1090,19 +1091,21 @@ void NominalTypeDecl::prepareConformanceTable() const {
10901091
// Synthesize the unconditional conformances to invertible protocols.
10911092
// For conditional ones, see findSynthesizedConformances .
10921093
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
1093-
bool missingOne = false;
1094-
for (auto ip : InvertibleProtocolSet::full()) {
1095-
auto invertible = getMarking(ip);
1096-
if (!invertible.getInverse() || bool(invertible.getPositive()))
1097-
addSynthesized(ctx.getProtocol(getKnownProtocolKind(ip)));
1098-
else
1099-
missingOne = true;
1100-
}
1101-
1102-
// FIXME: rdar://122289155 (NCGenerics: convert Equatable, Hashable, and RawRepresentable to ~Copyable.)
1103-
if (missingOne)
1104-
return;
1094+
// Classes get their conformances during ModuleDecl::lookupConformance.
1095+
if (!isa<ClassDecl>(this)) {
1096+
bool missingOne = false;
1097+
for (auto ip : InvertibleProtocolSet::full()) {
1098+
auto invertible = getMarking(ip);
1099+
if (!invertible.getInverse() || bool(invertible.getPositive()))
1100+
addSynthesized(ctx.getProtocol(getKnownProtocolKind(ip)));
1101+
else
1102+
missingOne = true;
1103+
}
11051104

1105+
// FIXME: rdar://122289155 (NCGenerics: convert Equatable, Hashable, and RawRepresentable to ~Copyable.)
1106+
if (missingOne)
1107+
return;
1108+
}
11061109
} else if (!canBeCopyable()) {
11071110
return; // No synthesized conformances for move-only nominals.
11081111
}

lib/Sema/TypeCheckInvertible.cpp

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
343343
if (!ip)
344344
llvm_unreachable("not an invertible protocol");
345345

346+
assert(!isa<ClassDecl>(nominal) && "classes aren't handled here");
346347
auto file = cast<FileUnit>(nominal->getModuleScopeContext());
347348

348349
// Generates a conformance for the nominal to the protocol.
@@ -403,20 +404,6 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
403404
return generateConformance(ext);
404405
};
405406

406-
switch (*ip) {
407-
case InvertibleProtocolKind::Copyable:
408-
// If move-only classes is enabled, we'll check the markings.
409-
if (ctx.LangOpts.hasFeature(Feature::MoveOnlyClasses))
410-
break;
411-
412-
LLVM_FALLTHROUGH;
413-
case InvertibleProtocolKind::Escapable:
414-
// Always derive unconditional IP conformance for classes
415-
if (isa<ClassDecl>(nominal))
416-
return generateConformance(nominal);
417-
break;
418-
}
419-
420407
auto marking = nominal->getMarking(*ip);
421408

422409
// Unexpected to have any positive marking for IP if we're deriving it.
@@ -430,10 +417,8 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
430417
return nullptr; // No positive IP conformance will be inferred.
431418

432419
case InverseMarking::Kind::Inferred:
433-
if (!isa<ClassDecl>(nominal))
434-
return generateConditionalConformance();
420+
return generateConditionalConformance();
435421

436-
LLVM_FALLTHROUGH;
437422
case InverseMarking::Kind::None:
438423
// All types already start with conformances to the invertible protocols in
439424
// this case, within `NominalTypeDecl::prepareConformanceTable`.

0 commit comments

Comments
 (0)