Skip to content

Commit 778a413

Browse files
committed
[NCGenerics] ensure ExistentialLayout is minimal
Instead of injecting Copyable & Escapable protocols into every ExistentialLayout, only add those protocols if the existing protocols don't already imply them. This simplifies things like `any Error` protocol, so it still only lists one protocol in its existential layout. But existentials like `any NoCopyP` still end up with a Copyable in its layout.
1 parent af7ff43 commit 778a413

File tree

5 files changed

+62
-15
lines changed

5 files changed

+62
-15
lines changed

include/swift/AST/Decl.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5171,6 +5171,10 @@ class ProtocolDecl final : public NominalTypeDecl {
51715171
/// Determine whether this protocol inherits from the given ("super")
51725172
/// protocol.
51735173
bool inheritsFrom(const ProtocolDecl *Super) const;
5174+
5175+
/// Determine whether this protocol requires conformance to `IP`, without
5176+
/// querying a generic signature.
5177+
bool requiresInvertible(InvertibleProtocolKind ip) const;
51745178

51755179
SourceLoc getStartLoc() const { return ProtocolLoc; }
51765180
SourceRange getSourceRange() const {
@@ -5216,9 +5220,9 @@ class ProtocolDecl final : public NominalTypeDecl {
52165220
/// semantics but has no corresponding witness table.
52175221
bool isMarkerProtocol() const;
52185222

5219-
/// Determine whether this is an invertible protocol,
5220-
/// i.e., for a protocol P, the inverse constraint ~P exists.
5221-
bool isInvertibleProtocol() const;
5223+
/// Determine if this is an invertible protocol and return its kind,
5224+
/// i.e., for a protocol P, returns the kind if inverse constraint ~P exists.
5225+
llvm::Optional<InvertibleProtocolKind> getInvertibleProtocolKind() const;
52225226

52235227
private:
52245228
void computeKnownProtocolKind() const;

include/swift/AST/Types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5873,6 +5873,7 @@ class ProtocolCompositionType final : public TypeBase,
58735873
private llvm::TrailingObjects<ProtocolCompositionType, Type> {
58745874
friend TrailingObjects;
58755875

5876+
// The inverse constraints `& ~IP` that are part of this composition.
58765877
InvertibleProtocolSet Inverses;
58775878

58785879
public:

lib/AST/Decl.cpp

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6414,14 +6414,12 @@ bool ProtocolDecl::isMarkerProtocol() const {
64146414
return getAttrs().hasAttribute<MarkerAttr>();
64156415
}
64166416

6417-
bool ProtocolDecl::isInvertibleProtocol() const {
6418-
if (auto kp = getKnownProtocolKind()) {
6419-
if (getInvertibleProtocolKind(*kp)) {
6420-
assert(isMarkerProtocol());
6421-
return true;
6422-
}
6423-
}
6424-
return false;
6417+
llvm::Optional<InvertibleProtocolKind>
6418+
ProtocolDecl::getInvertibleProtocolKind() const {
6419+
if (auto kp = getKnownProtocolKind())
6420+
return ::getInvertibleProtocolKind(*kp);
6421+
6422+
return llvm::None;
64256423
}
64266424

64276425
ArrayRef<ProtocolDecl *> ProtocolDecl::getInheritedProtocols() const {
@@ -6563,6 +6561,9 @@ bool ProtocolDecl::inheritsFrom(const ProtocolDecl *super) const {
65636561
if (this == super)
65646562
return false;
65656563

6564+
if (auto ip = super->getInvertibleProtocolKind())
6565+
return requiresInvertible(*ip);
6566+
65666567
return walkInheritedProtocols([super](ProtocolDecl *inherited) {
65676568
if (inherited == super)
65686569
return TypeWalker::Action::Stop;
@@ -6571,6 +6572,30 @@ bool ProtocolDecl::inheritsFrom(const ProtocolDecl *super) const {
65716572
});
65726573
}
65736574

6575+
bool ProtocolDecl::requiresInvertible(InvertibleProtocolKind ip) const {
6576+
// HACK: until we enable Feature::NoncopyableGenerics in the stdlib,
6577+
// hardcode the fact that an invertible protocol does not require any other!
6578+
if (getInvertibleProtocolKind())
6579+
return false;
6580+
6581+
auto kp = ::getKnownProtocolKind(ip);
6582+
return walkInheritedProtocols([kp, ip](ProtocolDecl *proto) {
6583+
if (proto->isSpecificProtocol(kp))
6584+
return TypeWalker::Action::Stop; // it is required.
6585+
6586+
switch (proto->getMarking(ip).getInverse().getKind()) {
6587+
case InverseMarking::Kind::None:
6588+
return TypeWalker::Action::Stop; // it is required.
6589+
6590+
case InverseMarking::Kind::LegacyExplicit:
6591+
case InverseMarking::Kind::Explicit:
6592+
case InverseMarking::Kind::Inferred:
6593+
// the implicit requirement was suppressed on this protocol, keep looking.
6594+
return TypeWalker::Action::Continue;
6595+
}
6596+
});
6597+
}
6598+
65746599
bool ProtocolDecl::requiresClass() const {
65756600
return evaluateOrDefault(getASTContext().evaluator,
65766601
ProtocolRequiresClassRequest{const_cast<ProtocolDecl *>(this)}, false);

lib/AST/Type.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,11 @@ bool TypeBase::allowsOwnership(const GenericSignatureImpl *sig) {
345345
}
346346

347347
/// Adds the inferred default protocols for an ExistentialLayout with respect
348-
/// to that existential's inverses. For example, if an inverse ~P exists, then
349-
/// P will not be added to the protocols list.
348+
/// to that existential's inverses and existing protocols. For example, if an
349+
/// inverse ~P exists, then P will not be added to the protocols list.
350+
///
351+
/// Similarly, if the protocols list has a protocol Q that already implies
352+
/// Copyable, then we will not add `Copyable` to the protocols list.
350353
///
351354
/// \param inverses the inverses '& ~P' that are in the existential's type.
352355
/// \param protocols the output vector of protocols for an ExistentialLayout
@@ -360,11 +363,25 @@ static void expandDefaultProtocols(
360363
if (!ctx.LangOpts.hasFeature(swift::Feature::NoncopyableGenerics))
361364
return;
362365

363-
// Try to add all invertible protocols, unless an inverse was provided.
366+
// Try to add all invertible protocols, unless:
367+
// - an inverse was provided
368+
// - an existing protocol already requires it
364369
for (auto ip : InvertibleProtocolSet::full()) {
365370
if (inverses.contains(ip))
366371
continue;
367372

373+
// This matches with `lookupExistentialConformance`'s use of 'inheritsFrom'.
374+
bool alreadyRequired = false;
375+
for (auto proto : protocols) {
376+
if (proto->requiresInvertible(ip)) {
377+
alreadyRequired = true;
378+
break;
379+
}
380+
}
381+
382+
if (alreadyRequired)
383+
continue;
384+
368385
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
369386
assert(proto);
370387

lib/Sema/MiscDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC,
415415

416416
auto layout = castType->getExistentialLayout();
417417
for (auto proto : layout.getProtocols()) {
418-
if (proto->isMarkerProtocol() && !proto->isInvertibleProtocol()) {
418+
if (proto->isMarkerProtocol() && !proto->getInvertibleProtocolKind()) {
419419
// can't conditionally cast to a marker protocol
420420
Ctx.Diags.diagnose(cast->getLoc(), diag::marker_protocol_cast,
421421
proto->getName());

0 commit comments

Comments
 (0)