Skip to content

Commit ffc62bb

Browse files
committed
Store the conforming type within an abstract ProtocolConformanceRef
An "abstract" ProtocolConformanceRef is a conformance of a type parameter or archetype to a given protocol. Previously, we would only store the protocol requirement itself---but not track the actual conforming type, requiring clients of ProtocolConformanceRef to keep track of this information separately. Record the conforming type as part of an abstract ProtocolConformanceRef, so that clients will be able to recover it later. This is handled by a uniqued AbstractConformance structure, so that ProtocolConformanceRef itself stays one pointer. There remain a small number of places where we create an abstract ProtocolConformanceRef with a null type. We'll want to chip away at those and establish some stronger invariants on the abstract conformance in the future.
1 parent 9354fd9 commit ffc62bb

24 files changed

+325
-70
lines changed

include/swift/AST/ProtocolConformanceRef.h

+54-13
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,24 @@
2626
#include "llvm/ADT/STLExtras.h"
2727
#include <optional>
2828

29+
namespace swift {
30+
class AbstractConformance;
31+
}
32+
2933
namespace llvm {
30-
class raw_ostream;
34+
class raw_ostream;
35+
36+
template <>
37+
struct PointerLikeTypeTraits<swift::AbstractConformance *> {
38+
public:
39+
static inline void *getAsVoidPointer(swift::AbstractConformance *ptr) {
40+
return ptr;
41+
}
42+
static inline swift::AbstractConformance *getFromVoidPointer(void *ptr) {
43+
return (swift::AbstractConformance *)ptr;
44+
}
45+
enum { NumLowBitsAvailable = swift::TypeAlignInBits };
46+
};
3147
}
3248

3349
namespace swift {
@@ -53,22 +69,27 @@ enum class EffectKind : uint8_t;
5369
/// ProtocolConformanceRef allows the efficient recovery of the protocol
5470
/// even when the conformance is abstract.
5571
class ProtocolConformanceRef {
56-
using UnionType = llvm::PointerUnion<ProtocolDecl *,
72+
public:
73+
using UnionType = llvm::PointerUnion<AbstractConformance *,
5774
ProtocolConformance *,
5875
PackConformance *>;
76+
77+
private:
5978
UnionType Union;
6079

6180
explicit ProtocolConformanceRef(UnionType value) : Union(value) {}
6281

82+
public:
83+
ProtocolConformanceRef() : Union() {}
84+
ProtocolConformanceRef(std::nullptr_t) : Union() {}
85+
6386
/// Create an abstract protocol conformance reference.
64-
explicit ProtocolConformanceRef(ProtocolDecl *proto) : Union(proto) {
65-
assert(proto != nullptr &&
87+
explicit ProtocolConformanceRef(AbstractConformance *abstract)
88+
: Union(abstract) {
89+
assert(abstract != nullptr &&
6690
"cannot construct ProtocolConformanceRef with null");
6791
}
6892

69-
public:
70-
ProtocolConformanceRef() : Union() {}
71-
7293
/// Create a concrete protocol conformance reference.
7394
explicit ProtocolConformanceRef(ProtocolConformance *conf) : Union(conf) {
7495
assert(conf != nullptr &&
@@ -95,7 +116,7 @@ class ProtocolConformanceRef {
95116
explicit operator bool() const { return !isInvalid(); }
96117

97118
/// Create an abstract conformance for a type parameter or archetype.
98-
static ProtocolConformanceRef forAbstract(Type subjectType,
119+
static ProtocolConformanceRef forAbstract(Type conformingType,
99120
ProtocolDecl *protocol);
100121

101122
bool isConcrete() const {
@@ -115,12 +136,11 @@ class ProtocolConformanceRef {
115136
}
116137

117138
bool isAbstract() const {
118-
return !isInvalid() && Union.is<ProtocolDecl*>();
139+
return !isInvalid() && Union.is<AbstractConformance*>();
119140
}
120141

121-
ProtocolDecl *getAbstract() const {
122-
ASSERT(isAbstract());
123-
return Union.get<ProtocolDecl*>();
142+
AbstractConformance *getAbstract() const {
143+
return Union.get<AbstractConformance *>();
124144
}
125145

126146
/// Determine whether this conformance (or a conformance it depends on)
@@ -149,7 +169,7 @@ class ProtocolConformanceRef {
149169
/// The given `body` will be called on each isolated conformance. If it ever
150170
/// returns `true`, this function will abort the search and return `true`.
151171
bool forEachIsolatedConformance(
152-
llvm::function_ref<bool(ProtocolConformance*)> body
172+
llvm::function_ref<bool(ProtocolConformanceRef)> body
153173
) const;
154174

155175
using OpaqueValue = void*;
@@ -158,6 +178,9 @@ class ProtocolConformanceRef {
158178
return ProtocolConformanceRef(UnionType::getFromOpaqueValue(value));
159179
}
160180

181+
/// Retrieve the conforming type.
182+
Type getConformingType() const;
183+
161184
/// Return the protocol requirement.
162185
ProtocolDecl *getRequirement() const;
163186

@@ -240,4 +263,22 @@ SourceLoc extractNearestSourceLoc(const ProtocolConformanceRef conformanceRef);
240263

241264
} // end namespace swift
242265

266+
namespace llvm {
267+
class raw_ostream;
268+
269+
template <>
270+
struct PointerLikeTypeTraits<swift::ProtocolConformanceRef>
271+
: PointerLikeTypeTraits<swift::ProtocolConformanceRef::UnionType>
272+
{
273+
public:
274+
static inline void *getAsVoidPointer(swift::ProtocolConformanceRef ref) {
275+
return ref.getOpaqueValue();
276+
}
277+
static inline swift::ProtocolConformanceRef getFromVoidPointer(void *ptr) {
278+
return swift::ProtocolConformanceRef::getFromOpaqueValue(ptr);
279+
}
280+
};
281+
282+
}
283+
243284
#endif // LLVM_SWIFT_AST_PROTOCOLCONFORMANCEREF_H

include/swift/AST/Requirement.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ class Requirement {
192192
CheckRequirementResult checkRequirement(
193193
SmallVectorImpl<Requirement> &subReqs,
194194
bool allowMissing = false,
195-
SmallVectorImpl<ProtocolConformance *> *isolatedConformances = nullptr
195+
SmallVectorImpl<ProtocolConformanceRef> *isolatedConformances = nullptr
196196
) const;
197197

198198
/// Determines if this substituted requirement can ever be satisfied,

lib/AST/ASTContext.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "swift/AST/ASTContext.h"
18+
#include "AbstractConformance.h"
1819
#include "ClangTypeConverter.h"
1920
#include "ForeignRepresentationInfo.h"
2021
#include "SubstitutionMapStorage.h"
@@ -595,6 +596,9 @@ struct ASTContext::Implementation {
595596
/// The set of substitution maps (uniqued by their storage).
596597
llvm::FoldingSet<SubstitutionMap::Storage> SubstitutionMaps;
597598

599+
/// The set of abstract conformances (uniqued by their storage).
600+
llvm::FoldingSet<AbstractConformance> AbstractConformances;
601+
598602
~Arena() {
599603
for (auto &conformance : SpecializedConformances)
600604
conformance.~SpecializedProtocolConformance();
@@ -3378,6 +3382,7 @@ void ASTContext::Implementation::Arena::dump(llvm::raw_ostream &os) const {
33783382
SIZE_AND_BYTES(BuiltinConformances);
33793383
SIZE(PackConformances);
33803384
SIZE(SubstitutionMaps);
3385+
SIZE(AbstractConformances);
33813386

33823387
#undef SIZE
33833388
#undef SIZE_AND_BYTES
@@ -5723,6 +5728,35 @@ SubstitutionMap::Storage *SubstitutionMap::Storage::get(
57235728
return result;
57245729
}
57255730

5731+
ProtocolConformanceRef ProtocolConformanceRef::forAbstract(
5732+
Type conformingType, ProtocolDecl *proto) {
5733+
ASTContext &ctx = proto->getASTContext();
5734+
5735+
// Figure out which arena this should go in.
5736+
RecursiveTypeProperties properties;
5737+
if (conformingType)
5738+
properties |= conformingType->getRecursiveProperties();
5739+
auto arena = getArena(properties);
5740+
5741+
// Profile the substitution map.
5742+
llvm::FoldingSetNodeID id;
5743+
AbstractConformance::Profile(id, conformingType, proto);
5744+
5745+
// Did we already record this abstract conformance?
5746+
void *insertPos;
5747+
auto &abstractConformances =
5748+
ctx.getImpl().getArena(arena).AbstractConformances;
5749+
if (auto result = abstractConformances.FindNodeOrInsertPos(id, insertPos))
5750+
return ProtocolConformanceRef(result);
5751+
5752+
// Allocate and record this abstract conformance.
5753+
auto mem = ctx.Allocate(sizeof(AbstractConformance),
5754+
alignof(AbstractConformance), arena);
5755+
auto result = new (mem) AbstractConformance(conformingType, proto);
5756+
abstractConformances.InsertNode(result, insertPos);
5757+
return ProtocolConformanceRef(result);
5758+
}
5759+
57265760
const AvailabilityContext::Storage *AvailabilityContext::Storage::get(
57275761
const AvailabilityRange &platformRange, bool isDeprecated,
57285762
llvm::ArrayRef<DomainInfo> domainInfos, const ASTContext &ctx) {

lib/AST/ASTDumper.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -5512,7 +5512,7 @@ class PrintConformance : public PrintBase {
55125512
assert(conformance.isAbstract());
55135513

55145514
printHead("abstract_conformance", ASTNodeColor, label);
5515-
printReferencedDeclField(conformance.getAbstract(),
5515+
printReferencedDeclField(conformance.getRequirement(),
55165516
Label::always("protocol"));
55175517
printFoot();
55185518
}

lib/AST/ASTMangler.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -4533,7 +4533,7 @@ void ASTMangler::appendAnyProtocolConformance(
45334533
return;
45344534

45354535
auto path = genericSig->getConformancePath(conformingType,
4536-
conformance.getAbstract());
4536+
conformance.getRequirement());
45374537
appendDependentProtocolConformance(path, genericSig);
45384538
} else if (auto opaqueType = conformingType->getAs<OpaqueTypeArchetypeType>()) {
45394539
if (forInvertible)
@@ -4544,7 +4544,7 @@ void ASTMangler::appendAnyProtocolConformance(
45444544
ConformancePath conformancePath =
45454545
opaqueSignature->getConformancePath(
45464546
opaqueType->getInterfaceType(),
4547-
conformance.getAbstract());
4547+
conformance.getRequirement());
45484548

45494549
// Append the conformance path with the signature of the opaque type.
45504550
appendDependentProtocolConformance(conformancePath, opaqueSignature);

lib/AST/AbstractConformance.h

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===--- AbstractConformance.h - Abstract conformance storage ---*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines the AbstractConformance class, which stores an
14+
// abstract conformance that is stashed in a ProtocolConformanceRef.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
#ifndef SWIFT_AST_ABSTRACT_CONFORMANCE_H
18+
#define SWIFT_AST_ABSTRACT_CONFORMANCE_H
19+
20+
#include "swift/AST/Type.h"
21+
#include "llvm/ADT/FoldingSet.h"
22+
23+
namespace swift {
24+
class AbstractConformance final : public llvm::FoldingSetNode {
25+
Type conformingType;
26+
ProtocolDecl *requirement;
27+
28+
public:
29+
AbstractConformance(Type conformingType, ProtocolDecl *requirement)
30+
: conformingType(conformingType), requirement(requirement) { }
31+
32+
Type getConformingType() const { return conformingType; }
33+
ProtocolDecl *getRequirement() const { return requirement; }
34+
35+
void Profile(llvm::FoldingSetNodeID &id) const {
36+
Profile(id, getConformingType(), getRequirement());
37+
}
38+
39+
/// Profile the substitution map storage, for use with LLVM's FoldingSet.
40+
static void Profile(llvm::FoldingSetNodeID &id,
41+
Type conformingType,
42+
ProtocolDecl *requirement) {
43+
id.AddPointer(conformingType.getPointer());
44+
id.AddPointer(requirement);
45+
}
46+
};
47+
48+
}
49+
50+
#endif // SWIFT_AST_ABSTRACT_CONFORMANCE_H
51+

0 commit comments

Comments
 (0)