Skip to content

Commit 27b211c

Browse files
committed
Lazy-load the eraser of @_typeEraser where possible
Type erasure requires a circular construction by its very nature: @_typeEraser(AnyProto) protocol Proto { /**/ } public struct AnyProto : Proto {} If we eagerly resolve AnyProto, the chain of resolution steps that deserialization must make goes a little something like this: Lookup(Proto) -> Deserialize(@_typeEraser(AnyProto)) -> Lookup(AnyProto) -> DeserializeInheritedStuff(AnyProto) -> Lookup(Proto) This cycle could be broken if the order of incremental inputs was such that we had already cached the lookup of Proto. Resolve this cycle in any case by suspending the deserialization of the type eraser until the point it's demanded by adding ResolveTypeEraserTypeRequest. rdar://61270195
1 parent 172d57f commit 27b211c

16 files changed

+192
-30
lines changed

include/swift/AST/Attr.h

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,16 +1111,58 @@ class DynamicReplacementAttr final
11111111
/// The \c @_typeEraser(TypeEraserType) attribute.
11121112
class TypeEraserAttr final : public DeclAttribute {
11131113
TypeLoc TypeEraserLoc;
1114-
public:
1115-
TypeEraserAttr(SourceLoc atLoc, SourceRange range, TypeLoc typeEraserLoc)
1114+
LazyMemberLoader *Resolver;
1115+
uint64_t ResolverContextData;
1116+
1117+
friend class ResolveTypeEraserTypeRequest;
1118+
1119+
TypeEraserAttr(SourceLoc atLoc, SourceRange range, TypeLoc typeEraserLoc,
1120+
LazyMemberLoader *Resolver, uint64_t Data)
11161121
: DeclAttribute(DAK_TypeEraser, atLoc, range, /*Implicit=*/false),
1117-
TypeEraserLoc(typeEraserLoc) {}
1122+
TypeEraserLoc(typeEraserLoc),
1123+
Resolver(Resolver), ResolverContextData(Data) {}
11181124

1119-
const TypeLoc &getTypeEraserLoc() const { return TypeEraserLoc; }
1120-
TypeLoc &getTypeEraserLoc() { return TypeEraserLoc; }
1125+
public:
1126+
static TypeEraserAttr *create(ASTContext &ctx,
1127+
SourceLoc atLoc, SourceRange range,
1128+
TypeLoc typeEraserLoc);
1129+
1130+
static TypeEraserAttr *create(ASTContext &ctx,
1131+
LazyMemberLoader *Resolver,
1132+
uint64_t Data);
1133+
1134+
/// Retrieve the parsed type repr for this attribute, if it
1135+
/// was parsed. Else returns \c nullptr.
1136+
TypeRepr *getParsedTypeEraserTypeRepr() const {
1137+
return TypeEraserLoc.getTypeRepr();
1138+
}
11211139

1140+
/// Retrieve the parsed location for this attribute, if it was parsed.
1141+
SourceLoc getLoc() const {
1142+
return TypeEraserLoc.getLoc();
1143+
}
1144+
1145+
/// Retrieve the resolved type of this attribute if it has been resolved by a
1146+
/// successful call to \c getResolvedType(). Otherwise,
1147+
/// returns \c Type()
1148+
///
1149+
/// This entrypoint is only suitable for syntactic clients like the
1150+
/// AST printer. Semantic clients should use \c getResolvedType() instead.
1151+
Type getTypeWithoutResolving() const {
1152+
return TypeEraserLoc.getType();
1153+
}
1154+
1155+
/// Returns \c true if the type eraser type has a valid implementation of the
1156+
/// erasing initializer for the given protocol.
11221157
bool hasViableTypeEraserInit(ProtocolDecl *protocol) const;
11231158

1159+
/// Resolves the type of this attribute.
1160+
///
1161+
/// This entrypoint is suitable for semantic clients like the
1162+
/// expression checker. Syntactic clients should use
1163+
/// \c getTypeWithoutResolving() instead.
1164+
Type getResolvedType(const ProtocolDecl *PD) const;
1165+
11241166
static bool classof(const DeclAttribute *DA) {
11251167
return DA->getKind() == DAK_TypeEraser;
11261168
}

include/swift/AST/LazyResolver.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ class alignas(void*) LazyMemberLoader {
105105
virtual ValueDecl *
106106
loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA,
107107
uint64_t contextData) = 0;
108+
109+
/// Returns the type for a given @_typeEraser() attribute.
110+
virtual Type loadTypeEraserType(const TypeEraserAttr *TRA,
111+
uint64_t contextData) = 0;
108112
};
109113

110114
/// A class that can lazily load conformances from a serialized format.

include/swift/AST/TypeCheckRequests.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,6 +2260,27 @@ class CheckRedeclarationRequest
22602260
evaluator::SideEffect) const;
22612261
};
22622262

2263+
class ResolveTypeEraserTypeRequest
2264+
: public SimpleRequest<ResolveTypeEraserTypeRequest,
2265+
Type (ProtocolDecl *, TypeEraserAttr *),
2266+
RequestFlags::SeparatelyCached> {
2267+
public:
2268+
using SimpleRequest::SimpleRequest;
2269+
2270+
private:
2271+
friend SimpleRequest;
2272+
2273+
// Evaluation.
2274+
Type evaluate(Evaluator &evaluator, ProtocolDecl *PD,
2275+
TypeEraserAttr *attr) const;
2276+
2277+
public:
2278+
// Separate caching.
2279+
bool isCached() const { return true; }
2280+
Optional<Type> getCachedResult() const;
2281+
void cacheResult(Type value) const;
2282+
};
2283+
22632284
// Allow AnyValue to compare two Type values, even though Type doesn't
22642285
// support ==.
22652286
template<>

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ SWIFT_REQUEST(TypeChecker, PreCheckFunctionBuilderRequest,
213213
SWIFT_REQUEST(TypeChecker, ResolveImplicitMemberRequest,
214214
evaluator::SideEffect(NominalTypeDecl *, ImplicitMemberAction),
215215
Uncached, NoLocationInfo)
216+
SWIFT_REQUEST(TypeChecker, ResolveTypeEraserTypeRequest,
217+
Type(ProtocolDecl *, TypeEraserAttr *),
218+
SeparatelyCached, NoLocationInfo)
216219
SWIFT_REQUEST(TypeChecker, SPIGroupsRequest,
217220
llvm::ArrayRef<Identifier>(Decl *),
218221
Cached, NoLocationInfo)

lib/AST/Attr.cpp

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,11 +1006,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
10061006
Printer.printAttrName("@_typeEraser");
10071007
Printer << "(";
10081008
Printer.callPrintNamePre(PrintNameContext::Attribute);
1009-
auto typeLoc = cast<TypeEraserAttr>(this)->getTypeEraserLoc();
1010-
if (auto type = typeLoc.getType())
1011-
type->print(Printer, Options);
1009+
auto *attr = cast<TypeEraserAttr>(this);
1010+
if (auto *repr = attr->getParsedTypeEraserTypeRepr())
1011+
repr->print(Printer, Options);
10121012
else
1013-
typeLoc.getTypeRepr()->print(Printer, Options);
1013+
attr->getTypeWithoutResolving()->print(Printer, Options);
10141014
Printer.printNamePost(PrintNameContext::Attribute);
10151015
Printer << ")";
10161016
break;
@@ -1381,6 +1381,19 @@ SourceLoc DynamicReplacementAttr::getRParenLoc() const {
13811381
return getTrailingLocations()[1];
13821382
}
13831383

1384+
TypeEraserAttr *TypeEraserAttr::create(ASTContext &ctx,
1385+
SourceLoc atLoc, SourceRange range,
1386+
TypeLoc typeEraserLoc) {
1387+
return new (ctx) TypeEraserAttr(atLoc, range, typeEraserLoc, nullptr, 0);
1388+
}
1389+
1390+
TypeEraserAttr *TypeEraserAttr::create(ASTContext &ctx,
1391+
LazyMemberLoader *Resolver,
1392+
uint64_t Data) {
1393+
return new (ctx) TypeEraserAttr(SourceLoc(), SourceRange(),
1394+
TypeLoc(), Resolver, Data);
1395+
}
1396+
13841397
bool
13851398
TypeEraserAttr::hasViableTypeEraserInit(ProtocolDecl *protocol) const {
13861399
return evaluateOrDefault(protocol->getASTContext().evaluator,
@@ -1389,6 +1402,15 @@ TypeEraserAttr::hasViableTypeEraserInit(ProtocolDecl *protocol) const {
13891402
false);
13901403
}
13911404

1405+
Type TypeEraserAttr::getResolvedType(const ProtocolDecl *PD) const {
1406+
auto &ctx = PD->getASTContext();
1407+
return evaluateOrDefault(ctx.evaluator,
1408+
ResolveTypeEraserTypeRequest{
1409+
const_cast<ProtocolDecl *>(PD),
1410+
const_cast<TypeEraserAttr *>(this)},
1411+
ErrorType::get(ctx));
1412+
}
1413+
13921414
AvailableAttr *
13931415
AvailableAttr::createPlatformAgnostic(ASTContext &C,
13941416
StringRef Message,

lib/AST/DeclContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,11 +1080,11 @@ bool DeclContext::isClassConstrainedProtocolExtension() const {
10801080

10811081
SourceLoc swift::extractNearestSourceLoc(const DeclContext *dc) {
10821082
switch (dc->getContextKind()) {
1083+
case DeclContextKind::Module:
10831084
case DeclContextKind::AbstractFunctionDecl:
10841085
case DeclContextKind::EnumElementDecl:
10851086
case DeclContextKind::ExtensionDecl:
10861087
case DeclContextKind::GenericTypeDecl:
1087-
case DeclContextKind::Module:
10881088
case DeclContextKind::SubscriptDecl:
10891089
case DeclContextKind::TopLevelCodeDecl:
10901090
return extractNearestSourceLoc(dc->getAsDecl());

lib/AST/TypeCheckRequests.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,23 @@ void LookupAllConformancesInContextRequest::writeDependencySink(
13711371
}
13721372
}
13731373

1374+
//----------------------------------------------------------------------------//
1375+
// ResolveTypeEraserTypeRequest computation.
1376+
//----------------------------------------------------------------------------//
1377+
1378+
Optional<Type> ResolveTypeEraserTypeRequest::getCachedResult() const {
1379+
auto ty = std::get<1>(getStorage())->TypeEraserLoc.getType();
1380+
if (ty.isNull()) {
1381+
return None;
1382+
}
1383+
return ty;
1384+
}
1385+
1386+
void ResolveTypeEraserTypeRequest::cacheResult(Type value) const {
1387+
assert(value && "Resolved type erasure type to null type!");
1388+
std::get<1>(getStorage())->TypeEraserLoc.setType(value);
1389+
}
1390+
13741391
//----------------------------------------------------------------------------//
13751392
// TypeCheckSourceFileRequest computation.
13761393
//----------------------------------------------------------------------------//

lib/ClangImporter/ImporterImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
12901290
llvm_unreachable("unimplemented for ClangImporter");
12911291
}
12921292

1293+
Type loadTypeEraserType(const TypeEraserAttr *TRA,
1294+
uint64_t contextData) override {
1295+
llvm_unreachable("unimplemented for ClangImporter");
1296+
}
1297+
12931298
void loadRequirementSignature(const ProtocolDecl *decl, uint64_t contextData,
12941299
SmallVectorImpl<Requirement> &reqs) override {
12951300
llvm_unreachable("unimplemented for ClangImporter");

lib/Parse/ParseDecl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,8 +2209,7 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
22092209
if (invalid)
22102210
return false;
22112211

2212-
Attributes.add(new (Context)
2213-
TypeEraserAttr(AtLoc, {Loc, RParenLoc}, ErasedType.get()));
2212+
Attributes.add(TypeEraserAttr::create(Context, AtLoc, {Loc, RParenLoc}, ErasedType.get()));
22142213
break;
22152214
}
22162215

lib/Sema/ConstraintSystem.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4103,11 +4103,13 @@ Expr *ConstraintSystem::buildTypeErasedExpr(Expr *expr, DeclContext *dc,
41034103
if (protocols.size() != 1)
41044104
return expr;
41054105

4106-
auto *attr = protocols.front()->getAttrs().getAttribute<TypeEraserAttr>();
4106+
auto *PD = protocols.front();
4107+
auto *attr = PD->getAttrs().getAttribute<TypeEraserAttr>();
41074108
if (!attr)
41084109
return expr;
41094110

4110-
auto typeEraser = attr->getTypeEraserLoc().getType();
4111+
auto typeEraser = attr->getResolvedType(PD);
4112+
assert(typeEraser && "Failed to resolve eraser type!");
41114113
auto &ctx = dc->getASTContext();
41124114
return CallExpr::createImplicit(ctx,
41134115
TypeExpr::createImplicit(typeEraser, ctx),

0 commit comments

Comments
 (0)