From c20d003aa4871387252ce0702670abb1e4eaf4c7 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 22 Nov 2019 00:53:54 -0800 Subject: [PATCH 1/6] Add loadDynamicallyReplacedFunctionDecl To support lazy resolution of the cross-referenced function in a serialized @_dynamicReplacement(for: ...) attribute, add a utility to the LazyMemberLoader and plumb it through. This is a more general utility than the current resolver, which relies on the type checker to strip the attribute off of VarDecls and fan it back out onto accessors, which means serialization has only ever seen AbstractFunctionDecls. --- include/swift/AST/LazyResolver.h | 5 +++++ lib/ClangImporter/ImporterImpl.h | 6 ++++++ lib/Serialization/Deserialization.cpp | 5 +++++ lib/Serialization/ModuleFile.h | 4 ++++ 4 files changed, 20 insertions(+) diff --git a/include/swift/AST/LazyResolver.h b/include/swift/AST/LazyResolver.h index b6cabd44b2a04..afd90352852cf 100644 --- a/include/swift/AST/LazyResolver.h +++ b/include/swift/AST/LazyResolver.h @@ -103,6 +103,11 @@ class alignas(void*) LazyMemberLoader { virtual void loadRequirementSignature(const ProtocolDecl *proto, uint64_t contextData, SmallVectorImpl &requirements) = 0; + + /// Returns the replaced decl for a given @_dynamicReplacement(for:) attribute. + virtual ValueDecl * + loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, + uint64_t contextData) = 0; }; /// A class that can lazily load conformances from a serialized format. diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 7dba7c806ea3e..86a51564f738d 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1230,6 +1230,12 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation llvm_unreachable("unimplemented for ClangImporter"); } + ValueDecl * + loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, + uint64_t contextData) override { + llvm_unreachable("unimplemented for ClangImporter"); + } + void loadRequirementSignature(const ProtocolDecl *decl, uint64_t contextData, SmallVectorImpl &reqs) override { llvm_unreachable("unimplemented for ClangImporter"); diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 56e23d273fc8f..969f6adcaae24 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -5415,6 +5415,11 @@ ModuleFile::loadAssociatedTypeDefault(const swift::AssociatedTypeDecl *ATD, return getType(contextData); } +ValueDecl *ModuleFile::loadDynamicallyReplacedFunctionDecl( + const DynamicReplacementAttr *DRA, uint64_t contextData) { + return cast(getDecl(contextData)); +} + void ModuleFile::finishNormalConformance(NormalProtocolConformance *conformance, uint64_t contextData) { using namespace decls_block; diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index a18febcfdab1b..839fb3c30265a 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -831,6 +831,10 @@ class ModuleFile virtual Type loadAssociatedTypeDefault(const AssociatedTypeDecl *ATD, uint64_t contextData) override; + virtual ValueDecl * + loadDynamicallyReplacedFunctionDecl(const DynamicReplacementAttr *DRA, + uint64_t contextData) override; + virtual void finishNormalConformance(NormalProtocolConformance *conformance, uint64_t contextData) override; From 231e97bc5dadcf5ba31ee4907d222f396d12084f Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 22 Nov 2019 00:54:24 -0800 Subject: [PATCH 2/6] Refactor DynamicReplacementAttr::create Remove a dead overload and add an overload for lazy member loading --- include/swift/AST/Attr.h | 35 +++++++++++++++++++++-------------- lib/AST/Attr.cpp | 21 ++++++++++----------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 776b22a25b259..de46bdf627b8a 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -52,6 +52,7 @@ class FuncDecl; class ClassDecl; class GenericFunctionType; class LazyConformanceLoader; +class LazyMemberLoader; class PatternBindingInitializer; class TrailingWhereClause; @@ -1010,18 +1011,31 @@ class DynamicReplacementAttr final : public DeclAttribute, private llvm::TrailingObjects { friend TrailingObjects; + friend class DynamicallyReplacedDeclRequest; DeclName ReplacedFunctionName; - AbstractFunctionDecl *ReplacedFunction; + LazyMemberLoader *Resolver = nullptr; + uint64_t ResolverContextData; /// Create an @_dynamicReplacement(for:) attribute written in the source. DynamicReplacementAttr(SourceLoc atLoc, SourceRange baseRange, DeclName replacedFunctionName, SourceRange parenRange); - explicit DynamicReplacementAttr(DeclName name) + DynamicReplacementAttr(DeclName name, AbstractFunctionDecl *f) : DeclAttribute(DAK_DynamicReplacement, SourceLoc(), SourceRange(), /*Implicit=*/false), - ReplacedFunctionName(name), ReplacedFunction(nullptr) { + ReplacedFunctionName(name), + Resolver(nullptr), ResolverContextData(0) { + Bits.DynamicReplacementAttr.HasTrailingLocationInfo = false; + } + + DynamicReplacementAttr(DeclName name, + LazyMemberLoader *Resolver = nullptr, + uint64_t Data = 0) + : DeclAttribute(DAK_DynamicReplacement, SourceLoc(), SourceRange(), + /*Implicit=*/false), + ReplacedFunctionName(name), + Resolver(Resolver), ResolverContextData(Data) { Bits.DynamicReplacementAttr.HasTrailingLocationInfo = false; } @@ -1045,25 +1059,18 @@ class DynamicReplacementAttr final SourceLoc LParenLoc, DeclName replacedFunction, SourceLoc RParenLoc); static DynamicReplacementAttr *create(ASTContext &ctx, - DeclName replacedFunction); + DeclName replacedFunction, + AbstractFunctionDecl *replacedFuncDecl); static DynamicReplacementAttr *create(ASTContext &ctx, DeclName replacedFunction, - AbstractFunctionDecl *replacedFuncDecl); + LazyMemberLoader *Resolver, + uint64_t Data); DeclName getReplacedFunctionName() const { return ReplacedFunctionName; } - AbstractFunctionDecl *getReplacedFunction() const { - return ReplacedFunction; - } - - void setReplacedFunction(AbstractFunctionDecl *f) { - assert(ReplacedFunction == nullptr); - ReplacedFunction = f; - } - /// Retrieve the location of the opening parentheses, if there is one. SourceLoc getLParenLoc() const; diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 3fdb973a67805..c7217aaa929c1 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -22,14 +22,14 @@ #include "swift/AST/GenericEnvironment.h" #include "swift/AST/IndexSubset.h" #include "swift/AST/Module.h" +#include "swift/AST/ParameterList.h" #include "swift/AST/TypeRepr.h" #include "swift/AST/Types.h" -#include "swift/AST/ParameterList.h" #include "swift/Basic/Defer.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/ErrorHandling.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" using namespace swift; #define DECL_ATTR(_, Id, ...) \ @@ -1135,7 +1135,7 @@ DynamicReplacementAttr::DynamicReplacementAttr(SourceLoc atLoc, SourceRange parenRange) : DeclAttribute(DAK_DynamicReplacement, atLoc, baseRange, /*Implicit=*/false), - ReplacedFunctionName(name), ReplacedFunction(nullptr) { + ReplacedFunctionName(name) { Bits.DynamicReplacementAttr.HasTrailingLocationInfo = true; getTrailingLocations()[0] = parenRange.Start; getTrailingLocations()[1] = parenRange.End; @@ -1152,17 +1152,16 @@ DynamicReplacementAttr::create(ASTContext &Ctx, SourceLoc AtLoc, SourceRange(LParenLoc, RParenLoc)); } -DynamicReplacementAttr *DynamicReplacementAttr::create(ASTContext &Ctx, - DeclName name) { - return new (Ctx) DynamicReplacementAttr(name); +DynamicReplacementAttr * +DynamicReplacementAttr::create(ASTContext &Ctx, DeclName name, + AbstractFunctionDecl *f) { + return new (Ctx) DynamicReplacementAttr(name, f); } DynamicReplacementAttr * DynamicReplacementAttr::create(ASTContext &Ctx, DeclName name, - AbstractFunctionDecl *f) { - auto res = new (Ctx) DynamicReplacementAttr(name); - res->setReplacedFunction(f); - return res; + LazyMemberLoader *Resolver, uint64_t Data) { + return new (Ctx) DynamicReplacementAttr(name, Resolver, Data); } SourceLoc DynamicReplacementAttr::getLParenLoc() const { From dc513364fae752ba20e42e237003c805b7904e1c Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 22 Nov 2019 00:56:38 -0800 Subject: [PATCH 3/6] Add a request for replaced decls Add DynamicallyReplacedDeclRequest to ValueDecl and plumb the request through to TypeCheckAttr where it replaces TypeChecker::findReplacedDynamicFunction. --- include/swift/AST/Decl.h | 6 + include/swift/AST/TypeCheckRequests.h | 19 ++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 + lib/AST/Decl.cpp | 7 + lib/Sema/TypeCheckAttr.cpp | 191 +++++++++----------- lib/Sema/TypeCheckDeclPrimary.cpp | 3 +- lib/Sema/TypeChecker.h | 1 - 7 files changed, 123 insertions(+), 107 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c1cbba1c20596..b2d6b5feca060 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2382,6 +2382,7 @@ class ValueDecl : public Decl { unsigned isIUO : 1; } LazySemanticInfo = { }; + friend class DynamicallyReplacedDeclRequest; friend class OverriddenDeclsRequest; friend class IsObjCRequest; friend class IsFinalRequest; @@ -2742,6 +2743,11 @@ class ValueDecl : public Decl { /// Retrieve the @functionBuilder type attached to this declaration, /// if there is one. Type getFunctionBuilderType() const; + + /// If this value or its backing storage is annotated + /// @_dynamicReplacement(for: ...), compute the original declaration + /// that this declaration dynamically replaces. + ValueDecl *getDynamicallyReplacedDecl() const; }; /// This is a common base class for declarations which declare a type. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index f62653436e8ea..4b0c8b937c3e0 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1886,6 +1886,25 @@ class DefaultArgumentExprRequest void cacheResult(Expr *expr) const; }; +class DynamicallyReplacedDeclRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + llvm::Expected + evaluate(Evaluator &evaluator, ValueDecl *VD) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + // Allow AnyValue to compare two Type values, even though Type doesn't // support ==. template<> diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 95ec95ce10449..5485d24cc2c07 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -41,6 +41,9 @@ SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest, SWIFT_REQUEST(TypeChecker, DefaultTypeRequest, Type(KnownProtocolKind, const DeclContext *), SeparatelyCached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DynamicallyReplacedDeclRequest, + ValueDecl *(ValueDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EmittedMembersRequest, DeclRange(ClassDecl *), SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, EnumRawValuesRequest, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 8a1dce1007355..4e2c526933410 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2817,6 +2817,13 @@ void ValueDecl::setIsDynamic(bool value) { LazySemanticInfo.isDynamic = value; } +ValueDecl *ValueDecl::getDynamicallyReplacedDecl() const { + return evaluateOrDefault(getASTContext().evaluator, + DynamicallyReplacedDeclRequest{ + const_cast(this)}, + nullptr); +} + bool ValueDecl::canBeAccessedByDynamicLookup() const { if (!hasName()) return false; diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 661880b093dcc..4678e6d36999b 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -2060,10 +2060,10 @@ void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) { } /// Lookup the replaced decl in the replacments scope. -void lookupReplacedDecl(DeclName replacedDeclName, - const DynamicReplacementAttr *attr, - const ValueDecl *replacement, - SmallVectorImpl &results) { +static void lookupReplacedDecl(DeclName replacedDeclName, + const DynamicReplacementAttr *attr, + const ValueDecl *replacement, + SmallVectorImpl &results) { auto *declCtxt = replacement->getDeclContext(); // Look at the accessors' storage's context. @@ -2220,11 +2220,12 @@ findReplacedFunction(DeclName replacedFunctionName, // Check for static/instance mismatch. if (result->isStatic() != replacement->isStatic()) continue; - + + auto resultTy = result->getInterfaceType(); + auto replaceTy = replacement->getInterfaceType(); TypeMatchOptions matchMode = TypeMatchFlags::AllowABICompatible; matchMode |= TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes; - if (result->getInterfaceType()->getCanonicalType()->matches( - replacement->getInterfaceType()->getCanonicalType(), matchMode)) { + if (resultTy->matches(replaceTy, matchMode)) { if (!result->isDynamic()) { if (Diags) { Diags->diagnose(attr->getLocation(), @@ -2274,9 +2275,11 @@ findReplacedStorageDecl(DeclName replacedFunctionName, // Check for static/instance mismatch. if (result->isStatic() != replacement->isStatic()) continue; - if (result->getInterfaceType()->getCanonicalType()->matches( - replacement->getInterfaceType()->getCanonicalType(), - TypeMatchFlags::AllowABICompatible)) { + auto resultTy = result->getInterfaceType(); + auto replaceTy = replacement->getInterfaceType(); + TypeMatchOptions matchMode = TypeMatchFlags::AllowABICompatible; + matchMode |= TypeMatchFlags::AllowCompatibleOpaqueTypeArchetypes; + if (resultTy->matches(replaceTy, matchMode)) { if (!result->isDynamic()) { return nullptr; } @@ -2286,113 +2289,48 @@ findReplacedStorageDecl(DeclName replacedFunctionName, return nullptr; } -ValueDecl *TypeChecker::findReplacedDynamicFunction(const ValueDecl *vd) { - assert(isa(vd) || isa(vd)); - if (isa(vd)) - return nullptr; - - auto *attr = vd->getAttrs().getAttribute(); - if (!attr) - return nullptr; - - auto *afd = dyn_cast(vd); - if (afd) { - // When we pass nullptr as the type checker argument attr is truely const. - return findReplacedFunction(attr->getReplacedFunctionName(), afd, - const_cast(attr), - nullptr); - } - auto *storageDecl = dyn_cast(vd); - if (!storageDecl) - return nullptr; - return findReplacedStorageDecl(attr->getReplacedFunctionName(), storageDecl, attr); -} - void AttributeChecker::visitDynamicReplacementAttr(DynamicReplacementAttr *attr) { assert(isa(D) || isa(D)); - auto *VD = cast(D); + auto *replacement = cast(D); - if (!isa(VD->getDeclContext()) && - !VD->getDeclContext()->isModuleScopeContext()) { + if (!isa(replacement->getDeclContext()) && + !replacement->getDeclContext()->isModuleScopeContext()) { diagnose(attr->getLocation(), diag::dynamic_replacement_not_in_extension, - VD->getBaseName()); + replacement->getBaseName()); attr->setInvalid(); return; } - if (VD->isNativeDynamic()) { + if (replacement->isNativeDynamic()) { diagnose(attr->getLocation(), diag::dynamic_replacement_must_not_be_dynamic, - VD->getBaseName()); + replacement->getBaseName()); attr->setInvalid(); return; } - // Don't process a declaration twice. This will happen to accessor decls after - // we have processed their var decls. - if (attr->getReplacedFunction()) + auto *original = replacement->getDynamicallyReplacedDecl(); + if (!original) { + attr->setInvalid(); return; - - SmallVector replacements; - SmallVector origs; - - // Collect the accessor replacement mapping if this is an abstract storage. - if (auto *var = dyn_cast(VD)) { - var->visitParsedAccessors([&](AccessorDecl *accessor) { - if (attr->isInvalid()) - return; - - auto *orig = findReplacedAccessor(attr->getReplacedFunctionName(), - accessor, attr, Ctx); - if (!orig) - return; - - origs.push_back(orig); - replacements.push_back(accessor); - }); - } else { - // Otherwise, find the matching function. - auto *fun = cast(VD); - if (auto *orig = findReplacedFunction(attr->getReplacedFunctionName(), fun, - attr, &Ctx.Diags)) { - origs.push_back(orig); - replacements.push_back(fun); - } else - return; } - // Annotate the replacement with the original func decl. - for (auto index : indices(replacements)) { - if (auto *attr = replacements[index] - ->getAttrs() - .getAttribute()) { - auto *replacedFun = origs[index]; - auto *replacement = replacements[index]; - if (replacedFun->isObjC() && !replacement->isObjC()) { - diagnose(attr->getLocation(), - diag::dynamic_replacement_replacement_not_objc_dynamic, - attr->getReplacedFunctionName()); - attr->setInvalid(); - return; - } - if (!replacedFun->isObjC() && replacement->isObjC()) { - diagnose(attr->getLocation(), - diag::dynamic_replacement_replaced_not_objc_dynamic, - attr->getReplacedFunctionName()); - attr->setInvalid(); - return; - } - attr->setReplacedFunction(replacedFun); - continue; - } - auto *newAttr = DynamicReplacementAttr::create( - VD->getASTContext(), attr->getReplacedFunctionName(), origs[index]); - DeclAttributes &attrs = replacements[index]->getAttrs(); - attrs.add(newAttr); + if (original->isObjC() && !replacement->isObjC()) { + diagnose(attr->getLocation(), + diag::dynamic_replacement_replacement_not_objc_dynamic, + attr->getReplacedFunctionName()); + attr->setInvalid(); + } + if (!original->isObjC() && replacement->isObjC()) { + diagnose(attr->getLocation(), + diag::dynamic_replacement_replaced_not_objc_dynamic, + attr->getReplacedFunctionName()); + attr->setInvalid(); } - if (auto *CD = dyn_cast(VD)) { + + if (auto *CD = dyn_cast(replacement)) { auto *attr = CD->getAttrs().getAttribute(); auto replacedIsConvenienceInit = - cast(attr->getReplacedFunction())->isConvenienceInit(); + cast(original)->isConvenienceInit(); if (replacedIsConvenienceInit &&!CD->isConvenienceInit()) { diagnose(attr->getLocation(), diag::dynamic_replacement_replaced_constructor_is_convenience, @@ -2404,13 +2342,6 @@ void AttributeChecker::visitDynamicReplacementAttr(DynamicReplacementAttr *attr) attr->getReplacedFunctionName()); } } - - - // Remove the attribute on the abstract storage (we have moved it to the - // accessor decl). - if (!isa(VD)) - return; - D->getAttrs().removeAttribute(attr); } void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { @@ -2882,3 +2813,53 @@ void TypeChecker::addImplicitDynamicAttribute(Decl *D) { D->getAttrs().add(attr); } } + +llvm::Expected +DynamicallyReplacedDeclRequest::evaluate(Evaluator &evaluator, + ValueDecl *VD) const { + // Dynamic replacements must be explicit. + if (VD->isImplicit()) + return nullptr; + + auto *attr = VD->getAttrs().getAttribute(); + if (!attr) { + // It's likely that the accessor isn't annotated but its storage is. + if (auto *AD = dyn_cast(VD)) { + // Try to grab the attribute from the storage. + attr = AD->getStorage()->getAttrs().getAttribute(); + } + + if (!attr) { + // Otherwise, it's not dynamically replacing anything. + return nullptr; + } + } + + // If the attribute is invalid, bail. + if (attr->isInvalid()) + return nullptr; + + // If we can lazily resolve the function, do so now. + if (auto *LazyResolver = attr->Resolver) { + auto decl = attr->Resolver->loadDynamicallyReplacedFunctionDecl( + attr, attr->ResolverContextData); + attr->Resolver = nullptr; + return decl; + } + + auto &Ctx = VD->getASTContext(); + if (auto *AD = dyn_cast(VD)) { + return findReplacedAccessor(attr->getReplacedFunctionName(), AD, attr, Ctx); + } + + if (auto *AFD = dyn_cast(VD)) { + return findReplacedFunction(attr->getReplacedFunctionName(), AFD, + attr, &Ctx.Diags); + } + + if (auto *SD = dyn_cast(VD)) { + return findReplacedStorageDecl(attr->getReplacedFunctionName(), SD, attr); + } + + return nullptr; +} diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index b82a865af0564..1ca2a7516cccf 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2054,7 +2054,8 @@ class DeclChecker : public DeclVisitor { // Force these requests in case they emit diagnostics. (void) FD->getInterfaceType(); (void) FD->getOperatorDecl(); - + (void) FD->getDynamicallyReplacedDecl(); + if (!FD->isInvalid()) { checkGenericParams(FD); TypeChecker::checkReferencedGenericParams(FD); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index ef096a3e98de5..8df184c37c6e3 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -725,7 +725,6 @@ class TypeChecker final { static void addImplicitDynamicAttribute(Decl *D); static void checkDeclAttributes(Decl *D); static void checkParameterAttributes(ParameterList *params); - static ValueDecl *findReplacedDynamicFunction(const ValueDecl *d); static Type checkReferenceOwnershipAttr(VarDecl *D, Type interfaceType, ReferenceOwnershipAttr *attr); From c2b7ea53c314a3dc9676834de549f613025ae39d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 22 Nov 2019 00:57:41 -0800 Subject: [PATCH 4/6] Rephrase hasDidSetOrWillSetDynamicReplacement Use the new dynamically replaced request to power this predicates. --- lib/AST/Decl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 4e2c526933410..d6cda6469a640 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4949,9 +4949,9 @@ bool AbstractStorageDecl::hasPrivateAccessor() const { bool AbstractStorageDecl::hasDidSetOrWillSetDynamicReplacement() const { if (auto *func = getParsedAccessor(AccessorKind::DidSet)) - return func->getAttrs().hasAttribute(); + return (bool)func->getDynamicallyReplacedDecl(); if (auto *func = getParsedAccessor(AccessorKind::WillSet)) - return func->getAttrs().hasAttribute(); + return (bool)func->getDynamicallyReplacedDecl(); return false; } From fc27288c298d7deec7b355f657692691ed178e4d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 22 Nov 2019 01:04:24 -0800 Subject: [PATCH 5/6] Replace semantic usages of DynamicReplacementAttr with the request Complete the refactoring by splitting the semantic callers for the original decl of a dynamically replaced declaration. There's also a change to the way this attribute is validated and placed. The old model visited the attribute on any functions and variable declarations it encountered in the primary. Once there, it would strip the attribute off of variables and attach the corresponding attribute to each parsed accessor, then perform some additional ObjC-related validation. The new approach instead leaves the attribute alone. The request exists specifically to perform the lookups and type matching required to find replaced decls, and the attribute visitor no longer needs to worry about revisiting decls it has just grafted attributes onto. This also means that a bunch of parts of IRGen and SILGen that needed to fan out to the accessors to ask for the @_dynamicReplacement attribute to undo the work the type checker had done can just look at the storage itself. Further, syntactic requests for the attribute will now consistently succeed, where before they would fail dependending on whether or not the type checker had run - which was generally not an issue by the time we hit SIL. --- include/swift/AST/Decl.h | 2 -- lib/AST/Decl.cpp | 7 ------- lib/IRGen/GenArchetype.cpp | 11 ++++------- lib/IRGen/GenDecl.cpp | 10 +++------- lib/SIL/SILFunctionBuilder.cpp | 10 +++++----- lib/SILGen/SILGenApply.cpp | 3 +-- lib/Sema/TypeCheckDeclObjC.cpp | 5 ++--- lib/Serialization/Deserialization.cpp | 7 ++----- lib/Serialization/Serialization.cpp | 9 +++++---- lib/TBDGen/TBDGen.cpp | 6 +++--- 10 files changed, 25 insertions(+), 45 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index b2d6b5feca060..60198bc1e3860 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4767,8 +4767,6 @@ class AbstractStorageDecl : public ValueDecl { bool hasAnyNativeDynamicAccessors() const; - bool hasAnyDynamicReplacementAccessors() const; - // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_AbstractStorageDecl && diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index d6cda6469a640..05461859dd5a1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4963,13 +4963,6 @@ bool AbstractStorageDecl::hasAnyNativeDynamicAccessors() const { return false; } -bool AbstractStorageDecl::hasAnyDynamicReplacementAccessors() const { - for (auto accessor : getAllAccessors()) { - if (accessor->getAttrs().hasAttribute()) - return true; - } - return false; -} void AbstractStorageDecl::setAccessors(SourceLoc lbraceLoc, ArrayRef accessors, SourceLoc rbraceLoc) { diff --git a/lib/IRGen/GenArchetype.cpp b/lib/IRGen/GenArchetype.cpp index a9afa0d8a5313..18a3c38d2a22c 100644 --- a/lib/IRGen/GenArchetype.cpp +++ b/lib/IRGen/GenArchetype.cpp @@ -452,21 +452,18 @@ withOpaqueTypeGenericArgs(IRGenFunction &IGF, bool shouldUseOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *opaque) { auto *namingDecl = opaque->getNamingDecl(); - auto *abstractStorage = dyn_cast(namingDecl); // Don't emit accessors for abstract storage that is not dynamic or a dynamic // replacement. - if (abstractStorage) { + if (auto *abstractStorage = dyn_cast(namingDecl)) { return abstractStorage->hasAnyNativeDynamicAccessors() || - abstractStorage->hasAnyDynamicReplacementAccessors(); + abstractStorage->getDynamicallyReplacedDecl(); } // Don't emit accessors for functions that are not dynamic or dynamic // replacements. - return opaque->getNamingDecl()->isNativeDynamic() || - opaque->getNamingDecl() - ->getAttrs() - .hasAttribute(); + return namingDecl->isNativeDynamic() || + (bool)namingDecl->getDynamicallyReplacedDecl(); } static llvm::Value * diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 2c15da60dc75b..490ffc5cb6601 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2347,15 +2347,14 @@ static void emitDynamicallyReplaceableThunk(IRGenModule &IGM, void IRGenModule::emitOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *opaque) { auto *namingDecl = opaque->getNamingDecl(); auto *abstractStorage = dyn_cast(namingDecl); - + bool isNativeDynamic = false; - bool isDynamicReplacement = false; + const bool isDynamicReplacement = namingDecl->getDynamicallyReplacedDecl(); // Don't emit accessors for abstract storage that is not dynamic or a dynamic // replacement. if (abstractStorage) { isNativeDynamic = abstractStorage->hasAnyNativeDynamicAccessors(); - isDynamicReplacement = abstractStorage->hasAnyDynamicReplacementAccessors(); if (!isNativeDynamic && !isDynamicReplacement) return; } @@ -2363,10 +2362,7 @@ void IRGenModule::emitOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *opaque) { // Don't emit accessors for functions that are not dynamic or dynamic // replacements. if (!abstractStorage) { - isNativeDynamic = opaque->getNamingDecl()->isNativeDynamic(); - isDynamicReplacement = opaque->getNamingDecl() - ->getAttrs() - .hasAttribute(); + isNativeDynamic = namingDecl->isNativeDynamic(); if (!isNativeDynamic && !isDynamicReplacement) return; } diff --git a/lib/SIL/SILFunctionBuilder.cpp b/lib/SIL/SILFunctionBuilder.cpp index 85c962470be37..0330b3d93cca0 100644 --- a/lib/SIL/SILFunctionBuilder.cpp +++ b/lib/SIL/SILFunctionBuilder.cpp @@ -76,13 +76,13 @@ void SILFunctionBuilder::addFunctionAttributes(SILFunction *F, SILFunctionTypeRepresentation::ObjCMethod) return; - auto *replacedFuncAttr = Attrs.getAttribute(); - if (!replacedFuncAttr) + // Only assign replacements when the thing being replaced is function-like and + // explicitly declared. + auto *origDecl = decl->getDynamicallyReplacedDecl(); + auto *replacedDecl = dyn_cast_or_null(origDecl); + if (!replacedDecl) return; - auto *replacedDecl = replacedFuncAttr->getReplacedFunction(); - assert(replacedDecl); - if (decl->isObjC()) { F->setObjCReplacement(replacedDecl); return; diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index d2980f81c191c..386217d3cc092 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -789,8 +789,7 @@ bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF, bool &isObjCReplacementSelfCall) { if (auto *func = dyn_cast_or_null(SGF.FunctionDC->getAsDecl())) { - auto *repl = func->getAttrs().getAttribute(); - if (repl && repl->getReplacedFunction() == afd) { + if (func->getDynamicallyReplacedDecl() == afd) { isObjCReplacementSelfCall = afd->isObjC(); return true; } diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 59eccfb70903b..bdeba011ddeac 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -1045,11 +1045,10 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { if (isa(VD) || isa(VD)) if (auto *replacementAttr = VD->getAttrs().getAttribute()) { - if (auto *replaced = replacementAttr->getReplacedFunction()) { + if (auto *replaced = VD->getDynamicallyReplacedDecl()) { if (replaced->isObjC()) return ObjCReason(ObjCReason::ImplicitlyObjC); - } else if (auto *replaced = - TypeChecker::findReplacedDynamicFunction(VD)) { + } else if (auto *replaced = VD->getDynamicallyReplacedDecl()) { if (replaced->isObjC()) return ObjCReason(ObjCReason::ImplicitlyObjC); } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 969f6adcaae24..7cd75394bbabb 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4082,9 +4082,6 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { serialization::decls_block::DynamicReplacementDeclAttrLayout:: readRecord(scratch, isImplicit, replacedFunID, numArgs, rawPieceIDs); - auto replacedFunDecl = MF.getDeclChecked(replacedFunID); - if (!replacedFunDecl) - return replacedFunDecl.takeError(); auto baseName = MF.getDeclBaseName(rawPieceIDs[0]); SmallVector pieces; for (auto pieceID : rawPieceIDs.slice(1)) @@ -4093,8 +4090,8 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() { assert(numArgs != 0); assert(!isImplicit && "Need to update for implicit"); Attr = DynamicReplacementAttr::create( - ctx, DeclName(ctx, baseName, ArrayRef(pieces)), - cast(*replacedFunDecl)); + ctx, DeclName(ctx, baseName, ArrayRef(pieces)), &MF, + replacedFunID); break; } diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index c490924d422e5..890863d1aa53c 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2070,7 +2070,7 @@ class Serializer::DeclSerializer : public DeclVisitor { didVerifyAttrs = true; } - void writeDeclAttribute(const DeclAttribute *DA) { + void writeDeclAttribute(const Decl *D, const DeclAttribute *DA) { using namespace decls_block; // Completely ignore attributes that aren't serialized. @@ -2246,10 +2246,11 @@ class Serializer::DeclSerializer : public DeclVisitor { pieces.push_back(S.addDeclBaseNameRef(replacedFun.getBaseName())); for (auto argName : replacedFun.getArgumentNames()) pieces.push_back(S.addDeclBaseNameRef(argName)); - assert(theAttr->getReplacedFunction()); + auto *afd = cast(D)->getDynamicallyReplacedDecl(); + assert(afd && "Missing replaced decl!"); DynamicReplacementDeclAttrLayout::emitRecord( S.Out, S.ScratchRecord, abbrCode, false, /*implicit flag*/ - S.addDeclRef(theAttr->getReplacedFunction()), pieces.size(), pieces); + S.addDeclRef(afd), pieces.size(), pieces); return; } @@ -2658,7 +2659,7 @@ class Serializer::DeclSerializer : public DeclVisitor { void visit(const Decl *D) { // Emit attributes (if any). for (auto Attr : D->getAttrs()) - writeDeclAttribute(Attr); + writeDeclAttribute(D, Attr); if (auto *value = dyn_cast(D)) writeDiscriminatorsIfNeeded(value); diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index f9929708d3cda..1f3fea838c059 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -226,7 +226,7 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { addSymbol( LinkEntity::forDynamicallyReplaceableFunctionKey(AFD, useAllocator)); } - if (AFD->getAttrs().hasAttribute()) { + if (AFD->getDynamicallyReplacedDecl()) { bool useAllocator = shouldUseAllocatorMangling(AFD); addSymbol(LinkEntity::forDynamicallyReplaceableFunctionVariable( AFD, useAllocator)); @@ -254,7 +254,7 @@ void TBDGenVisitor::visitFuncDecl(FuncDecl *FD) { addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorKey(opaqueResult)); addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorVar(opaqueResult)); } - if (FD->getAttrs().hasAttribute()) { + if (FD->getDynamicallyReplacedDecl()) { addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessor(opaqueResult)); addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorVar(opaqueResult)); } @@ -282,7 +282,7 @@ void TBDGenVisitor::visitAbstractStorageDecl(AbstractStorageDecl *ASD) { addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorKey(opaqueResult)); addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorVar(opaqueResult)); } - if (ASD->hasAnyDynamicReplacementAccessors()) { + if (ASD->getDynamicallyReplacedDecl()) { addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessor(opaqueResult)); addSymbol(LinkEntity::forOpaqueTypeDescriptorAccessorVar(opaqueResult)); } From 4579570ffe702f8b67e4cd675ad655a2f326a14e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Fri, 22 Nov 2019 01:08:35 -0800 Subject: [PATCH 6/6] Bump the module format version We've changed *what* is serialized by changing the way @_dynamicReplacement is type checked, but not *how* it's serialized. Bump the format so there aren't strange incompatibilities because of this. --- lib/IRGen/GenDecl.cpp | 2 +- lib/Sema/TypeCheckDeclObjC.cpp | 3 --- lib/Serialization/ModuleFormat.h | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 490ffc5cb6601..cf64b040e7eb5 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2347,7 +2347,7 @@ static void emitDynamicallyReplaceableThunk(IRGenModule &IGM, void IRGenModule::emitOpaqueTypeDescriptorAccessor(OpaqueTypeDecl *opaque) { auto *namingDecl = opaque->getNamingDecl(); auto *abstractStorage = dyn_cast(namingDecl); - + bool isNativeDynamic = false; const bool isDynamicReplacement = namingDecl->getDynamicallyReplacedDecl(); diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index bdeba011ddeac..54fe278987e52 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -1048,9 +1048,6 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { if (auto *replaced = VD->getDynamicallyReplacedDecl()) { if (replaced->isObjC()) return ObjCReason(ObjCReason::ImplicitlyObjC); - } else if (auto *replaced = VD->getDynamicallyReplacedDecl()) { - if (replaced->isObjC()) - return ObjCReason(ObjCReason::ImplicitlyObjC); } } diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 18907813aa127..0c148267a1dae 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 524; // function type differentiability +const uint16_t SWIFTMODULE_VERSION_MINOR = 526; // @_dynamicReplacement adjustments /// A standard hash seed used for all string hashes in a serialized module. ///