Skip to content

Commit 500a470

Browse files
committed
[Generics] ~Copyable in inheritance clauses
Have `~Copyable` change the signatures of a generic type param, protocol, or associated type if written in the inheritance clause position.
1 parent 0c23e66 commit 500a470

File tree

6 files changed

+93
-11
lines changed

6 files changed

+93
-11
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2349,6 +2349,10 @@ ERROR(inferred_opaque_type,none,
23492349
"property definition has inferred type %0, involving the 'some' "
23502350
"return type of another declaration", (Type))
23512351

2352+
// Inverse Constraints
2353+
ERROR(inverse_type_not_invertable,none,
2354+
"type %0 is not invertable", (Type))
2355+
23522356
// Extensions
23532357
ERROR(non_nominal_extension,none,
23542358
"non-nominal type %0 cannot be extended", (Type))

include/swift/AST/KnownProtocols.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_AST_KNOWNPROTOCOLS_H
1515

1616
#include "swift/Basic/InlineBitfield.h"
17+
#include "swift/Basic/FixedBitSet.h"
1718
#include "swift/Config.h"
1819

1920
namespace llvm {
@@ -41,6 +42,12 @@ enum : uint8_t {
4142
enum : unsigned { NumKnownProtocolKindBits =
4243
countBitsUsed(static_cast<unsigned>(NumKnownProtocols - 1)) };
4344

45+
using KnownProtocolSet = FixedBitSet<NumKnownProtocols, KnownProtocolKind>;
46+
47+
/// Produces a set of all protocols that have an inverse, i.e., for every
48+
/// known protocol KP in the set, ~KP exists.
49+
KnownProtocolSet getInvertableProtocols();
50+
4451
/// Retrieve the name of the given known protocol.
4552
llvm::StringRef getProtocolName(KnownProtocolKind kind);
4653

lib/AST/ASTContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ llvm::StringRef swift::getProtocolName(KnownProtocolKind kind) {
102102
llvm_unreachable("bad KnownProtocolKind");
103103
}
104104

105+
KnownProtocolSet swift::getInvertableProtocols() {
106+
return { KnownProtocolKind::Copyable };
107+
}
108+
105109
namespace {
106110
enum class SearchPathKind : uint8_t {
107111
Import = 1 << 0,

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,7 @@ void swift::rewriting::realizeInheritedRequirements(
732732
auto inheritedTypes = decl->getInherited();
733733
auto *dc = decl->getInnermostDeclContext();
734734
auto *moduleForInference = dc->getParentModule();
735+
auto defaults = getInvertableProtocols();
735736

736737
for (auto index : inheritedTypes.getIndices()) {
737738
Type inheritedType =
@@ -749,6 +750,17 @@ void swift::rewriting::realizeInheritedRequirements(
749750

750751
auto *typeRepr = inheritedTypes.getTypeRepr(index);
751752
SourceLoc loc = (typeRepr ? typeRepr->getStartLoc() : SourceLoc());
753+
754+
// For any inverses, we only need to cancel out a default requirement.
755+
if (dyn_cast_or_null<InverseTypeRepr>(typeRepr)) {
756+
if (auto protoTy = inheritedType->getAs<ProtocolType>())
757+
if (auto protoDecl = protoTy->getDecl())
758+
if (auto kp = protoDecl->getKnownProtocolKind())
759+
defaults.remove(*kp);
760+
761+
continue;
762+
}
763+
752764
if (shouldInferRequirements) {
753765
inferRequirements(inheritedType, loc, moduleForInference,
754766
decl->getInnermostDeclContext(), result);
@@ -759,15 +771,17 @@ void swift::rewriting::realizeInheritedRequirements(
759771

760772
auto &ctx = dc->getASTContext();
761773
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
762-
// All associated types or generic type params are Copyable by default.
763-
// TODO: if we saw ~Copyable in the inherited types list earlier, skip this!
774+
// Add the remaining default conformance requirements to this
775+
// associated type or generic type param.
776+
for (auto kp : defaults) {
777+
ProtocolDecl *decl = ctx.getProtocol(kp);
778+
assert(decl && "couldn't load protocol??");
764779

765-
if (auto *copyable = ctx.getProtocol(KnownProtocolKind::Copyable)) {
766780
SourceLoc loc = decl->getLoc();
767-
Type copyableType = copyable->getDeclaredInterfaceType();
781+
Type protocolType = decl->getDeclaredInterfaceType();
768782

769-
// Add Copyable as an "inferred" requirement.
770-
Requirement req(RequirementKind::Conformance, type, copyableType);
783+
// Add the protocol as an "inferred" requirement.
784+
Requirement req(RequirementKind::Conformance, type, protocolType);
771785
result.push_back({req, loc, /*wasInferred=*/true});
772786
}
773787
}

lib/Sema/TypeCheckType.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5046,14 +5046,14 @@ NeverNullType TypeResolver::resolveInverseType(InverseTypeRepr *repr,
50465046
if (ty->hasError())
50475047
return ErrorType::get(getASTContext());
50485048

5049-
// TODO: more user-friendly verification
5050-
50515049
if (auto protoTy = ty->getAs<ProtocolType>())
50525050
if (auto protoDecl = protoTy->getDecl())
5053-
if (protoDecl->isSpecificProtocol(KnownProtocolKind::Copyable))
5054-
return ty;
5051+
if (auto kp = protoDecl->getKnownProtocolKind())
5052+
if (getInvertableProtocols().contains(*kp))
5053+
return ty;
50555054

5056-
llvm_unreachable("todo: verification");
5055+
diagnoseInvalid(repr, repr->getLoc(), diag::inverse_type_not_invertable, ty);
5056+
return ErrorType::get(getASTContext());
50575057
}
50585058

50595059
NeverNullType TypeResolver::resolveProtocolType(ProtocolTypeRepr *repr,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %target-swift-frontend -enable-experimental-feature NoncopyableGenerics -typecheck %s -debug-generic-signatures 2>&1 | %FileCheck %s
2+
3+
// CHECK-LABEL: (file).genericFn@
4+
// CHECK: Generic signature: <T where T : Copyable>
5+
func genericFn<T>(_ t: T) {}
6+
7+
// CHECK-LABEL: .withInverse@
8+
// CHECK: Generic signature: <T>
9+
func withInverse<T: ~Copyable>(_ t: T) {}
10+
11+
// CHECK-LABEL: .S1@
12+
// CHECK: Generic signature: <T where T : Copyable>
13+
struct S1<T> {}
14+
15+
// CHECK-LABEL: .S1_I@
16+
// CHECK: Generic signature: <T>
17+
struct S1_I<T: ~Copyable> {}
18+
19+
// CHECK-LABEL: .C1@
20+
// CHECK: Generic signature: <T, U where T : Copyable, U : Copyable>
21+
class C1<T, U> {}
22+
23+
// CHECK-LABEL: .C1_IC@
24+
// CHECK: Generic signature: <T, U where U : Copyable>
25+
class C1_IC<T: ~Copyable, U> {}
26+
27+
// CHECK-LABEL: .C1_CI@
28+
// CHECK: Generic signature: <T, U where T : Copyable>
29+
class C1_CI<T, U: ~Copyable> {}
30+
31+
// CHECK-LABEL: .C1_II@
32+
// CHECK: Generic signature: <T, U>
33+
class C1_II<T: ~Copyable, U: ~Copyable> {}
34+
35+
// CHECK-LABEL: .P1@
36+
// CHECK: Requirement signature: <Self where Self : Copyable>
37+
protocol P1 {}
38+
39+
// CHECK-LABEL: .P2@
40+
// CHECK: <Self where Self : Copyable, Self.[P2]A : Copyable>
41+
protocol P2 { associatedtype A }
42+
43+
// CHECK-LABEL: .P2_IC@
44+
// CHECK: <Self where Self.[P2_IC]A : Copyable>
45+
protocol P2_IC: ~Copyable { associatedtype A }
46+
47+
// CHECK-LABEL: .P2_CI@
48+
// CHECK: Requirement signature: <Self where Self : Copyable>
49+
protocol P2_CI { associatedtype A: ~Copyable }
50+
51+
// CHECK-LABEL: .P2_II@
52+
// CHECK: Requirement signature: <Self>
53+
protocol P2_II: ~Copyable { associatedtype A: ~Copyable }

0 commit comments

Comments
 (0)