Skip to content

Commit b78ed53

Browse files
authored
Merge pull request #30800 from CodaFi/erase-install
[Serialization] Lazily Resolve the Eraser Type of @ _typeEraser
2 parents 5eea0ae + bce1dd6 commit b78ed53

17 files changed

+193
-42
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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,11 +1080,12 @@ bool DeclContext::isClassConstrainedProtocolExtension() const {
10801080

10811081
SourceLoc swift::extractNearestSourceLoc(const DeclContext *dc) {
10821082
switch (dc->getContextKind()) {
1083+
case DeclContextKind::Module:
1084+
return SourceLoc();
10831085
case DeclContextKind::AbstractFunctionDecl:
10841086
case DeclContextKind::EnumElementDecl:
10851087
case DeclContextKind::ExtensionDecl:
10861088
case DeclContextKind::GenericTypeDecl:
1087-
case DeclContextKind::Module:
10881089
case DeclContextKind::SubscriptDecl:
10891090
case DeclContextKind::TopLevelCodeDecl:
10901091
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),

lib/Sema/TypeCheckAttr.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,25 +2394,36 @@ void AttributeChecker::visitDynamicReplacementAttr(DynamicReplacementAttr *attr)
23942394
}
23952395
}
23962396

2397+
Type
2398+
ResolveTypeEraserTypeRequest::evaluate(Evaluator &evaluator,
2399+
ProtocolDecl *PD,
2400+
TypeEraserAttr *attr) const {
2401+
if (auto *typeEraserRepr = attr->getParsedTypeEraserTypeRepr()) {
2402+
auto resolution = TypeResolution::forContextual(PD);
2403+
return resolution.resolveType(typeEraserRepr, /*options=*/None);
2404+
} else {
2405+
auto *LazyResolver = attr->Resolver;
2406+
assert(LazyResolver && "type eraser was neither parsed nor deserialized?");
2407+
auto ty = LazyResolver->loadTypeEraserType(attr, attr->ResolverContextData);
2408+
attr->Resolver = nullptr;
2409+
if (!ty) {
2410+
return ErrorType::get(PD->getASTContext());
2411+
}
2412+
return ty;
2413+
}
2414+
}
2415+
23972416
bool
23982417
TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
23992418
TypeEraserAttr *attr,
24002419
ProtocolDecl *protocol) const {
24012420
auto &ctx = protocol->getASTContext();
24022421
auto &diags = ctx.Diags;
2403-
TypeLoc &typeEraserLoc = attr->getTypeEraserLoc();
2404-
TypeRepr *typeEraserRepr = typeEraserLoc.getTypeRepr();
24052422
DeclContext *dc = protocol->getDeclContext();
24062423
Type protocolType = protocol->getDeclaredType();
24072424

24082425
// Get the NominalTypeDecl for the type eraser.
2409-
Type typeEraser = typeEraserLoc.getType();
2410-
if (!typeEraser && typeEraserRepr) {
2411-
auto resolution = TypeResolution::forContextual(protocol);
2412-
typeEraser = resolution.resolveType(typeEraserRepr, /*options=*/None);
2413-
typeEraserLoc.setType(typeEraser);
2414-
}
2415-
2426+
Type typeEraser = attr->getResolvedType(protocol);
24162427
if (typeEraser->hasError())
24172428
return false;
24182429

@@ -2422,13 +2433,13 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
24222433
nominalTypeDecl = typeAliasDecl->getUnderlyingType()->getAnyNominal();
24232434

24242435
if (!nominalTypeDecl || isa<ProtocolDecl>(nominalTypeDecl)) {
2425-
diags.diagnose(typeEraserLoc.getLoc(), diag::non_nominal_type_eraser);
2436+
diags.diagnose(attr->getLoc(), diag::non_nominal_type_eraser);
24262437
return false;
24272438
}
24282439

24292440
// The nominal type must be accessible wherever the protocol is accessible
24302441
if (nominalTypeDecl->getFormalAccess() < protocol->getFormalAccess()) {
2431-
diags.diagnose(typeEraserLoc.getLoc(), diag::type_eraser_not_accessible,
2442+
diags.diagnose(attr->getLoc(), diag::type_eraser_not_accessible,
24322443
nominalTypeDecl->getFormalAccess(), nominalTypeDecl->getName(),
24332444
protocolType, protocol->getFormalAccess());
24342445
diags.diagnose(nominalTypeDecl->getLoc(), diag::type_eraser_declared_here);
@@ -2437,7 +2448,7 @@ TypeEraserHasViableInitRequest::evaluate(Evaluator &evaluator,
24372448

24382449
// The type eraser must conform to the annotated protocol
24392450
if (!TypeChecker::conformsToProtocol(typeEraser, protocol, dc, None)) {
2440-
diags.diagnose(typeEraserLoc.getLoc(), diag::type_eraser_does_not_conform,
2451+
diags.diagnose(attr->getLoc(), diag::type_eraser_does_not_conform,
24412452
typeEraser, protocolType);
24422453
diags.diagnose(nominalTypeDecl->getLoc(), diag::type_eraser_declared_here);
24432454
return false;

lib/Serialization/Deserialization.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4295,10 +4295,8 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() {
42954295
serialization::decls_block::TypeEraserDeclAttrLayout::readRecord(
42964296
scratch, isImplicit, typeEraserID);
42974297

4298-
auto typeEraser = MF.getType(typeEraserID);
42994298
assert(!isImplicit);
4300-
Attr = new (ctx) TypeEraserAttr(SourceLoc(), SourceRange(),
4301-
TypeLoc::withoutLoc(typeEraser));
4299+
Attr = TypeEraserAttr::create(ctx, &MF, typeEraserID);
43024300
break;
43034301
}
43044302

@@ -5943,6 +5941,11 @@ ValueDecl *ModuleFile::loadDynamicallyReplacedFunctionDecl(
59435941
return cast<ValueDecl>(getDecl(contextData));
59445942
}
59455943

5944+
Type ModuleFile::loadTypeEraserType(const TypeEraserAttr *TRA,
5945+
uint64_t contextData) {
5946+
return getType(contextData);
5947+
}
5948+
59465949
void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance,
59475950
uint64_t contextData) {
59485951
using namespace decls_block;

lib/Serialization/ModuleFile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,9 @@ class ModuleFile
882882
loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA,
883883
uint64_t contextData) override;
884884

885+
virtual Type loadTypeEraserType(const TypeEraserAttr *TRA,
886+
uint64_t contextData) override;
887+
885888
virtual void finishNormalConformance(NormalProtocolConformance *conformance,
886889
uint64_t contextData) override;
887890

lib/Serialization/Serialization.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2367,7 +2367,8 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
23672367
case DAK_TypeEraser: {
23682368
auto abbrCode = S.DeclTypeAbbrCodes[TypeEraserDeclAttrLayout::Code];
23692369
auto attr = cast<TypeEraserAttr>(DA);
2370-
auto typeEraser = attr->getTypeEraserLoc().getType();
2370+
auto typeEraser = attr->getResolvedType(cast<ProtocolDecl>(D));
2371+
assert(typeEraser && "Failed to resolve erasure type!");
23712372
TypeEraserDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode,
23722373
attr->isImplicit(),
23732374
S.addTypeRef(typeEraser));
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@_typeEraser(AnyWindows)
2+
public protocol Vista {
3+
associatedtype Software : Vista
4+
5+
var dlls: Software { get }
6+
}
7+
8+
extension Never: Vista {
9+
public var dlls: Never { fatalError() }
10+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -module-name Multi -o %t/multi-file.swiftmodule -primary-file %s %S/Inputs/multi-file-type-eraser.swift
3+
// RUN: %target-swift-frontend -emit-module -module-name Multi -o %t/multi-file-2.swiftmodule %s -primary-file %S/Inputs/multi-file-type-eraser.swift
4+
5+
// RUN: %target-swift-frontend -emit-module -module-name Multi %t/multi-file.swiftmodule %t/multi-file-2.swiftmodule -o %t -print-stats 2>&1 | %FileCheck %s
6+
// RUN: %target-swift-frontend -emit-module -module-name Multi %t/multi-file-2.swiftmodule %t/multi-file.swiftmodule -o %t -print-stats 2>&1 | %FileCheck %s
7+
// Ensure we don't crash during merge-modules - no matter the order of inputs.
8+
9+
// CHECK: Statistics
10+
// CHECK: 2 Serialization - # of normal protocol conformances completed
11+
12+
public struct AnyWindows : Vista {
13+
public init<V: Vista>(erasing vista: V) {
14+
fatalError()
15+
}
16+
17+
public var dlls: Never { fatalError() }
18+
}
19+

0 commit comments

Comments
 (0)