Skip to content

Commit 2cd2989

Browse files
committed
[NCGenerics] fix existential conformances
I was not expanding default requirements in AbstractGenericSignatureRequest or ExistentialLayout. Also fixes printing of composition types.
1 parent 1797c4c commit 2cd2989

File tree

11 files changed

+134
-44
lines changed

11 files changed

+134
-44
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5179,6 +5179,10 @@ class ProtocolDecl final : public NominalTypeDecl {
51795179
/// semantics but has no corresponding witness table.
51805180
bool isMarkerProtocol() const;
51815181

5182+
/// Determine whether this is an invertible protocol,
5183+
/// i.e., for a protocol P, the inverse constraint ~P exists.
5184+
bool isInvertibleProtocol() const;
5185+
51825186
private:
51835187
void computeKnownProtocolKind() const;
51845188

include/swift/AST/ExistentialLayout.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ struct ExistentialLayout {
3131

3232
ExistentialLayout() {
3333
hasExplicitAnyObject = false;
34-
hasInverseCopyable = false;
3534
containsNonObjCProtocol = false;
3635
containsParameterized = false;
36+
representsAnyObject = false;
3737
}
3838

3939
ExistentialLayout(CanProtocolType type);
@@ -47,15 +47,15 @@ struct ExistentialLayout {
4747
/// Whether the existential contains an explicit '& AnyObject' constraint.
4848
bool hasExplicitAnyObject : 1;
4949

50-
/// Whether the existential contains an explicit '& ~Copyable' constraint.
51-
bool hasInverseCopyable : 1;
52-
5350
/// Whether any protocol members are non-@objc.
5451
bool containsNonObjCProtocol : 1;
5552

5653
/// Whether any protocol members are parameterized.s
5754
bool containsParameterized : 1;
5855

56+
/// Whether this layout is the canonical layout for plain-old 'AnyObject'.
57+
bool representsAnyObject : 1;
58+
5959
/// Return the kind of this existential (class/error/opaque).
6060
Kind getKind() {
6161
if (requiresClass())
@@ -69,7 +69,7 @@ struct ExistentialLayout {
6969
return Kind::Opaque;
7070
}
7171

72-
bool isAnyObject() const;
72+
bool isAnyObject() const { return representsAnyObject; }
7373

7474
bool isObjC() const {
7575
// FIXME: Does the superclass have to be @objc?

include/swift/AST/Types.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5790,8 +5790,6 @@ class ProtocolCompositionType final : public TypeBase,
57905790
private llvm::TrailingObjects<ProtocolCompositionType, Type> {
57915791
friend TrailingObjects;
57925792

5793-
// TODO(kavon): this could probably be folded into the existing Bits field
5794-
// or we could just store the InverseType's in the Members array.
57955793
InvertibleProtocolSet Inverses;
57965794

57975795
public:

lib/AST/ASTDumper.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4340,6 +4340,14 @@ namespace {
43404340

43414341
printFlag(T->hasExplicitAnyObject(), "any_object");
43424342

4343+
for (auto ip : T->getInverses()) {
4344+
switch (ip) {
4345+
case InvertibleProtocolKind::Copyable:
4346+
printFlag("inverse_copyable");
4347+
break;
4348+
}
4349+
}
4350+
43434351
for (auto proto : T->getMembers()) {
43444352
printRec(proto);
43454353
}

lib/AST/ASTPrinter.cpp

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7192,17 +7192,29 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
71927192
}
71937193

71947194
void visitProtocolCompositionType(ProtocolCompositionType *T) {
7195-
if (T->getMembers().empty()) {
7196-
if (T->hasExplicitAnyObject())
7197-
Printer << "AnyObject";
7198-
else
7199-
Printer.printKeyword("Any", Options);
7200-
} else {
7201-
interleave(T->getMembers(), [&](Type Ty) { visit(Ty); },
7202-
[&] { Printer << " & "; });
7203-
if (T->hasExplicitAnyObject())
7204-
Printer << " & AnyObject";
7205-
}
7195+
interleave(T->getMembers(), [&](Type Ty) { visit(Ty); },
7196+
[&] { Printer << " & "; });
7197+
7198+
bool printed = !T->getMembers().empty();
7199+
auto printSpecial = [&](llvm::StringRef str, bool tilde=false) {
7200+
if (printed)
7201+
Printer << " & ";
7202+
7203+
if (tilde)
7204+
Printer << "~";
7205+
7206+
Printer << str;
7207+
printed = true;
7208+
};
7209+
7210+
if (T->hasExplicitAnyObject())
7211+
printSpecial("AnyObject");
7212+
7213+
for (auto ip : T->getInverses())
7214+
printSpecial(getProtocolName(getKnownProtocolKind(ip)), true);
7215+
7216+
if (!printed)
7217+
Printer.printKeyword("Any", Options);
72067218
}
72077219

72087220
void visitParameterizedProtocolType(ParameterizedProtocolType *T) {

lib/AST/Decl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6383,6 +6383,16 @@ bool ProtocolDecl::isMarkerProtocol() const {
63836383
return getAttrs().hasAttribute<MarkerAttr>();
63846384
}
63856385

6386+
bool ProtocolDecl::isInvertibleProtocol() const {
6387+
if (auto kp = getKnownProtocolKind()) {
6388+
if (getInvertibleProtocolKind(*kp)) {
6389+
assert(isMarkerProtocol());
6390+
return true;
6391+
}
6392+
}
6393+
return false;
6394+
}
6395+
63866396
ArrayRef<ProtocolDecl *> ProtocolDecl::getInheritedProtocols() const {
63876397
auto *mutThis = const_cast<ProtocolDecl *>(this);
63886398
return evaluateOrDefault(getASTContext().evaluator,

lib/AST/Module.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,11 +1591,12 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
15911591
if (!protocol->existentialConformsToSelf())
15921592
return ProtocolConformanceRef::forInvalid();
15931593

1594-
// All existentials are Copyable.
1595-
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
1596-
return ProtocolConformanceRef(
1597-
ctx.getBuiltinConformance(type, protocol,
1598-
BuiltinConformanceKind::Synthesized));
1594+
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)
1595+
&& !ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
1596+
// Prior to noncopyable generics, all existentials conform to Copyable.
1597+
return ProtocolConformanceRef(
1598+
ctx.getBuiltinConformance(type, protocol,
1599+
BuiltinConformanceKind::Synthesized));
15991600
}
16001601

16011602
auto layout = type->getExistentialLayout();

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,13 @@ AbstractGenericSignatureRequest::evaluate(
658658
requirements.push_back({req, SourceLoc(), /*wasInferred=*/false});
659659
}
660660

661+
/// Next, we need to expand default requirements for the added parameters.
662+
SmallVector<Type, 2> localGPs;
663+
for (auto *gtpt : addedParameters)
664+
localGPs.push_back(gtpt);
665+
666+
expandDefaultRequirements(ctx, localGPs, requirements, errors);
667+
661668
auto &rewriteCtx = ctx.getRewriteContext();
662669

663670
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {

lib/AST/Type.cpp

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -335,44 +335,65 @@ bool TypeBase::allowsOwnership(const GenericSignatureImpl *sig) {
335335
return getCanonicalType().allowsOwnership(sig);
336336
}
337337

338+
/// Adds the inferred default protocols for an ExistentialLayout with respect
339+
/// to that existential's inverses. For example, if an inverse ~P exists, then
340+
/// P will not be added to the protocols list.
341+
///
342+
/// \param inverses the inverses '& ~P' that are in the existential's type.
343+
/// \param protocols the output vector of protocols for an ExistentialLayout
344+
/// to be modified.
345+
static void expandDefaultProtocols(
346+
ASTContext &ctx,
347+
InvertibleProtocolSet inverses,
348+
SmallVectorImpl<ProtocolDecl*> &protocols) {
349+
350+
// Skip unless noncopyable generics is enabled
351+
if (!ctx.LangOpts.hasFeature(swift::Feature::NoncopyableGenerics))
352+
return;
353+
354+
// Try to add all invertible protocols, unless an inverse was provided.
355+
for (auto ip : InvertibleProtocolSet::full()) {
356+
if (inverses.contains(ip))
357+
continue;
358+
359+
auto proto = ctx.getProtocol(getKnownProtocolKind(ip));
360+
assert(proto);
361+
362+
protocols.push_back(proto);
363+
}
364+
}
365+
338366
ExistentialLayout::ExistentialLayout(CanProtocolType type) {
339367
auto *protoDecl = type->getDecl();
340368

341369
hasExplicitAnyObject = false;
342-
hasInverseCopyable = false;
343370
containsNonObjCProtocol = !isObjCProtocol(protoDecl);
344371
containsParameterized = false;
372+
representsAnyObject = false;
345373

346374
protocols.push_back(protoDecl);
375+
376+
// NOTE: all the invertible protocols are usable from ObjC.
377+
expandDefaultProtocols(type->getASTContext(), {}, protocols);
347378
}
348379

349380
ExistentialLayout::ExistentialLayout(CanInverseType type) {
350381
hasExplicitAnyObject = false;
351-
hasInverseCopyable = false;
352382
containsNonObjCProtocol = false;
353383
containsParameterized = false;
384+
representsAnyObject = false;
354385

355-
// Handle inverse.
356-
switch (type->getInverseKind()) {
357-
case InvertibleProtocolKind::Copyable:
358-
hasInverseCopyable = true;
359-
}
386+
// NOTE: all the invertible protocols are usable from ObjC.
387+
expandDefaultProtocols(type->getASTContext(),
388+
{type->getInverseKind()},
389+
protocols);
360390
}
361391

362392
ExistentialLayout::ExistentialLayout(CanProtocolCompositionType type) {
363393
hasExplicitAnyObject = type->hasExplicitAnyObject();
364-
hasInverseCopyable = false;
365394
containsNonObjCProtocol = false;
366395
containsParameterized = false;
367396

368-
// Handle inverses.
369-
for (auto ip : type->getInverses()) {
370-
switch (ip) {
371-
case InvertibleProtocolKind::Copyable:
372-
hasInverseCopyable = true;
373-
}
374-
}
375-
376397
auto members = type.getMembers();
377398
if (!members.empty() &&
378399
(members[0].getClassOrBoundGenericClass() ||
@@ -393,6 +414,12 @@ ExistentialLayout::ExistentialLayout(CanProtocolCompositionType type) {
393414
containsNonObjCProtocol |= !isObjCProtocol(protoDecl);
394415
protocols.push_back(protoDecl);
395416
}
417+
418+
representsAnyObject =
419+
hasExplicitAnyObject && !explicitSuperclass && getProtocols().empty();
420+
421+
// NOTE: all the invertible protocols are usable from ObjC.
422+
expandDefaultProtocols(type->getASTContext(), type->getInverses(), protocols);
396423
}
397424

398425
ExistentialLayout::ExistentialLayout(CanParameterizedProtocolType type)
@@ -466,10 +493,6 @@ Type ExistentialLayout::getSuperclass() const {
466493
return Type();
467494
}
468495

469-
bool ExistentialLayout::isAnyObject() const {
470-
return (hasExplicitAnyObject && !explicitSuperclass && getProtocols().empty());
471-
}
472-
473496
bool TypeBase::isObjCExistentialType() {
474497
return getCanonicalType().isObjCExistentialType();
475498
}

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()) {
418+
if (proto->isMarkerProtocol() && !proto->isInvertibleProtocol()) {
419419
// can't conditionally cast to a marker protocol
420420
Ctx.Diags.diagnose(cast->getLoc(), diag::marker_protocol_cast,
421421
proto->getName());

test/Generics/inverse_copyable_generics.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,30 @@ extension EnumExtendo: ~Copyable {} // expected-error {{cannot apply inverse '~C
177177

178178
extension NeedsCopyable where Self: ~Copyable {}
179179
// expected-error@-1 {{cannot add inverse constraint 'Self: ~Copyable' on generic parameter 'Self' defined in outer scope}}
180+
181+
protocol NoCopyP: ~Copyable {}
182+
183+
func needsCopyable<T>(_ t: T) {} // expected-note 2{{generic parameter 'T' has an implicit Copyable requirement}}
184+
func noCopyable(_ t: borrowing some ~Copyable) {}
185+
func noCopyableAndP(_ t: borrowing some NoCopyP & ~Copyable) {}
186+
187+
func openingExistentials(_ a: borrowing any NoCopyP & ~Copyable,
188+
_ b: any NoCopyP,
189+
_ nc: borrowing any ~Copyable) {
190+
needsCopyable(a) // expected-error {{noncopyable type 'any NoCopyP & ~Copyable' cannot be substituted for copyable generic parameter 'T' in 'needsCopyable'}}
191+
noCopyable(a)
192+
noCopyableAndP(a)
193+
194+
needsCopyable(b)
195+
noCopyable(b)
196+
noCopyableAndP(b)
197+
198+
needsCopyable(nc) // expected-error {{noncopyable type 'any ~Copyable' cannot be substituted for copyable generic parameter 'T' in 'needsCopyable'}}
199+
noCopyable(nc)
200+
noCopyableAndP(nc) // expected-error {{global function 'noCopyableAndP' requires that 'some NoCopyP & ~Copyable' conform to 'NoCopyP'}}
201+
}
202+
203+
func project<CurValue>(_ base: CurValue) { }
204+
func testSpecial(_ a: Any) {
205+
_openExistential(a, do: project)
206+
}

0 commit comments

Comments
 (0)