Skip to content

Commit a921818

Browse files
committed
[Sema] handle inverses everywhere
Previously, inverses were only accounted-for in inheritance clauses. This batch of changes handles inverses appearing in other places, like: - Protocol compositions - `some ~Copyable` - where clauses with proper attribution of default requirements in their absence.
1 parent 66712ce commit a921818

18 files changed

+339
-131
lines changed

include/swift/AST/ExistentialLayout.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct ExistentialLayout {
3636
}
3737

3838
ExistentialLayout(CanProtocolType type);
39+
ExistentialLayout(CanInverseType type);
3940
ExistentialLayout(CanProtocolCompositionType type);
4041
ExistentialLayout(CanParameterizedProtocolType type);
4142

include/swift/AST/KnownProtocols.def

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@
4141
PROTOCOL_WITH_NAME(Id, Name)
4242
#endif
4343

44+
/// \def INVERTIBLE_PROTOCOL_WITH_NAME(id, name)
45+
/// \param Name a string literal with the external name of this protocol
46+
/// \param Id the internal "enum name" of this protocol.
47+
#ifndef INVERTIBLE_PROTOCOL_WITH_NAME
48+
#define INVERTIBLE_PROTOCOL_WITH_NAME(Id, Name) \
49+
PROTOCOL_WITH_NAME(Id, Name)
50+
#endif
51+
4452

4553
#define PROTOCOL(name) PROTOCOL_WITH_NAME(name, #name)
4654
#define PROTOCOL_(name) PROTOCOL_WITH_NAME(name, "_" #name)
@@ -127,7 +135,7 @@ PROTOCOL(AsyncIteratorProtocol)
127135

128136
PROTOCOL(FloatingPoint)
129137

130-
PROTOCOL(Copyable)
138+
INVERTIBLE_PROTOCOL_WITH_NAME(Copyable, "Copyable")
131139

132140
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)
133141
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByBooleanLiteral, "BooleanLiteralType", true)
@@ -156,6 +164,7 @@ BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_(ExpressibleByBuiltinUnicodeScalarLitera
156164
#undef EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME
157165
#undef BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_
158166
#undef BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME
167+
#undef INVERTIBLE_PROTOCOL_WITH_NAME
159168
#undef PROTOCOL
160169
#undef PROTOCOL_
161170
#undef PROTOCOL_WITH_NAME

include/swift/AST/KnownProtocols.h

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,37 @@ enum : uint8_t {
4242
enum : unsigned { NumKnownProtocolKindBits =
4343
countBitsUsed(static_cast<unsigned>(NumKnownProtocols - 1)) };
4444

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 getInvertibleProtocols();
50-
5145
/// Retrieve the name of the given known protocol.
5246
llvm::StringRef getProtocolName(KnownProtocolKind kind);
5347

48+
/// MARK: Invertible protocols
49+
///
50+
/// The invertible protocols are a subset of the known protocols.
51+
52+
enum : uint8_t {
53+
// Use preprocessor trick to count all the invertible protocols.
54+
#define INVERTIBLE_PROTOCOL_WITH_NAME(Id, Name) +1
55+
/// The number of invertible protocols.
56+
NumInvertibleProtocols =
57+
#include "swift/AST/KnownProtocols.def"
58+
};
59+
60+
enum class InvertibleProtocolKind : uint8_t {
61+
#define INVERTIBLE_PROTOCOL_WITH_NAME(Id, Name) Id,
62+
#include "swift/AST/KnownProtocols.def"
63+
};
64+
65+
using InvertibleProtocolSet = FixedBitSet<NumInvertibleProtocols,
66+
InvertibleProtocolKind>;
67+
68+
/// Maps a KnownProtocol to the set of InvertibleProtocols, if a mapping exists.
69+
/// \returns None if the known protocol is not invertible.
70+
llvm::Optional<InvertibleProtocolKind>
71+
getInvertibleProtocolKind(KnownProtocolKind kp);
72+
73+
/// Returns the KnownProtocolKind corresponding to an InvertibleProtocolKind.
74+
KnownProtocolKind getKnownProtocolKind(InvertibleProtocolKind ip);
75+
5476
} // end namespace swift
5577

5678
#endif

include/swift/AST/TypeMatcher.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,8 @@ class TypeMatcher {
500500
bool visitInverseType(CanInverseType firstInverse,
501501
Type secondType,
502502
Type sugaredFirstType) {
503-
// FIXME: If this visitor is reached, determine whether it should've been.
503+
// NOTE: If this visitor is reached, determine whether it should've been
504+
// before implementing this.
504505
llvm_unreachable("Yahaha! You found me!");
505506
}
506507

include/swift/AST/Types.h

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/ExtInfo.h"
2525
#include "swift/AST/GenericParamKey.h"
2626
#include "swift/AST/Identifier.h"
27+
#include "swift/AST/KnownProtocols.h"
2728
#include "swift/AST/Ownership.h"
2829
#include "swift/AST/ProtocolConformanceRef.h"
2930
#include "swift/AST/Requirement.h"
@@ -100,7 +101,6 @@ class ProtocolConformance;
100101
enum PointerTypeKind : unsigned;
101102
struct ValueOwnershipKind;
102103
class ErrorExpr;
103-
enum class KnownProtocolKind : uint8_t;
104104

105105
typedef CanTypeWrapper<SILFunctionType> CanSILFunctionType;
106106

@@ -1530,11 +1530,16 @@ DEFINE_EMPTY_CAN_TYPE_WRAPPER(NominalOrBoundGenericNominalType, AnyGenericType)
15301530

15311531
/// InverseType represents the "inverse" of a ProtocolType as a constraint.
15321532
/// An inverse represents the _absence_ of an implicit constraint to the given
1533-
/// protocol. It is not a real type.
1533+
/// protocol.
1534+
///
1535+
/// Otherwise, an inverse is not a real type! It's an annotation for other types
1536+
/// to signal whether an implicit requirement on that type should be omitted.
1537+
/// Because that annotation is expressed in the surface language as if it _were_
1538+
/// a type (that is, as a type constraint) we still model it as a Type through
1539+
/// typechecking.
15341540
class InverseType final : public TypeBase {
15351541
Type protocol;
15361542

1537-
// The Error type is always canonical.
15381543
InverseType(Type type,
15391544
const ASTContext *canonicalContext,
15401545
RecursiveTypeProperties properties)
@@ -1548,10 +1553,14 @@ class InverseType final : public TypeBase {
15481553
static Type get(Type protocolType);
15491554

15501555

1556+
/// Obtain the underlying \c ProtocolType that was inverted.
15511557
Type getInvertedProtocol() const {
15521558
return protocol;
15531559
}
15541560

1561+
/// Get known kind of inverse this type represents.
1562+
InvertibleProtocolKind getInverseKind() const;
1563+
15551564
// Implement isa/cast/dyncast/etc.
15561565
static bool classof(const TypeBase *T) {
15571566
return T->getKind() == TypeKind::Inverse;
@@ -5766,13 +5775,37 @@ class ProtocolCompositionType final : public TypeBase,
57665775
public llvm::FoldingSetNode,
57675776
private llvm::TrailingObjects<ProtocolCompositionType, Type> {
57685777
friend TrailingObjects;
5778+
5779+
// TODO: this could probably be folded into the existing Bits field
5780+
// or we could just store the InverseType's in the Members array.
5781+
InvertibleProtocolSet Inverses;
57695782

57705783
public:
57715784
/// Retrieve an instance of a protocol composition type with the
5772-
/// given set of members.
5785+
/// given set of members. A "hidden member" is an implicit constraint that
5786+
/// is present for all protocol compositions.
5787+
///
5788+
/// \param Members the regular members of this composition.
5789+
/// \param Inverses the set of inverses that are a member of the composition,
5790+
/// i.e., if \c IP is in this set, then \c ~IP is a member of
5791+
/// this composition.
5792+
/// \param HasExplicitAnyObject indicates whether this composition should be
5793+
/// treated as if \c AnyObject was a member.
5794+
static Type get(const ASTContext &C, ArrayRef<Type> Members,
5795+
InvertibleProtocolSet Inverses,
5796+
bool HasExplicitAnyObject);
5797+
5798+
/// Retrieve an instance of a protocol composition type with the
5799+
/// given set of members. Assumes no inverses are present in \c Members.
57735800
static Type get(const ASTContext &C, ArrayRef<Type> Members,
57745801
bool HasExplicitAnyObject);
57755802

5803+
/// Constructs a protocol composition corresponding to the `Any` type.
5804+
static Type theAnyType(const ASTContext &C);
5805+
5806+
/// Constructs a protocol composition corresponding to the `AnyObject` type.
5807+
static Type theAnyObjectType(const ASTContext &C);
5808+
57765809
/// Canonical protocol composition types are minimized only to a certain
57775810
/// degree to preserve ABI compatibility. This routine enables performing
57785811
/// slower, but stricter minimization at need (e.g. redeclaration checking).
@@ -5795,11 +5828,14 @@ class ProtocolCompositionType final : public TypeBase,
57955828
return {getTrailingObjects<Type>(), Bits.ProtocolCompositionType.Count};
57965829
}
57975830

5831+
InvertibleProtocolSet getInverses() const { return Inverses; }
5832+
57985833
void Profile(llvm::FoldingSetNodeID &ID) {
5799-
Profile(ID, getMembers(), hasExplicitAnyObject());
5834+
Profile(ID, getMembers(), getInverses(), hasExplicitAnyObject());
58005835
}
58015836
static void Profile(llvm::FoldingSetNodeID &ID,
58025837
ArrayRef<Type> Members,
5838+
InvertibleProtocolSet Inverses,
58035839
bool HasExplicitAnyObject);
58045840

58055841
/// True if the composition requires the concrete conforming type to
@@ -5820,12 +5856,15 @@ class ProtocolCompositionType final : public TypeBase,
58205856
private:
58215857
static ProtocolCompositionType *build(const ASTContext &C,
58225858
ArrayRef<Type> Members,
5859+
InvertibleProtocolSet Inverses,
58235860
bool HasExplicitAnyObject);
58245861

58255862
ProtocolCompositionType(const ASTContext *ctx, ArrayRef<Type> members,
5863+
InvertibleProtocolSet inverses,
58265864
bool hasExplicitAnyObject,
58275865
RecursiveTypeProperties properties)
5828-
: TypeBase(TypeKind::ProtocolComposition, /*Context=*/ctx, properties) {
5866+
: TypeBase(TypeKind::ProtocolComposition, /*Context=*/ctx, properties),
5867+
Inverses(inverses) {
58295868
Bits.ProtocolCompositionType.HasExplicitAnyObject = hasExplicitAnyObject;
58305869
Bits.ProtocolCompositionType.Count = members.size();
58315870
std::uninitialized_copy(members.begin(), members.end(),
@@ -7267,6 +7306,7 @@ inline bool CanType::isExistentialTypeImpl(CanType type) {
72677306
isa<ProtocolCompositionType>(type) ||
72687307
isa<ExistentialType>(type) ||
72697308
isa<ParameterizedProtocolType>(type);
7309+
// TODO(kavon): treat InverseType as an existential, etc?
72707310
}
72717311

72727312
inline bool CanType::isAnyExistentialTypeImpl(CanType type) {

include/swift/Basic/FixedBitSet.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,21 @@ class FixedBitSet {
8484

8585
public:
8686
/// Build an empty set.
87-
FixedBitSet() {}
87+
constexpr FixedBitSet() {}
8888

8989
/// Build a set containing the given elements.
9090
FixedBitSet(std::initializer_list<ValueType> elements) {
9191
for (const auto &elt : elements)
9292
insert(elt);
9393
}
9494

95+
/// Build a set filled with all possible elements, i.e., a "full" set.
96+
static FixedBitSet full() {
97+
FixedBitSet set;
98+
set.insertAll();
99+
return set;
100+
}
101+
95102
/// Return true if the set is empty.
96103
bool empty() const {
97104
for (auto chunk : chunks)

lib/AST/ASTContext.cpp

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

105-
KnownProtocolSet swift::getInvertibleProtocols() {
106-
return { KnownProtocolKind::Copyable };
105+
/// Maps a KnownProtocol to the set of InvertibleProtocols, if a mapping exists.
106+
llvm::Optional<InvertibleProtocolKind>
107+
swift::getInvertibleProtocolKind(KnownProtocolKind kp) {
108+
switch (kp) {
109+
#define INVERTIBLE_PROTOCOL_WITH_NAME(Id, Name) \
110+
case KnownProtocolKind::Id: return InvertibleProtocolKind::Id;
111+
#include "swift/AST/KnownProtocols.def"
112+
default: return llvm::None;
113+
}
114+
}
115+
116+
/// Returns the KnownProtocolKind corresponding to an InvertibleProtocolKind.
117+
KnownProtocolKind swift::getKnownProtocolKind(InvertibleProtocolKind ip) {
118+
switch (ip) {
119+
#define INVERTIBLE_PROTOCOL_WITH_NAME(Id, Name) \
120+
case InvertibleProtocolKind::Id: return KnownProtocolKind::Id;
121+
#include "swift/AST/KnownProtocols.def"
122+
}
107123
}
108124

109125
namespace {
@@ -444,6 +460,7 @@ struct ASTContext::Implementation {
444460
llvm::DenseMap<std::pair<ClassDecl*, Type>, ClassType*> ClassTypes;
445461
llvm::DenseMap<std::pair<ProtocolDecl*, Type>, ProtocolType*> ProtocolTypes;
446462
llvm::DenseMap<Type, ExistentialType *> ExistentialTypes;
463+
llvm::DenseMap<Type, InverseType *> InverseTypes;
447464
llvm::FoldingSet<UnboundGenericType> UnboundGenericTypes;
448465
llvm::FoldingSet<BoundGenericType> BoundGenericTypes;
449466
llvm::FoldingSet<ProtocolCompositionType> ProtocolCompositionTypes;
@@ -669,8 +686,7 @@ ASTContext::ASTContext(
669686
UnresolvedType(*this)),
670687
TheEmptyTupleType(TupleType::get(ArrayRef<TupleTypeElt>(), *this)),
671688
TheEmptyPackType(PackType::get(*this, {})),
672-
TheAnyType(ProtocolCompositionType::get(*this, ArrayRef<Type>(),
673-
/*HasExplicitAnyObject=*/false)),
689+
TheAnyType(ProtocolCompositionType::theAnyType(*this)),
674690
#define SINGLETON_TYPE(SHORT_ID, ID) \
675691
The##SHORT_ID##Type(new (*this, AllocationArena::Permanent) \
676692
ID##Type(*this)),
@@ -1056,8 +1072,7 @@ CanType ASTContext::getAnyObjectConstraint() const {
10561072
}
10571073

10581074
getImpl().AnyObjectType = CanType(
1059-
ProtocolCompositionType::get(
1060-
*this, {}, /*HasExplicitAnyObject=*/true));
1075+
ProtocolCompositionType::theAnyObjectType(*this));
10611076
return getImpl().AnyObjectType;
10621077
}
10631078

@@ -3632,13 +3647,14 @@ ClassType *ClassType::get(ClassDecl *D, Type Parent, const ASTContext &C) {
36323647

36333648
ProtocolCompositionType *
36343649
ProtocolCompositionType::build(const ASTContext &C, ArrayRef<Type> Members,
3650+
InvertibleProtocolSet Inverses,
36353651
bool HasExplicitAnyObject) {
3636-
assert(Members.size() != 1 || HasExplicitAnyObject);
3652+
assert(Members.size() != 1 || HasExplicitAnyObject || !Inverses.empty());
36373653

36383654
// Check to see if we've already seen this protocol composition before.
36393655
void *InsertPos = nullptr;
36403656
llvm::FoldingSetNodeID ID;
3641-
ProtocolCompositionType::Profile(ID, Members, HasExplicitAnyObject);
3657+
ProtocolCompositionType::Profile(ID, Members, Inverses, HasExplicitAnyObject);
36423658

36433659
bool isCanonical = true;
36443660
RecursiveTypeProperties properties;
@@ -3661,6 +3677,7 @@ ProtocolCompositionType::build(const ASTContext &C, ArrayRef<Type> Members,
36613677
auto mem = C.Allocate(size, alignof(ProtocolCompositionType), arena);
36623678
auto compTy = new (mem) ProtocolCompositionType(isCanonical ? &C : nullptr,
36633679
Members,
3680+
Inverses,
36643681
HasExplicitAnyObject,
36653682
properties);
36663683
C.getImpl().getArena(arena).ProtocolCompositionTypes.InsertNode(
@@ -3821,28 +3838,26 @@ Type ExistentialMetatypeType::getExistentialInstanceType() {
38213838
return ExistentialType::get(getInstanceType());
38223839
}
38233840

3824-
Type InverseType::get(Type protocol) {
3825-
auto &C = protocol->getASTContext();
3826-
llvm_unreachable("TODO");
3827-
// // ExistentialMetatypeType is already an existential type.
3828-
// if (constraint->is<ExistentialMetatypeType>())
3829-
// return constraint;
3830-
//
3831-
// bool printWithAny = true;
3832-
// if (constraint->isEqual(C.TheAnyType) || constraint->isAnyObject())
3833-
// printWithAny = false;
3834-
//
3835-
// auto properties = constraint->getRecursiveProperties();
3836-
// auto arena = getArena(properties);
3837-
//
3838-
// auto &entry = C.getImpl().getArena(arena).ExistentialTypes[constraint];
3839-
// if (entry)
3840-
// return entry;
3841-
//
3842-
// const ASTContext *canonicalContext = constraint->isCanonical() ? &C : nullptr;
3843-
// return entry = new (C, arena) ExistentialType(constraint, printWithAny,
3844-
// canonicalContext,
3845-
// properties);
3841+
InvertibleProtocolKind InverseType::getInverseKind() const {
3842+
return *getInvertibleProtocolKind(*protocol->getKnownProtocol());
3843+
}
3844+
3845+
Type InverseType::get(Type invertedProto) {
3846+
auto &C = invertedProto->getASTContext();
3847+
3848+
auto properties = invertedProto->getRecursiveProperties();
3849+
auto arena = getArena(properties);
3850+
3851+
auto &entry = C.getImpl().getArena(arena).InverseTypes[invertedProto];
3852+
if (entry)
3853+
return entry;
3854+
3855+
const ASTContext *canonicalContext =
3856+
invertedProto->isCanonical() ? &C : nullptr;
3857+
3858+
return entry = new (C, arena) InverseType(invertedProto,
3859+
canonicalContext,
3860+
properties);
38463861
}
38473862

38483863
ModuleType *ModuleType::get(ModuleDecl *M) {

lib/AST/Builtins.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ Type swift::getBuiltinType(ASTContext &Context, StringRef Name) {
132132
// AnyObject is the empty class-constrained existential.
133133
if (Name == "AnyObject")
134134
return CanType(
135-
ProtocolCompositionType::get(Context, {},
136-
/*HasExplicitAnyObject=*/true));
135+
ProtocolCompositionType::theAnyObjectType(Context));
137136

138137
return Type();
139138
}

lib/AST/ExistentialGeneralization.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ class Generalizer : public CanTypeVisitor<Generalizer, Type> {
111111
}
112112

113113
Type visitInverseType(CanInverseType type) {
114-
llvm_unreachable("todo: determine how to handle inverses for Generalizer");
114+
// TODO(kavon): determine how to handle inverses for ExistentialGeneralization
115+
llvm_unreachable("unimplemented");
115116
}
116117

117118
// Generalize the type arguments of nominal types.

0 commit comments

Comments
 (0)