//===--- SILDeclRef.cpp - Implements SILDeclRef ---------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/SIL/SILDeclRef.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/Initializer.h" #include "swift/AST/ParameterList.h" #include "swift/AST/PropertyWrappers.h" #include "swift/AST/SourceFile.h" #include "swift/Basic/Assertions.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/SILLinkage.h" #include "swift/SIL/SILLocation.h" #include "swift/SILOptimizer/Utils/SpecializationMangler.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Mangle.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" using namespace swift; /// Get the method dispatch mechanism for a method. MethodDispatch swift::getMethodDispatch(AbstractFunctionDecl *method) { // Some methods are forced to be statically dispatched. if (method->hasForcedStaticDispatch()) return MethodDispatch::Static; if (method->getAttrs().hasAttribute()) return MethodDispatch::Static; // Import-as-member declarations are always statically referenced. if (method->isImportAsMember()) return MethodDispatch::Static; auto dc = method->getDeclContext(); if (dc->getSelfClassDecl()) { if (method->shouldUseObjCDispatch()) { return MethodDispatch::Class; } // Final methods can be statically referenced. if (method->isFinal()) return MethodDispatch::Static; // Imported class methods are dynamically dispatched. if (method->isObjC() && method->hasClangNode()) return MethodDispatch::Class; // Members defined directly inside a class are dynamically dispatched. if (isa(dc)) { // Native convenience initializers are not dynamically dispatched unless // required. if (auto ctor = dyn_cast(method)) { if (!ctor->isRequired() && !ctor->isDesignatedInit() && !requiresForeignEntryPoint(ctor)) return MethodDispatch::Static; } return MethodDispatch::Class; } } // Otherwise, it can be referenced statically. return MethodDispatch::Static; } bool swift::requiresForeignToNativeThunk(ValueDecl *vd) { // Functions imported from C, Objective-C methods imported from Objective-C, // as well as methods in @objc protocols (even protocols defined in Swift) // require a foreign to native thunk. auto dc = vd->getDeclContext(); if (auto proto = dyn_cast(dc)) if (proto->isObjC()) return true; if (auto fd = dyn_cast(vd)) return fd->hasClangNode(); return false; } bool swift::requiresForeignEntryPoint(ValueDecl *vd) { assert(!isa(vd)); if (vd->shouldUseObjCDispatch()) { return true; } if (vd->isObjC() && isa(vd->getDeclContext())) return true; if (vd->isImportAsMember()) return true; if (vd->hasClangNode()) return true; if (auto *accessor = dyn_cast(vd)) { // Property accessors should be generated alongside the property. if (accessor->isGetterOrSetter()) { auto *asd = accessor->getStorage(); if (asd->isObjC() && asd->hasClangNode()) return true; } } return false; } SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign, bool isDistributedThunk, bool isKnownToBeLocal, bool isRuntimeAccessible, SILDeclRef::BackDeploymentKind backDeploymentKind, AutoDiffDerivativeFunctionIdentifier *derivativeId) : loc(vd), kind(kind), isForeign(isForeign), distributedThunk(isDistributedThunk), isKnownToBeLocal(isKnownToBeLocal), isRuntimeAccessible(isRuntimeAccessible), backDeploymentKind(backDeploymentKind), defaultArgIndex(0), isAsyncLetClosure(0), pointer(derivativeId) {} SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign, bool asDistributed, bool asDistributedKnownToBeLocal) : isRuntimeAccessible(false), backDeploymentKind(SILDeclRef::BackDeploymentKind::None), defaultArgIndex(0), isAsyncLetClosure(0), pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr) { if (auto *vd = baseLoc.dyn_cast()) { if (auto *fd = dyn_cast(vd)) { // Map FuncDecls directly to Func SILDeclRefs. loc = fd; kind = Kind::Func; } // Map ConstructorDecls to the Allocator SILDeclRef of the constructor. else if (auto *cd = dyn_cast(vd)) { loc = cd; kind = Kind::Allocator; } // Map EnumElementDecls to the EnumElement SILDeclRef of the element. else if (auto *ed = dyn_cast(vd)) { loc = ed; kind = Kind::EnumElement; } // VarDecl constants require an explicit kind. else if (isa(vd)) { llvm_unreachable("must create SILDeclRef for VarDecl with explicit kind"); } // Map DestructorDecls to the Deallocator of the destructor. else if (auto dtor = dyn_cast(vd)) { loc = dtor; kind = Kind::Deallocator; } else { llvm_unreachable("invalid loc decl for SILDeclRef!"); } } else if (auto *ACE = baseLoc.dyn_cast()) { loc = ACE; kind = Kind::Func; if (ACE->getASTContext().LangOpts.hasFeature( Feature::RegionBasedIsolation)) { assert(ACE->getASTContext().LangOpts.hasFeature( Feature::SendingArgsAndResults) && "Sending args and results should always be enabled"); if (auto *autoClosure = dyn_cast(ACE)) { isAsyncLetClosure = autoClosure->getThunkKind() == AutoClosureExpr::Kind::AsyncLet; } } } else { llvm_unreachable("impossible SILDeclRef loc"); } isForeign = asForeign; distributedThunk = asDistributed; isKnownToBeLocal = asDistributedKnownToBeLocal; } SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, GenericSignature prespecializedSig) : SILDeclRef(baseLoc, false, false) { pointer = prespecializedSig.getPointer(); } std::optional SILDeclRef::getAnyFunctionRef() const { switch (getLocKind()) { case LocKind::Decl: if (auto *afd = getAbstractFunctionDecl()) return AnyFunctionRef(afd); return std::nullopt; case LocKind::Closure: return AnyFunctionRef(getAbstractClosureExpr()); case LocKind::File: return std::nullopt; } llvm_unreachable("Unhandled case in switch"); } DeclContext *SILDeclRef::getInnermostDeclContext() const { if (!loc) return nullptr; switch (getLocKind()) { case LocKind::Decl: return getDecl()->getInnermostDeclContext(); case LocKind::Closure: return getAbstractClosureExpr(); case LocKind::File: return getFileUnit(); } llvm_unreachable("Unhandled case in switch"); } ASTContext &SILDeclRef::getASTContext() const { auto *DC = getInnermostDeclContext(); assert(DC && "Must have a decl context"); return DC->getASTContext(); } std::optional SILDeclRef::getAvailabilityForLinkage() const { // Back deployment thunks and fallbacks don't have availability since they // are non-ABI. // FIXME: Generalize this check to all kinds of non-ABI functions. if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None) return std::nullopt; return getDecl()->getAvailabilityForLinkage(); } bool SILDeclRef::isThunk() const { return isForeignToNativeThunk() || isNativeToForeignThunk() || isDistributedThunk() || isBackDeploymentThunk(); } bool SILDeclRef::isClangImported() const { if (!hasDecl()) return false; ValueDecl *d = getDecl(); DeclContext *moduleContext = d->getDeclContext()->getModuleScopeContext(); if (isa(moduleContext)) { if (isClangGenerated()) return true; if (isa(d) || isa(d)) return !isForeign; if (auto *FD = dyn_cast(d)) if (isa(FD) || isa(d->getDeclContext())) return !isForeign; } return false; } bool SILDeclRef::isClangGenerated() const { if (!hasDecl()) return false; return isClangGenerated(getDecl()->getClangNode()); } // FIXME: this is a weird predicate. bool SILDeclRef::isClangGenerated(ClangNode node) { if (auto nd = dyn_cast_or_null(node.getAsDecl())) { // ie, 'static inline' functions for which we must ask Clang to emit a body // for explicitly if (!nd->isExternallyVisible()) return true; } return false; } bool SILDeclRef::isImplicit() const { switch (getLocKind()) { case LocKind::Decl: return getDecl()->isImplicit(); case LocKind::Closure: return getAbstractClosureExpr()->isImplicit(); case LocKind::File: // Files are currently never considered implicit. return false; } llvm_unreachable("Unhandled case in switch"); } bool SILDeclRef::hasUserWrittenCode() const { // Non-implicit decls generally have user-written code. if (!isImplicit()) { switch (kind) { case Kind::PropertyWrapperBackingInitializer: { // Only has user-written code if any of the property wrappers have // arguments to apply. Otherwise, it's just a forwarding initializer for // the wrappedValue. auto *var = cast(getDecl()); return llvm::any_of(var->getAttachedPropertyWrappers(), [&](auto *attr) { return attr->hasArgs(); }); } case Kind::PropertyWrapperInitFromProjectedValue: // Never has user-written code, is just a forwarding initializer. return false; default: if (auto decl = getDecl()) { // Declarations synthesized by ClangImporter by definition don't have // user written code, but despite that they aren't always marked // implicit. auto moduleContext = decl->getDeclContext()->getModuleScopeContext(); if (isa(moduleContext)) return false; } // TODO: This checking is currently conservative, we ought to // exhaustively handle all the cases here, and use emitOrDelayFunction // in more cases to take advantage of it. return true; } llvm_unreachable("Unhandled case in switch!"); } // Implicit decls generally don't have user-written code, but some splice // user code into their body. switch (kind) { case Kind::Func: { if (getAbstractClosureExpr()) { // Auto-closures have user-written code. if (auto *ACE = getAutoClosureExpr()) { // Currently all types of auto-closures can contain user code. Note this // logic does not affect delayed emission, as we eagerly emit all // closure definitions. This does however affect profiling. switch (ACE->getThunkKind()) { case AutoClosureExpr::Kind::None: case AutoClosureExpr::Kind::SingleCurryThunk: case AutoClosureExpr::Kind::DoubleCurryThunk: case AutoClosureExpr::Kind::AsyncLet: return true; } llvm_unreachable("Unhandled case in switch!"); } // Otherwise, assume an implicit closure doesn't have user code. return false; } // Lazy getters splice in the user-written initializer expr. if (auto *accessor = dyn_cast(getFuncDecl())) { auto *storage = accessor->getStorage(); if (accessor->isGetter() && !storage->isImplicit() && storage->getAttrs().hasAttribute()) { return true; } } return false; } case Kind::StoredPropertyInitializer: { // Property wrapper initializers for the implicit backing storage can splice // in the user-written initializer on the original property. auto *var = cast(getDecl()); if (auto *originalProperty = var->getOriginalWrappedProperty()) { if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) return true; } return false; } case Kind::Allocator: case Kind::Initializer: case Kind::EnumElement: case Kind::Destroyer: case Kind::Deallocator: case Kind::IsolatedDeallocator: case Kind::GlobalAccessor: case Kind::DefaultArgGenerator: case Kind::IVarInitializer: case Kind::IVarDestroyer: case Kind::PropertyWrapperBackingInitializer: case Kind::PropertyWrapperInitFromProjectedValue: case Kind::EntryPoint: case Kind::AsyncEntryPoint: // Implicit decls for these don't splice in user-written code. return false; } llvm_unreachable("Unhandled case in switch!"); } bool SILDeclRef::shouldBeEmittedForDebugger() const { if (!isFunc()) return false; if (getASTContext().SILOpts.OptMode != OptimizationMode::NoOptimization) return false;; if (!getASTContext().SILOpts.ShouldFunctionsBePreservedToDebugger) return false; if (getASTContext().LangOpts.hasFeature(Feature::Embedded)) return false; ValueDecl *decl = getDecl(); DeclAttributes &attrs = decl->getAttrs(); if (attrs.hasSemanticsAttr("no.preserve.debugger")) return false; if (getLinkage(ForDefinition) == SILLinkage::Shared) return false; if (auto decl = getDecl()) if (!decl->isImplicit()) return true; // Synthesized getters are still callable in the debugger. if (auto *accessor = dyn_cast_or_null(getFuncDecl())) { return accessor->isSynthesized() && accessor->isGetterOrSetter(); }; return false; } namespace { enum class LinkageLimit { /// No limit. None, /// The linkage should behave as if the decl is private. Private, /// The declaration is emitted on-demand; it should end up with internal /// or shared linkage. OnDemand, /// The declaration should never be made public. NeverPublic, /// The declaration should always be emitted into the client, AlwaysEmitIntoClient, }; } // end anonymous namespace /// Compute the linkage limit for a given SILDeclRef. This augments the /// mapping of access level to linkage to provide a maximum or minimum linkage. static LinkageLimit getLinkageLimit(SILDeclRef constant) { using Limit = LinkageLimit; using Kind = SILDeclRef::Kind; auto *d = constant.getDecl(); ASSERT(ABIRoleInfo(d).providesAPI() && "getLinkageLimit() for ABI decl?"); // Back deployment thunks and fallbacks are emitted into the client. if (constant.backDeploymentKind != SILDeclRef::BackDeploymentKind::None) return Limit::AlwaysEmitIntoClient; if (auto *fn = dyn_cast(d)) { // Native-to-foreign thunks for top-level decls are created on-demand, // unless they are marked @_cdecl, in which case they expose a dedicated // entry-point with the visibility of the function. // // Native-to-foreign thunks for methods are always just private, since // they're anchored by Objective-C metadata. auto &attrs = fn->getAttrs(); if (constant.isNativeToForeignThunk() && !attrs.hasAttribute()) { auto isTopLevel = fn->getDeclContext()->isModuleScopeContext(); return isTopLevel ? Limit::OnDemand : Limit::Private; } } if (auto fn = constant.getFuncDecl()) { // Forced-static-dispatch functions are created on-demand and have // at best shared linkage. if (fn->hasForcedStaticDispatch()) return Limit::OnDemand; } if (isa(d)) { // The destructor of a class implemented with @_objcImplementation is only // ever called by its ObjC thunk, so it should not be public. if (d->getDeclContext()->getSelfNominalTypeDecl()->hasClangNode()) return Limit::OnDemand; } switch (constant.kind) { case Kind::Func: case Kind::Allocator: case Kind::Initializer: case Kind::Deallocator: case Kind::IsolatedDeallocator: case Kind::Destroyer: { // @_alwaysEmitIntoClient declarations are like the default arguments of // public functions; they are roots for dead code elimination and have // serialized bodies, but no public symbol in the generated binary. if (d->getAttrs().hasAttribute()) return Limit::AlwaysEmitIntoClient; if (auto accessor = dyn_cast(d)) { auto *storage = accessor->getStorage(); if (storage->getAttrs().hasAttribute()) return Limit::AlwaysEmitIntoClient; } break; } case Kind::EnumElement: return Limit::OnDemand; case Kind::GlobalAccessor: // global unsafeMutableAddressor should be kept hidden if its decl // is resilient. return cast(d)->isResilient() ? Limit::NeverPublic : Limit::None; case Kind::DefaultArgGenerator: // If the default argument is to be serialized, only use non-ABI public // linkage. If the argument is not to be serialized, don't use a limit. // This actually means that default arguments *can be ABI public* if // `isSerialized()` returns false and the effective access level is public, // which happens under `-enable-testing` with an internal decl. return constant.isSerialized() ? Limit::AlwaysEmitIntoClient : Limit::None; case Kind::PropertyWrapperBackingInitializer: case Kind::PropertyWrapperInitFromProjectedValue: { if (!d->getDeclContext()->isTypeContext()) { // If the backing initializer is to be serialized, only use non-ABI public // linkage. If the initializer is not to be serialized, don't use a limit. // This actually means that it *can be ABI public* if `isSerialized()` // returns false and the effective access level is public, which happens // under `-enable-testing` with an internal decl. return constant.isSerialized() ? Limit::AlwaysEmitIntoClient : Limit::None; } // Otherwise, regular property wrapper backing initializers (for properties) // are treated just like stored property initializers. LLVM_FALLTHROUGH; } case Kind::StoredPropertyInitializer: { // Stored property initializers get the linkage of their containing type. // There are three cases: // // 1) Type is formally @_fixed_layout/@frozen. Root initializers can be // declared @inlinable. The property initializer must only reference // public symbols, and is serialized, so we give it PublicNonABI linkage. // // 2) Type is not formally @_fixed_layout/@frozen and the module is not // resilient. Root initializers can be declared @inlinable. This is the // annoying case. We give the initializer public linkage if the type is // public. // // 3) Type is resilient. The property initializer is never public because // root initializers cannot be @inlinable. // // FIXME: Get rid of case 2 somehow. if (constant.isSerialized()) return Limit::AlwaysEmitIntoClient; // FIXME: This should always be true. if (d->getModuleContext()->isStrictlyResilient()) return Limit::NeverPublic; break; } case Kind::IVarInitializer: case Kind::IVarDestroyer: // ivar initializers and destroyers are completely contained within the // class from which they come, and never get seen externally. return Limit::NeverPublic; case Kind::EntryPoint: case Kind::AsyncEntryPoint: llvm_unreachable("Already handled"); } return Limit::None; } SILLinkage SILDeclRef::getDefinitionLinkage() const { using Limit = LinkageLimit; auto privateLinkage = [&]() { // Private decls may still be serialized if they are e.g in an inlinable // function. In such a case, they receive shared linkage. return isNotSerialized() ? SILLinkage::Private : SILLinkage::Shared; }; // Prespecializations are public. if (getSpecializedSignature()) return SILLinkage::Public; // Closures can only be referenced from the same file. if (getAbstractClosureExpr()) return privateLinkage(); // The main entry-point is public. if (kind == Kind::EntryPoint) return SILLinkage::Public; if (kind == Kind::AsyncEntryPoint) { // async main entrypoint is referenced only from @main and // they are in the same SIL module. Hiding this entrypoint // from other object file makes it possible to link multiple // executable targets for SwiftPM testing with -entry-point-function-name return SILLinkage::Private; } // Calling convention thunks have shared linkage. if (isForeignToNativeThunk()) return SILLinkage::Shared; // Declarations imported from Clang modules have shared linkage. if (isClangImported()) return SILLinkage::Shared; const auto limit = getLinkageLimit(*this); if (limit == Limit::Private) return privateLinkage(); auto *decl = getDecl(); if (isPropertyWrapperBackingInitializer()) { auto *dc = decl->getDeclContext(); // External property wrapper backing initializers have linkage based // on the access level of their function. if (isa(decl)) { if (isa(dc)) return privateLinkage(); decl = cast(dc->getAsDecl()); } // Property wrappers in types have linkage based on the access level of // their nominal. if (dc->isTypeContext()) decl = cast(dc); } // Stored property initializers have linkage based on the access level of // their nominal. if (isStoredPropertyInitializer()) decl = cast( decl->getDeclContext()->getImplementedObjCContext()); // Compute the effective access level, taking e.g testable into consideration. auto effectiveAccess = decl->getEffectiveAccess(); // Private setter implementations for an internal storage declaration should // be at least internal as well, so that a dynamically-writable // keypath can be formed from other files in the same module. if (auto *accessor = dyn_cast(decl)) { auto storageAccess = accessor->getStorage()->getEffectiveAccess(); if (accessor->isSetter() && storageAccess >= AccessLevel::Internal) effectiveAccess = std::max(effectiveAccess, AccessLevel::Internal); } switch (effectiveAccess) { case AccessLevel::Private: case AccessLevel::FilePrivate: return privateLinkage(); case AccessLevel::Internal: assert(!isSerialized() && "Serialized decls should either be private (for decls in inlinable " "code), or they should be public"); if (limit == Limit::OnDemand) return SILLinkage::Shared; return SILLinkage::Hidden; case AccessLevel::Package: switch (limit) { case Limit::None: return SILLinkage::Package; case Limit::AlwaysEmitIntoClient: // Drop the AEIC if the enclosing decl is not effectively public. // This matches what we do in the `internal` case. if (isSerialized()) return SILLinkage::PackageNonABI; else return SILLinkage::Package; case Limit::OnDemand: return SILLinkage::Shared; case Limit::NeverPublic: return SILLinkage::Hidden; case Limit::Private: llvm_unreachable("Already handled"); } case AccessLevel::Public: case AccessLevel::Open: switch (limit) { case Limit::None: return SILLinkage::Public; case Limit::AlwaysEmitIntoClient: return SILLinkage::PublicNonABI; case Limit::OnDemand: return SILLinkage::Shared; case Limit::NeverPublic: return SILLinkage::Hidden; case Limit::Private: llvm_unreachable("Already handled"); } } llvm_unreachable("unhandled access"); } SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const { // Add external to the linkage of the definition // (e.g. Public -> PublicExternal) if this is a declaration. auto linkage = getDefinitionLinkage(); return forDefinition ? linkage : addExternalToLinkage(linkage); } SILDeclRef SILDeclRef::getDefaultArgGenerator(Loc loc, unsigned defaultArgIndex) { SILDeclRef result; result.loc = loc; result.kind = Kind::DefaultArgGenerator; result.defaultArgIndex = defaultArgIndex; return result; } SILDeclRef SILDeclRef::getMainDeclEntryPoint(ValueDecl *decl) { auto *file = cast(decl->getDeclContext()->getModuleScopeContext()); assert(file->getMainDecl() == decl); SILDeclRef result; result.loc = decl; result.kind = Kind::EntryPoint; return result; } SILDeclRef SILDeclRef::getAsyncMainDeclEntryPoint(ValueDecl *decl) { auto *file = cast(decl->getDeclContext()->getModuleScopeContext()); assert(file->getMainDecl() == decl); SILDeclRef result; result.loc = decl; result.kind = Kind::AsyncEntryPoint; return result; } SILDeclRef SILDeclRef::getAsyncMainFileEntryPoint(FileUnit *file) { assert(file->hasEntryPoint() && !file->getMainDecl()); SILDeclRef result; result.loc = file; result.kind = Kind::AsyncEntryPoint; return result; } SILDeclRef SILDeclRef::getMainFileEntryPoint(FileUnit *file) { assert(file->hasEntryPoint() && !file->getMainDecl()); SILDeclRef result; result.loc = file; result.kind = Kind::EntryPoint; return result; } bool SILDeclRef::hasClosureExpr() const { return loc.is() && isa(getAbstractClosureExpr()); } bool SILDeclRef::hasAutoClosureExpr() const { return loc.is() && isa(getAbstractClosureExpr()); } bool SILDeclRef::hasFuncDecl() const { return loc.is() && isa(getDecl()); } ClosureExpr *SILDeclRef::getClosureExpr() const { return dyn_cast_or_null(getAbstractClosureExpr()); } AutoClosureExpr *SILDeclRef::getAutoClosureExpr() const { return dyn_cast_or_null(getAbstractClosureExpr()); } FuncDecl *SILDeclRef::getFuncDecl() const { return dyn_cast_or_null(getDecl()); } ModuleDecl *SILDeclRef::getModuleContext() const { if (hasDecl()) { return getDecl()->getModuleContext(); } else if (hasFileUnit()) { return getFileUnit()->getParentModule(); } else if (hasClosureExpr()) { return getClosureExpr()->getParentModule(); } else if (hasAutoClosureExpr()) { return getAutoClosureExpr()->getParentModule(); } llvm_unreachable("Unknown declaration reference"); } bool SILDeclRef::isSetter() const { if (!hasDecl()) return false; if (auto accessor = dyn_cast(getDecl())) return accessor->isSetter(); return false; } AbstractFunctionDecl *SILDeclRef::getAbstractFunctionDecl() const { return dyn_cast_or_null(getDecl()); } bool SILDeclRef::isInitAccessor() const { if (kind != Kind::Func || !hasDecl()) return false; if (auto accessor = dyn_cast(getDecl())) return accessor->getAccessorKind() == AccessorKind::Init; return false; } /// True if the function should be treated as transparent. bool SILDeclRef::isTransparent() const { if (isEnumElement()) return true; if (isStoredPropertyInitializer()) return true; if (hasAutoClosureExpr()) { auto *ace = getAutoClosureExpr(); switch (ace->getThunkKind()) { case AutoClosureExpr::Kind::None: return true; case AutoClosureExpr::Kind::AsyncLet: case AutoClosureExpr::Kind::DoubleCurryThunk: case AutoClosureExpr::Kind::SingleCurryThunk: break; } } // To support using metatypes as type hints in Embedded Swift. A default // argument generator might be returning a metatype, which we normally don't // support in Embedded Swift, but to still allow metatypes as type hints, we // make the generator always inline to the callee by marking it transparent. if (getASTContext().LangOpts.hasFeature(Feature::Embedded)) { if (isDefaultArgGenerator() && hasDecl()) { auto *decl = getDecl(); auto *param = getParameterAt(decl, defaultArgIndex); Type paramType = param->getTypeOfDefaultExpr(); if (paramType && paramType->is()) return true; } } if (hasDecl()) { if (auto *AFD = dyn_cast(getDecl())) return AFD->isTransparent(); if (auto *ASD = dyn_cast(getDecl())) return ASD->isTransparent(); } return false; } bool SILDeclRef::isSerialized() const { return getSerializedKind() == IsSerialized; } bool SILDeclRef::isNotSerialized() const { return getSerializedKind() == IsNotSerialized; } /// True if the function should have its body serialized. SerializedKind_t SILDeclRef::getSerializedKind() const { if (auto closure = getAbstractClosureExpr()) { // Ask the AST if we're inside an @inlinable context. if (closure->getResilienceExpansion() == ResilienceExpansion::Minimal) { return IsSerialized; } return IsNotSerialized; } if (kind == Kind::EntryPoint || kind == Kind::AsyncEntryPoint) return IsNotSerialized; if (isIVarInitializerOrDestroyer()) return IsNotSerialized; auto *d = getDecl(); ASSERT(ABIRoleInfo(d).providesAPI() && "should not get serialization info from ABI-only decl"); // Default and property wrapper argument generators are serialized if the // containing declaration is public. if (isDefaultArgGenerator() || (isPropertyWrapperBackingInitializer() && isa(d))) { if (isPropertyWrapperBackingInitializer()) { if (auto *func = dyn_cast_or_null(d->getDeclContext()->getAsDecl())) { d = func; } } // Ask the AST if we're inside an @inlinable context. if (d->getDeclContext()->getResilienceExpansion() == ResilienceExpansion::Minimal) { return IsSerialized; } // Otherwise, check if the owning declaration is public. auto scope = d->getFormalAccessScope(/*useDC=*/nullptr, /*treatUsableFromInlineAsPublic=*/true); if (scope.isPublic()) return IsSerialized; return IsNotSerialized; } // Stored property initializers are inlinable if the type is explicitly // marked as @frozen. if (isStoredPropertyInitializer() || (isPropertyWrapperBackingInitializer() && d->getDeclContext()->isTypeContext())) { auto *nominal = dyn_cast(d->getDeclContext()); // If this isn't in a nominal, it must be in an @objc @implementation // extension. We don't serialize those since clients outside the module // don't think of these as Swift classes. if (!nominal) { ASSERT(isa(d->getDeclContext()) && cast(d->getDeclContext())->isObjCImplementation()); return IsNotSerialized; } auto scope = nominal->getFormalAccessScope(/*useDC=*/nullptr, /*treatUsableFromInlineAsPublic=*/true); if (!scope.isPublic()) return IsNotSerialized; if (nominal->isFormallyResilient()) return IsNotSerialized; return IsSerialized; } // Note: if 'd' is a function, then 'dc' is the function itself, not // its parent context. auto *dc = d->getInnermostDeclContext(); // Local functions are serializable if their parent function is // serializable. if (d->getDeclContext()->isLocalContext()) { if (dc->getResilienceExpansion() == ResilienceExpansion::Minimal) return IsSerialized; return IsNotSerialized; } // Anything else that is not public is not serializable. if (d->getEffectiveAccess() < AccessLevel::Public) return IsNotSerialized; // Enum element constructors are serializable if the enum is // @usableFromInline or public. if (isEnumElement()) return IsSerialized; // 'read' and 'modify' accessors synthesized on-demand are serialized if // visible outside the module. if (auto fn = dyn_cast(d)) if (!isClangImported() && fn->hasForcedStaticDispatch()) return IsSerialized; if (isForeignToNativeThunk()) return IsSerialized; // The allocating entry point for designated initializers are serialized // if the class is @usableFromInline or public. Actors are excluded because // whether the init is designated is not clearly reflected in the source code. if (kind == SILDeclRef::Kind::Allocator) { auto *ctor = cast(d); if (auto classDecl = ctor->getDeclContext()->getSelfClassDecl()) { if (!classDecl->isAnyActor() && ctor->isDesignatedInit()) if (!ctor->hasClangNode()) return IsSerialized; } } if (isForeign) { // @objc thunks for methods are not serializable since they're only // referenced from the method table. if (d->getDeclContext()->isTypeContext()) return IsNotSerialized; // @objc thunks for top-level functions are serializable since they're // referenced from @convention(c) conversions inside inlinable // functions. return IsSerialized; } // Declarations imported from Clang modules are serialized if // referenced from an inlinable context. if (isClangImported()) return IsSerialized; // Handle back deployed functions. The original back deployed function // should not be serialized, but the thunk and fallback should be since they // need to be emitted into the client. if (isBackDeployed()) { switch (backDeploymentKind) { case BackDeploymentKind::None: return IsNotSerialized; case BackDeploymentKind::Fallback: case BackDeploymentKind::Thunk: return IsSerialized; } } // Otherwise, ask the AST if we're inside an @inlinable context. if (dc->getResilienceExpansion() == ResilienceExpansion::Minimal) return IsSerialized; return IsNotSerialized; } /// True if the function has an @inline(never) attribute. bool SILDeclRef::isNoinline() const { if (!hasDecl()) return false; auto *decl = getDecl(); ASSERT(ABIRoleInfo(decl).providesAPI() && "should not get inline attr from ABI-only decl"); if (auto *attr = decl->getAttrs().getAttribute()) if (attr->getKind() == InlineKind::Never) return true; if (auto *accessorDecl = dyn_cast(decl)) { auto *storage = accessorDecl->getStorage(); if (auto *attr = storage->getAttrs().getAttribute()) if (attr->getKind() == InlineKind::Never) return true; } return false; } /// True if the function has the @inline(__always) attribute. bool SILDeclRef::isAlwaysInline() const { swift::Decl *decl = nullptr; if (hasDecl()) { decl = getDecl(); } else if (auto *ce = getAbstractClosureExpr()) { // Closures within @inline(__always) functions should be always inlined, too. // Note that this is different from @inline(never), because closures inside // @inline(never) _can_ be inlined within the inline-never function. decl = ce->getParent()->getInnermostDeclarationDeclContext(); if (!decl) return false; } else { return false; } ASSERT(ABIRoleInfo(decl).providesAPI() && "should not get inline attr from ABI-only decl"); if (auto attr = decl->getAttrs().getAttribute()) if (attr->getKind() == InlineKind::Always) return true; if (auto *accessorDecl = dyn_cast(decl)) { auto *storage = accessorDecl->getStorage(); if (auto *attr = storage->getAttrs().getAttribute()) if (attr->getKind() == InlineKind::Always) return true; } return false; } bool SILDeclRef::isBackDeployed() const { if (!hasDecl()) return false; auto *decl = getDecl(); ASSERT(ABIRoleInfo(decl).providesAPI() && "should not get backDeployed from ABI-only decl"); if (auto afd = dyn_cast(decl)) return afd->isBackDeployed(getASTContext()); return false; } bool SILDeclRef::isForeignToNativeThunk() const { // If this isn't a native entry-point, it's not a foreign-to-native thunk. if (isForeign) return false; // Non-decl entry points are never natively foreign, so they would never // have a foreign-to-native thunk. if (!hasDecl()) return false; // A default argument generator for a C++ function is a Swift function, so no // thunk needed. if (isDefaultArgGenerator()) return false; if (requiresForeignToNativeThunk(getDecl())) return true; // ObjC initializing constructors and factories are foreign. // We emit a special native allocating constructor though. if (isa(getDecl()) && (kind == Kind::Initializer || cast(getDecl())->isFactoryInit()) && getDecl()->hasClangNode()) return true; return false; } bool SILDeclRef::isNativeToForeignThunk() const { // If this isn't a foreign entry-point, it's not a native-to-foreign thunk. if (!isForeign) return false; switch (getLocKind()) { case LocKind::Decl: // A decl with a clang node doesn't have a native entry-point to forward // onto. if (getDecl()->hasClangNode()) return false; // No thunk is required if the decl directly references an external decl. if (getDecl()->getAttrs().hasAttribute()) return false; // Only certain kinds of SILDeclRef can expose native-to-foreign thunks. return kind == Kind::Func || kind == Kind::Initializer || kind == Kind::Deallocator; case LocKind::Closure: // We can have native-to-foreign thunks over closures. return true; case LocKind::File: return false; } llvm_unreachable("Unhandled case in switch"); } bool SILDeclRef::isDistributedThunk() const { if (!distributedThunk) return false; return kind == Kind::Func; } bool SILDeclRef::isDistributed() const { if (!hasFuncDecl()) return false; if (auto decl = getFuncDecl()) { return decl->isDistributed(); } return false; } bool SILDeclRef::isBackDeploymentFallback() const { if (backDeploymentKind != BackDeploymentKind::Fallback) return false; return kind == Kind::Func || kind == Kind::Initializer || kind == Kind::Allocator; } bool SILDeclRef::isBackDeploymentThunk() const { if (backDeploymentKind != BackDeploymentKind::Thunk) return false; return kind == Kind::Func || kind == Kind::Initializer || kind == Kind::Allocator; } /// Use the Clang importer to mangle a Clang declaration. static void mangleClangDeclViaImporter(raw_ostream &buffer, const clang::NamedDecl *clangDecl, ASTContext &ctx) { auto *importer = static_cast(ctx.getClangModuleLoader()); importer->getMangledName(buffer, clangDecl); } static std::string mangleClangDecl(Decl *decl, bool isForeign) { auto clangDecl = decl->getClangDecl(); if (auto namedClangDecl = dyn_cast(clangDecl)) { if (auto asmLabel = namedClangDecl->getAttr()) { std::string s(1, '\01'); s += asmLabel->getLabel(); return s; } else if (namedClangDecl->hasAttr() || decl->getASTContext().LangOpts.EnableCXXInterop) { std::string storage; llvm::raw_string_ostream SS(storage); mangleClangDeclViaImporter(SS, namedClangDecl, decl->getASTContext()); return SS.str(); } return namedClangDecl->getName().str(); } else if (auto objcDecl = dyn_cast(clangDecl)) { if (objcDecl->isDirectMethod() && isForeign) { std::string storage; llvm::raw_string_ostream SS(storage); clang::ASTContext &ctx = clangDecl->getASTContext(); std::unique_ptr mangler(ctx.createMangleContext()); mangler->mangleObjCMethodName(objcDecl, SS, /*includePrefixByte=*/true, /*includeCategoryNamespace=*/false); return SS.str(); } } return ""; } std::string SILDeclRef::mangle(ManglingKind MKind) const { ASSERT(!hasDecl() || ABIRoleInfo(getDecl()).providesAPI() && "SILDeclRef mangling ABI decl directly?"); using namespace Mangle; ASTMangler mangler(getASTContext()); if (auto *derivativeFunctionIdentifier = getDerivativeFunctionIdentifier()) { std::string originalMangled = asAutoDiffOriginalFunction().mangle(MKind); auto *silParameterIndices = autodiff::getLoweredParameterIndices( derivativeFunctionIdentifier->getParameterIndices(), getDecl()->getInterfaceType()->castTo()); // FIXME: is this correct in the presence of curried types? auto *resultIndices = autodiff::getFunctionSemanticResultIndices( asAutoDiffOriginalFunction().getAbstractFunctionDecl(), derivativeFunctionIdentifier->getParameterIndices()); AutoDiffConfig silConfig( silParameterIndices, resultIndices, derivativeFunctionIdentifier->getDerivativeGenericSignature()); return mangler.mangleAutoDiffDerivativeFunction( asAutoDiffOriginalFunction().getAbstractFunctionDecl(), derivativeFunctionIdentifier->getKind(), silConfig); } // As a special case, Clang functions and globals don't get mangled at all // - except \c objc_direct decls. if (hasDecl() && !isDefaultArgGenerator()) { if (getDecl()->getClangDecl()) { if (!isForeignToNativeThunk() && !isNativeToForeignThunk()) { auto clangMangling = mangleClangDecl(getDecl(), isForeign); if (!clangMangling.empty()) return clangMangling; } } } // Mangle prespecializations. if (getSpecializedSignature()) { SILDeclRef nonSpecializedDeclRef = *this; nonSpecializedDeclRef.pointer = (AutoDiffDerivativeFunctionIdentifier *)nullptr; auto mangledNonSpecializedString = nonSpecializedDeclRef.mangle(); auto *funcDecl = cast(getDecl()); auto genericSig = funcDecl->getGenericSignature(); return GenericSpecializationMangler::manglePrespecialization( getASTContext(), mangledNonSpecializedString, genericSig, getSpecializedSignature()); } ASTMangler::SymbolKind SKind = ASTMangler::SymbolKind::Default; switch (MKind) { case SILDeclRef::ManglingKind::Default: if (isForeign) { SKind = ASTMangler::SymbolKind::SwiftAsObjCThunk; } else if (isForeignToNativeThunk()) { SKind = ASTMangler::SymbolKind::ObjCAsSwiftThunk; } else if (isDistributedThunk()) { SKind = ASTMangler::SymbolKind::DistributedThunk; } else if (isBackDeploymentThunk()) { SKind = ASTMangler::SymbolKind::BackDeploymentThunk; } else if (isBackDeploymentFallback()) { SKind = ASTMangler::SymbolKind::BackDeploymentFallback; } break; case SILDeclRef::ManglingKind::DynamicThunk: SKind = ASTMangler::SymbolKind::DynamicThunk; break; } switch (kind) { case SILDeclRef::Kind::Func: if (auto *ACE = getAbstractClosureExpr()) return mangler.mangleClosureEntity(ACE, SKind); // As a special case, functions can have manually mangled names. // Use the SILGen name only for the original non-thunked, non-curried entry // point. if (auto NameA = getDecl()->getAttrs().getAttribute()) if (!NameA->Name.empty() && !isThunk()) { return NameA->Name.str(); } if (auto *ExternA = ExternAttr::find(getDecl()->getAttrs(), ExternKind::C)) { assert(isa(getDecl()) && "non-FuncDecl with @_extern should be rejected by typechecker"); return ExternA->getCName(cast(getDecl())).str(); } // Use a given cdecl name for native-to-foreign thunks. if (auto CDeclA = getDecl()->getAttrs().getAttribute()) if (isNativeToForeignThunk()) { // If this is an @implementation @_cdecl, mangle it like the clang // function it implements. if (auto objcInterface = getDecl()->getImplementedObjCDecl()) { auto clangMangling = mangleClangDecl(objcInterface, isForeign); if (!clangMangling.empty()) return clangMangling; } return CDeclA->Name.str(); } if (SKind == ASTMangler::SymbolKind::DistributedThunk) { return mangler.mangleDistributedThunk(cast(getDecl())); } // Otherwise, fall through into the 'other decl' case. LLVM_FALLTHROUGH; case SILDeclRef::Kind::EnumElement: return mangler.mangleEntity(getDecl(), SKind); case SILDeclRef::Kind::Deallocator: return mangler.mangleDestructorEntity(cast(getDecl()), DestructorKind::Deallocating, SKind); case SILDeclRef::Kind::Destroyer: return mangler.mangleDestructorEntity(cast(getDecl()), DestructorKind::NonDeallocating, SKind); case SILDeclRef::Kind::IsolatedDeallocator: return mangler.mangleDestructorEntity(cast(getDecl()), DestructorKind::IsolatedDeallocating, SKind); case SILDeclRef::Kind::Allocator: // As a special case, initializers can have manually mangled names. // Use the SILGen name only for the original non-thunked, non-curried entry // point. if (auto NameA = getDecl()->getAttrs().getAttribute()) { if (!NameA->Name.empty() && !isThunk()) { return NameA->Name.str(); } } return mangler.mangleConstructorEntity(cast(getDecl()), /*allocating*/ true, SKind); case SILDeclRef::Kind::Initializer: return mangler.mangleConstructorEntity(cast(getDecl()), /*allocating*/ false, SKind); case SILDeclRef::Kind::IVarInitializer: case SILDeclRef::Kind::IVarDestroyer: return mangler.mangleIVarInitDestroyEntity(cast(getDecl()), kind == SILDeclRef::Kind::IVarDestroyer, SKind); case SILDeclRef::Kind::GlobalAccessor: return mangler.mangleAccessorEntity(AccessorKind::MutableAddress, cast(getDecl()), /*isStatic*/ false, SKind); case SILDeclRef::Kind::DefaultArgGenerator: return mangler.mangleDefaultArgumentEntity( cast(getDecl()), defaultArgIndex, SKind); case SILDeclRef::Kind::StoredPropertyInitializer: return mangler.mangleInitializerEntity(cast(getDecl()), SKind); case SILDeclRef::Kind::PropertyWrapperBackingInitializer: return mangler.mangleBackingInitializerEntity(cast(getDecl()), SKind); case SILDeclRef::Kind::PropertyWrapperInitFromProjectedValue: return mangler.mangleInitFromProjectedValueEntity(cast(getDecl()), SKind); case SILDeclRef::Kind::AsyncEntryPoint: { return "async_Main"; } case SILDeclRef::Kind::EntryPoint: { return getASTContext().getEntryPointFunctionName(); } } llvm_unreachable("bad entity kind!"); } // Returns true if the given JVP/VJP SILDeclRef requires a new vtable entry. // FIXME(https://github.com/apple/swift/issues/54833): Also consider derived declaration `@derivative` attributes. static bool derivativeFunctionRequiresNewVTableEntry(SILDeclRef declRef) { assert(declRef.getDerivativeFunctionIdentifier() && "Expected a derivative function SILDeclRef"); auto overridden = declRef.getOverridden(); if (!overridden) return false; // Get the derived `@differentiable` attribute. auto *derivedDiffAttr = *llvm::find_if( declRef.getDecl()->getAttrs().getAttributes(), [&](const DifferentiableAttr *derivedDiffAttr) { return derivedDiffAttr->getParameterIndices() == declRef.getDerivativeFunctionIdentifier()->getParameterIndices(); }); assert(derivedDiffAttr && "Expected `@differentiable` attribute"); // Otherwise, if the base `@differentiable` attribute specifies a derivative // function, then the derivative is inherited and no new vtable entry is // needed. Return false. auto baseDiffAttrs = overridden.getDecl()->getAttrs().getAttributes(); for (auto *baseDiffAttr : baseDiffAttrs) { if (baseDiffAttr->getParameterIndices() == declRef.getDerivativeFunctionIdentifier()->getParameterIndices()) return false; } // Otherwise, if there is no base `@differentiable` attribute exists, then a // new vtable entry is needed. Return true. return true; } bool SILDeclRef::requiresNewVTableEntry() const { if (getDerivativeFunctionIdentifier()) if (derivativeFunctionRequiresNewVTableEntry(*this)) return true; if (!hasDecl()) return false; if (isBackDeploymentThunk()) return false; auto fnDecl = dyn_cast(getDecl()); if (!fnDecl) return false; if (fnDecl->needsNewVTableEntry()) return true; return false; } bool SILDeclRef::requiresNewWitnessTableEntry() const { return cast(getDecl())->requiresNewWitnessTableEntry(); } SILDeclRef SILDeclRef::getOverridden() const { if (!hasDecl()) return SILDeclRef(); auto overridden = getDecl()->getOverriddenDecl(); if (!overridden) return SILDeclRef(); return withDecl(overridden); } SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const { if (auto overridden = getOverridden()) { // Back deployed methods should not be overridden. assert(backDeploymentKind == SILDeclRef::BackDeploymentKind::None); // If we overrode a foreign decl or dynamic method, if this is an // accessor for a property that overrides an ObjC decl, or if it is an // @NSManaged property, then it won't be in the vtable. if (overridden.getDecl()->hasClangNode()) return SILDeclRef(); // Distributed thunks are not in the vtable. if (isDistributedThunk()) return SILDeclRef(); // An @objc convenience initializer can be "overridden" in the sense that // its selector is reclaimed by a subclass's convenience init with the // same name. The AST models this as an override for the purposes of // ObjC selector validation, but it isn't for Swift method dispatch // purposes. if (overridden.kind == SILDeclRef::Kind::Allocator) { auto overriddenCtor = cast(overridden.getDecl()); if (!overriddenCtor->isDesignatedInit() && !overriddenCtor->isRequired()) return SILDeclRef(); } // Initializing entry points for initializers won't be in the vtable. // For Swift designated initializers, they're only used in super.init // chains, which can always be statically resolved. Other native Swift // initializers only have allocating entry points. ObjC initializers always // have the initializing entry point (corresponding to the -init method) // but those are never in the vtable. if (overridden.kind == SILDeclRef::Kind::Initializer) { return SILDeclRef(); } // Overrides of @objc dynamic declarations are not in the vtable. if (overridden.getDecl()->shouldUseObjCDispatch()) { return SILDeclRef(); } if (auto *accessor = dyn_cast(overridden.getDecl())) { auto *asd = accessor->getStorage(); if (asd->hasClangNode()) return SILDeclRef(); if (asd->shouldUseObjCDispatch()) { return SILDeclRef(); } } // If we overrode a decl from an extension, it won't be in a vtable // either. This can occur for extensions to ObjC classes. if (isa(overridden.getDecl()->getDeclContext())) return SILDeclRef(); // JVPs/VJPs are overridden only if the base declaration has a // `@differentiable` attribute with the same parameter indices. if (getDerivativeFunctionIdentifier()) { auto overriddenAttrs = overridden.getDecl()->getAttrs().getAttributes(); for (const auto *attr : overriddenAttrs) { if (attr->getParameterIndices() != getDerivativeFunctionIdentifier()->getParameterIndices()) continue; auto *overriddenDerivativeId = overridden.getDerivativeFunctionIdentifier(); overridden.pointer = AutoDiffDerivativeFunctionIdentifier::get( overriddenDerivativeId->getKind(), overriddenDerivativeId->getParameterIndices(), attr->getDerivativeGenericSignature(), getDecl()->getASTContext()); return overridden; } return SILDeclRef(); } return overridden; } return SILDeclRef(); } SILDeclRef SILDeclRef::getOverriddenWitnessTableEntry() const { auto bestOverridden = getOverriddenWitnessTableEntry(cast(getDecl())); return withDecl(bestOverridden); } AbstractFunctionDecl *SILDeclRef::getOverriddenWitnessTableEntry( AbstractFunctionDecl *func) { if (!isa(func->getDeclContext())) return func; AbstractFunctionDecl *bestOverridden = nullptr; SmallVector stack; SmallPtrSet visited; stack.push_back(func); visited.insert(func); while (!stack.empty()) { auto current = stack.back(); stack.pop_back(); auto overriddenDecls = current->getOverriddenDecls(); if (overriddenDecls.empty()) { // This entry introduced a witness table entry. Determine whether it is // better than the best entry we've seen thus far. if (!bestOverridden || ProtocolDecl::compare( cast(current->getDeclContext()), cast(bestOverridden->getDeclContext())) < 0) { bestOverridden = cast(current); } continue; } // Add overridden declarations to the stack. for (auto overridden : overriddenDecls) { auto overriddenFunc = cast(overridden); if (visited.insert(overriddenFunc).second) stack.push_back(overriddenFunc); } } return bestOverridden; } SILDeclRef SILDeclRef::getOverriddenVTableEntry() const { SILDeclRef cur = *this, next = *this; do { cur = next; if (cur.requiresNewVTableEntry()) return cur; next = cur.getNextOverriddenVTableEntry(); } while (next); return cur; } SILLocation SILDeclRef::getAsRegularLocation() const { switch (getLocKind()) { case LocKind::Decl: return RegularLocation(getDecl()); case LocKind::Closure: return RegularLocation(getAbstractClosureExpr()); case LocKind::File: return RegularLocation::getModuleLocation(); } llvm_unreachable("Unhandled case in switch"); } SubclassScope SILDeclRef::getSubclassScope() const { if (!hasDecl()) return SubclassScope::NotApplicable; auto *decl = getDecl(); if (!isa(decl)) return SubclassScope::NotApplicable; DeclContext *context = decl->getDeclContext(); // Only methods in non-final classes go in the vtable. auto *classType = dyn_cast(context); if (!classType || classType->isFinal()) return SubclassScope::NotApplicable; // If a method appears in the vtable of a class, we must give it's symbol // special consideration when computing visibility because the SIL-level // linkage does not map to the symbol's visibility in a straightforward // way. // // In particular, the rules are: // - If the class metadata is not resilient, then all method symbols must // be visible from any translation unit where a subclass might be defined, // because the subclass metadata will re-emit all vtable entries. // // - For resilient classes, we do the opposite: generally, a method's symbol // can be hidden from other translation units, because we want to enforce // that resilient access patterns are used for method calls and overrides. // // Constructors and final methods are the exception here, because they can // be called directly. // FIXME: This is too narrow. Any class with resilient metadata should // probably have this, at least for method overrides that don't add new // vtable entries. bool isStrictResilientClass = classType->isStrictlyResilient(); if (auto *CD = dyn_cast(decl)) { if (isStrictResilientClass) return SubclassScope::NotApplicable; // Initializing entry points do not appear in the vtable. if (kind == SILDeclRef::Kind::Initializer) return SubclassScope::NotApplicable; // Non-required convenience inits do not appear in the vtable. if (!CD->isRequired() && !CD->isDesignatedInit()) return SubclassScope::NotApplicable; } else if (isa(decl)) { // Destructors do not appear in the vtable. return SubclassScope::NotApplicable; } else { assert(isa(decl)); } // Various forms of thunks don't go in the vtable. if (isThunk() || isForeign) return SubclassScope::NotApplicable; // Default arg generators don't go in the vtable. if (isDefaultArgGenerator()) return SubclassScope::NotApplicable; if (decl->isFinal()) { // Final methods only go in the vtable if they override something. if (!decl->getOverriddenDecl()) return SubclassScope::NotApplicable; // In the resilient case, we're going to be making symbols _less_ // visible, so make sure we stop now; final methods can always be // called directly. if (isStrictResilientClass) return SubclassScope::Internal; } assert(decl->getEffectiveAccess() <= classType->getEffectiveAccess() && "class must be as visible as its members"); if (isStrictResilientClass) { // The symbol should _only_ be reached via the vtable, so we're // going to make it hidden. return SubclassScope::Resilient; } switch (classType->getEffectiveAccess()) { case AccessLevel::Private: case AccessLevel::FilePrivate: // If the class is private, it can only be subclassed from the same // SILModule, so we don't need to do anything. return SubclassScope::NotApplicable; case AccessLevel::Internal: case AccessLevel::Package: case AccessLevel::Public: // If the class is internal or public, it can only be subclassed from // the same AST Module, but possibly a different SILModule. return SubclassScope::Internal; case AccessLevel::Open: // If the class is open, it can be subclassed from a different // AST Module. All method symbols are public. return SubclassScope::External; } llvm_unreachable("Unhandled access level in switch."); } Expr *SILDeclRef::getInitializationExpr() const { switch (kind) { case Kind::StoredPropertyInitializer: { auto *var = cast(getDecl()); auto *pbd = var->getParentPatternBinding(); unsigned idx = pbd->getPatternEntryIndexForVarDecl(var); auto *init = pbd->getInit(idx); assert(!pbd->isInitializerSubsumed(idx)); // If this is the backing storage for a property with an attached wrapper // that was initialized with `=`, use that expression as the initializer. if (auto originalProperty = var->getOriginalWrappedProperty()) { if (originalProperty->isPropertyMemberwiseInitializedWithWrappedType()) { auto wrapperInfo = originalProperty->getPropertyWrapperInitializerInfo(); auto *placeholder = wrapperInfo.getWrappedValuePlaceholder(); init = placeholder->getOriginalWrappedValue(); assert(init); } } return init; } case Kind::PropertyWrapperBackingInitializer: { auto *var = cast(getDecl()); auto wrapperInfo = var->getPropertyWrapperInitializerInfo(); assert(wrapperInfo.hasInitFromWrappedValue()); return wrapperInfo.getInitFromWrappedValue(); } case Kind::PropertyWrapperInitFromProjectedValue: { auto *var = cast(getDecl()); auto wrapperInfo = var->getPropertyWrapperInitializerInfo(); assert(wrapperInfo.hasInitFromProjectedValue()); return wrapperInfo.getInitFromProjectedValue(); } default: return nullptr; } } unsigned SILDeclRef::getParameterListCount() const { // Only decls can introduce currying. if (!hasDecl()) return 1; // Always uncurried even if the underlying function is curried. if (kind == Kind::DefaultArgGenerator || kind == Kind::EntryPoint || kind == Kind::AsyncEntryPoint) return 1; auto *vd = getDecl(); if (isa(vd) || isa(vd)) { // For functions and enum elements, the number of parameter lists is the // same as in their interface type. return vd->getNumCurryLevels(); } else if (isa(vd)) { return 2; } else if (isa(vd)) { return 1; } else { llvm_unreachable("Unhandled ValueDecl for SILDeclRef"); } } static bool isDesignatedConstructorForClass(ValueDecl *decl) { if (auto *ctor = dyn_cast_or_null(decl)) if (ctor->getDeclContext()->getSelfClassDecl()) return ctor->isDesignatedInit(); return false; } bool SILDeclRef::canBeDynamicReplacement() const { // The foreign entry of a @dynamicReplacement(for:) of @objc method in a // generic class can't be a dynamic replacement. if (isForeign && hasDecl() && getDecl()->isNativeMethodReplacement()) return false; if (isDistributedThunk()) return false; if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None) return false; if (kind == SILDeclRef::Kind::Destroyer || kind == SILDeclRef::Kind::DefaultArgGenerator) return false; if (kind == SILDeclRef::Kind::Initializer) return isDesignatedConstructorForClass(getDecl()); if (kind == SILDeclRef::Kind::Allocator) return !isDesignatedConstructorForClass(getDecl()); return true; } bool SILDeclRef::isDynamicallyReplaceable() const { // The non-foreign entry of a @dynamicReplacement(for:) of @objc method in a // generic class can't be a dynamically replaced. if (!isForeign && hasDecl() && getDecl()->isNativeMethodReplacement()) return false; if (isDistributedThunk()) return false; if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None) return false; if (kind == SILDeclRef::Kind::DefaultArgGenerator) return false; if (isStoredPropertyInitializer() || isPropertyWrapperBackingInitializer()) return false; // Class allocators are not dynamic replaceable. if (kind == SILDeclRef::Kind::Allocator && isDesignatedConstructorForClass(getDecl())) return false; if (kind == SILDeclRef::Kind::Destroyer || (kind == SILDeclRef::Kind::Initializer && !isDesignatedConstructorForClass(getDecl())) || kind == SILDeclRef::Kind::GlobalAccessor) { return false; } if (!hasDecl()) return false; auto decl = getDecl(); if (isForeign) return false; // We can't generate categories for generic classes. So the standard mechanism // for replacing @objc dynamic methods in generic classes does not work. // Instead we mark the non @objc entry dynamically replaceable and replace // that. // For now, we only support this behavior if -enable-implicit-dynamic is // enabled. return decl->shouldUseNativeMethodReplacement(); } bool SILDeclRef::hasAsync() const { if (isDistributedThunk()) return true; if (hasDecl()) { if (auto afd = dyn_cast(getDecl())) { return afd->hasAsync(); } return false; } return getAbstractClosureExpr()->isBodyAsync(); } bool SILDeclRef::isCalleeAllocatedCoroutine() const { if (!hasDecl()) return false; auto *accessor = dyn_cast(getDecl()); if (!accessor) return false; if (!requiresFeatureCoroutineAccessors(accessor->getAccessorKind())) return false; return getASTContext().SILOpts.CoroutineAccessorsUseYieldOnce2; }