diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index a0e91df7a4c99..f4180b83eabe0 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -171,6 +171,8 @@ types where the metadata itself has unknown layout.) global ::= global 'To' // swift-as-ObjC thunk global ::= global 'TD' // dynamic dispatch thunk global ::= global 'Td' // direct method reference thunk + global ::= global 'TI' // implementation of a dynamic_replaceable function + global ::= global 'TX' // function pointer of a dynamic_replaceable function global ::= entity entity 'TV' // vtable override thunk, derived followed by base global ::= type label-list? 'D' // type mangling for the debugger with label list for function types. global ::= type 'TC' // continuation prototype (not actually used for real symbols) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 8ca8a9acf177b..14384669117f0 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -4230,6 +4230,76 @@ TargetTypeContextDescriptor::getSingletonMetadataInitialization() const } } +/// An entry in the chain of dynamic replacement functions. +struct DynamicReplacementChainEntry { + void *implementationFunction; + DynamicReplacementChainEntry *next; +}; + +/// A record describing the root of dynamic replacements for a function. +struct DynamicReplacementKey { + RelativeDirectPointer root; + uint32_t flags; +}; + +/// A record describing a dynamic function replacement. +class DynamicReplacementDescriptor { + RelativeIndirectablePointer replacedFunctionKey; + RelativeDirectPointer replacementFunction; + RelativeDirectPointer chainEntry; + uint32_t flags; + +public: + /// Enable this replacement by changing the function's replacement chain's + /// root entry. + /// This replacement must be done while holding a global lock that guards this + /// function's chain. Currently this is done by holding the + /// \p DynamicReplacementLock. + void enableReplacement() const; + + /// Disable this replacement by changing the function's replacement chain's + /// root entry. + /// This replacement must be done while holding a global lock that guards this + /// function's chain. Currently this is done by holding the + /// \p DynamicReplacementLock. + void disableReplacement() const; + + uint32_t getFlags() const { return flags; } +}; + +/// A collection of dynamic replacement records. +class DynamicReplacementScope + : private swift::ABI::TrailingObjects { + + uint32_t flags; + uint32_t numReplacements; + + using TrailingObjects = + swift::ABI::TrailingObjects; + friend TrailingObjects; + + ArrayRef getReplacementDescriptors() const { + return {this->template getTrailingObjects(), + numReplacements}; + } + +public: + void enable() const { + for (auto &descriptor : getReplacementDescriptors()) { + descriptor.enableReplacement(); + } + } + + void disable() const { + for (auto &descriptor : getReplacementDescriptors()) { + descriptor.disableReplacement(); + } + } + uint32_t getFlags() { return flags; } +}; + } // end namespace swift #pragma clang diagnostic pop diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index 533aabde34cf6..58f7076b7f0f8 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -378,6 +378,9 @@ SIMPLE_DECL_ATTR(_nonoverride, NonOverride, OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor | OnAssociatedType | UserInaccessible | NotSerialized, 79) +DECL_ATTR(_dynamicReplacement, DynamicReplacement, + OnAbstractFunction | OnVar | OnSubscript | UserInaccessible, + 80) #undef TYPE_ATTR #undef DECL_ATTR_ALIAS diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 417f9249d8db3..5e4ee88112a8a 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -44,6 +44,8 @@ class ASTPrinter; class ASTContext; struct PrintOptions; class Decl; +class AbstractFunctionDecl; +class FuncDecl; class ClassDecl; class GenericFunctionType; class LazyConformanceLoader; @@ -205,6 +207,12 @@ class DeclAttribute : public AttributeBase { Swift3Inferred : 1 ); + SWIFT_INLINE_BITFIELD(DynamicReplacementAttr, DeclAttribute, 1, + /// Whether this attribute has location information that trails the main + /// record, which contains the locations of the parentheses and any names. + HasTrailingLocationInfo : 1 + ); + SWIFT_INLINE_BITFIELD(AbstractAccessControlAttr, DeclAttribute, 3, AccessLevel : 3 ); @@ -893,6 +901,76 @@ class ObjCAttr final : public DeclAttribute, } }; +/// The @_dynamicReplacement(for:) attribute. +class DynamicReplacementAttr final + : public DeclAttribute, + private llvm::TrailingObjects { + friend TrailingObjects; + + DeclName ReplacedFunctionName; + AbstractFunctionDecl *ReplacedFunction; + + /// Create an @_dynamicReplacement(for:) attribute written in the source. + DynamicReplacementAttr(SourceLoc atLoc, SourceRange baseRange, + DeclName replacedFunctionName, SourceRange parenRange); + + explicit DynamicReplacementAttr(DeclName name) + : DeclAttribute(DAK_DynamicReplacement, SourceLoc(), SourceRange(), + /*Implicit=*/false), + ReplacedFunctionName(name), ReplacedFunction(nullptr) { + Bits.DynamicReplacementAttr.HasTrailingLocationInfo = false; + } + + /// Retrieve the trailing location information. + MutableArrayRef getTrailingLocations() { + assert(Bits.DynamicReplacementAttr.HasTrailingLocationInfo); + unsigned length = 2; + return {getTrailingObjects(), length}; + } + + /// Retrieve the trailing location information. + ArrayRef getTrailingLocations() const { + assert(Bits.DynamicReplacementAttr.HasTrailingLocationInfo); + unsigned length = 2; // lParens, rParens + return {getTrailingObjects(), length}; + } + +public: + static DynamicReplacementAttr * + create(ASTContext &Context, SourceLoc AtLoc, SourceLoc DynReplLoc, + SourceLoc LParenLoc, DeclName replacedFunction, SourceLoc RParenLoc); + + static DynamicReplacementAttr *create(ASTContext &ctx, + DeclName replacedFunction); + + static DynamicReplacementAttr *create(ASTContext &ctx, + DeclName replacedFunction, + AbstractFunctionDecl *replacedFuncDecl); + + 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; + + /// Retrieve the location of the closing parentheses, if there is one. + SourceLoc getRParenLoc() const; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_DynamicReplacement; + } +}; + /// Represents any sort of access control modifier. class AbstractAccessControlAttr : public DeclAttribute { protected: diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 8dc885fa6a100..121ea605abbeb 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -2590,6 +2590,10 @@ class ValueDecl : public Decl { /// Is this declaration marked with 'dynamic'? bool isDynamic() const; + bool isObjCDynamic() const { + return isObjC() && isDynamic(); + } + /// Set whether this type is 'dynamic' or not. void setIsDynamic(bool value); diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 2e7c09db45231..7b8b6482f8d08 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -625,6 +625,8 @@ ERROR(expected_sil_rbrace,none, "expected '}' at the end of a sil body", ()) ERROR(expected_sil_function_type, none, "sil function expected to have SIL function type", ()) +ERROR(sil_dynamically_replaced_func_not_found,none, + "dynamically replaced function not found %0", (Identifier)) // SIL Stage ERROR(expected_sil_stage_name, none, @@ -1406,6 +1408,15 @@ ERROR(attr_nskeyedarchiverencodenongenericsubclassesonly_removed, none, "@NSKeyedArchiverEncodeNonGenericSubclassesOnly is no longer necessary", ()) +ERROR(attr_dynamic_replacement_expected_rparen,none, + "expected ')' after function name for @_dynamicReplacement", ()) +ERROR(attr_dynamic_replacement_expected_function,none, + "expected a function name in @_dynamicReplacement(for:)", ()) +ERROR(attr_dynamic_replacement_expected_for,none, + "expected 'for' in '_dynamicReplacement' attribute", ()) +ERROR(attr_dynamic_replacement_expected_colon,none, + "expected ':' after @_dynamicReplacement(for", ()) + // opened ERROR(opened_attribute_expected_lparen,none, "expected '(' after 'opened' attribute", ()) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 7f7e7efa4422d..a70e1caa1892f 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3878,10 +3878,37 @@ ERROR(dynamic_with_final,none, ERROR(dynamic_with_nonobjc,none, "a declaration cannot be both '@nonobjc' and 'dynamic'", ()) +ERROR(dynamic_with_transparent,none, + "a declaration cannot be both '@_tranparent' and 'dynamic'", + ()) ERROR(dynamic_requires_objc,none, "'dynamic' %0 %1 must also be '@objc'", (DescriptiveDeclKind, DeclName)) +//------------------------------------------------------------------------------ +// MARK: @_dynamicReplacement(for:) +//------------------------------------------------------------------------------ + +ERROR(dynamic_replacement_accessor_type_mismatch, none, + "replaced accessor %0's type does not match", (DeclName)) +ERROR(dynamic_replacement_accessor_not_dynamic, none, + "replaced accessor for %0 is not marked dynamic", (DeclName)) +ERROR(dynamic_replacement_accessor_not_explicit, none, + "replaced accessor %select{get|set|_read|_modify|willSet|didSet|unsafeAddress|addressWithOwner|addressWithNativeOwner|unsafeMutableAddress|mutableAddressWithOwner|}0 for %1 is not explicitly defined", + (unsigned, DeclName)) +ERROR(dynamic_replacement_function_not_dynamic, none, + "replaced function %0 is not marked dynamic", (DeclName)) +ERROR(dynamic_replacement_function_not_found, none, + "replaced function %0 could not be found", (DeclName)) +ERROR(dynamic_replacement_accessor_not_found, none, + "replaced accessor for %0 could not be found", (DeclName)) +ERROR(dynamic_replacement_function_of_type_not_found, none, + "replaced function %0 of type %1 could not be found", (DeclName, Type)) +NOTE(dynamic_replacement_found_function_of_type, none, + "found function %0 of type %1", (DeclName, Type)) +ERROR(dynamic_replacement_not_in_extension, none, + "dynamicReplacement(for:) of %0 is not defined in an extension or at the file level", (DeclName)) + //------------------------------------------------------------------------------ // MARK: @available //------------------------------------------------------------------------------ diff --git a/include/swift/AST/StorageImpl.h b/include/swift/AST/StorageImpl.h index 5dbb783c8bca5..7ab423a10fbac 100644 --- a/include/swift/AST/StorageImpl.h +++ b/include/swift/AST/StorageImpl.h @@ -160,8 +160,12 @@ class AccessStrategy { Kind getKind() const { return TheKind; } + bool hasAccessor() const { + return TheKind == DirectToAccessor || TheKind == DispatchToAccessor; + } + AccessorKind getAccessor() const { - assert(TheKind == DirectToAccessor || TheKind == DispatchToAccessor); + assert(hasAccessor()); return FirstAccessor; } diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index db2cbc5c56f59..1e6ac7f43db2e 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -144,6 +144,10 @@ namespace swift { /// was not compiled with -enable-testing. bool EnableTestableAttrRequiresTestableModule = true; + /// If true, the 'dynamic' attribute is added to all applicable + /// declarations. + bool EnableImplicitDynamic = false; + /// /// Flags for developers /// diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index 10ac2d9c7ec5a..ca4dda0ac66e7 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -71,6 +71,9 @@ NODE(Directness) NODE(DynamicAttribute) NODE(DirectMethodReferenceAttribute) NODE(DynamicSelf) +NODE(DynamicallyReplaceableFunctionImpl) +NODE(DynamicallyReplaceableFunctionKey) +NODE(DynamicallyReplaceableFunctionVar) CONTEXT_NODE(Enum) NODE(EnumCase) NODE(ErrorType) diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 8658c7855099f..1c7a392803688 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -103,6 +103,10 @@ class LinkEntity { // This field appears in associated conformance access functions. AssociatedConformanceIndexShift = 8, AssociatedConformanceIndexMask = ~KindMask, + + // This field appears in SILFunction. + IsDynamicallyReplaceableImplShift = 8, + IsDynamicallyReplaceableImplMask = ~KindMask, }; #define LINKENTITY_SET_FIELD(field, value) (value << field##Shift) #define LINKENTITY_GET_FIELD(value, field) ((value & field##Mask) >> field##Shift) @@ -226,6 +230,20 @@ class LinkEntity { /// is stored in the data. DefaultAssociatedConformanceAccessor, + /// A global function pointer for dynamically replaceable functions. + /// The pointer is a AbstractStorageDecl*. + DynamicallyReplaceableFunctionVariableAST, + + /// The pointer is a AbstractStorageDecl*. + DynamicallyReplaceableFunctionKeyAST, + + /// The original implementation of a dynamically replaceable function. + /// The pointer is a AbstractStorageDecl*. + DynamicallyReplaceableFunctionImpl, + + /// The pointer is a SILFunction*. + DynamicallyReplaceableFunctionKey, + /// A SIL function. The pointer is a SILFunction*. SILFunction, @@ -315,6 +333,9 @@ class LinkEntity { /// A coroutine continuation prototype function. CoroutineContinuationPrototype, + + /// A global function pointer for dynamically replaceable functions. + DynamicallyReplaceableFunctionVariable, }; friend struct llvm::DenseMapInfo; @@ -323,7 +344,7 @@ class LinkEntity { } static bool isDeclKind(Kind k) { - return k <= Kind::DefaultAssociatedConformanceAccessor; + return k <= Kind::DynamicallyReplaceableFunctionImpl; } static bool isTypeKind(Kind k) { return k >= Kind::ProtocolWitnessTableLazyAccessFunction; @@ -705,12 +726,15 @@ class LinkEntity { return entity; } - static LinkEntity forSILFunction(SILFunction *F) - { + static LinkEntity + forSILFunction(SILFunction *F, bool IsDynamicallyReplaceableImplementation) { LinkEntity entity; entity.Pointer = F; entity.SecondaryPointer = nullptr; - entity.Data = LINKENTITY_SET_FIELD(Kind, unsigned(Kind::SILFunction)); + entity.Data = + LINKENTITY_SET_FIELD(Kind, unsigned(Kind::SILFunction)) | + LINKENTITY_SET_FIELD(IsDynamicallyReplaceableImpl, + (unsigned)IsDynamicallyReplaceableImplementation); return entity; } @@ -837,6 +861,45 @@ class LinkEntity { return entity; } + static LinkEntity forDynamicallyReplaceableFunctionVariable(SILFunction *F) { + LinkEntity entity; + entity.Pointer = F; + entity.SecondaryPointer = nullptr; + entity.Data = LINKENTITY_SET_FIELD( + Kind, unsigned(Kind::DynamicallyReplaceableFunctionVariable)); + return entity; + } + + static LinkEntity + forDynamicallyReplaceableFunctionVariable(AbstractFunctionDecl *decl) { + LinkEntity entity; + entity.setForDecl(Kind::DynamicallyReplaceableFunctionVariableAST, decl); + return entity; + } + + static LinkEntity forDynamicallyReplaceableFunctionKey(SILFunction *F) { + LinkEntity entity; + entity.Pointer = F; + entity.SecondaryPointer = nullptr; + entity.Data = LINKENTITY_SET_FIELD( + Kind, unsigned(Kind::DynamicallyReplaceableFunctionKey)); + return entity; + } + + static LinkEntity + forDynamicallyReplaceableFunctionKey(AbstractFunctionDecl *decl) { + LinkEntity entity; + entity.setForDecl(Kind::DynamicallyReplaceableFunctionKeyAST, decl); + return entity; + } + + static LinkEntity + forDynamicallyReplaceableFunctionImpl(AbstractFunctionDecl *decl) { + LinkEntity entity; + entity.setForDecl(Kind::DynamicallyReplaceableFunctionImpl, decl); + return entity; + } + void mangle(llvm::raw_ostream &out) const; void mangle(SmallVectorImpl &buffer) const; std::string mangleAsString() const; @@ -863,7 +926,9 @@ class LinkEntity { } SILFunction *getSILFunction() const { - assert(getKind() == Kind::SILFunction); + assert(getKind() == Kind::SILFunction || + getKind() == Kind::DynamicallyReplaceableFunctionVariable || + getKind() == Kind::DynamicallyReplaceableFunctionKey); return reinterpret_cast(Pointer); } @@ -899,7 +964,10 @@ class LinkEntity { assert(getKind() == Kind::AssociatedTypeWitnessTableAccessFunction); return reinterpret_cast(Pointer); } - + bool isDynamicallyReplaceable() const { + assert(getKind() == Kind::SILFunction); + return LINKENTITY_GET_FIELD(Data, IsDynamicallyReplaceableImpl); + } bool isValueWitness() const { return getKind() == Kind::ValueWitness; } CanType getType() const { assert(isTypeKind(getKind())); diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index ea4dc3ac8ca1d..9552b25ad0288 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -354,6 +354,10 @@ def disable_swift3_objc_inference : Flags<[FrontendOption, HelpHidden]>, HelpText<"Disable Swift 3's @objc inference rules for NSObject-derived classes and 'dynamic' members (emulates Swift 4 behavior)">; +def enable_implicit_dynamic : Flag<["-"], "enable-implicit-dynamic">, + Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>, + HelpText<"Add 'dynamic' to all declarations">; + def enable_nskeyedarchiver_diagnostics : Flag<["-"], "enable-nskeyedarchiver-diagnostics">, HelpText<"Diagnose classes with unstable mangled names adopting NSCoding">; diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index fce560c965923..eb24d7a4d7940 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1247,7 +1247,8 @@ class Parser { DeclName parseUnqualifiedDeclName(bool afterDot, DeclNameLoc &loc, const Diagnostic &diag, bool allowOperators=false, - bool allowZeroArgCompoundNames=false); + bool allowZeroArgCompoundNames=false, + bool allowDeinitAndSubscript=false); Expr *parseExprIdentifier(); Expr *parseExprEditorPlaceholder(Token PlaceholderTok, diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index 92d7f09b1f7aa..d877e92789799 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -130,6 +130,14 @@ void swift_abortUnownedRetainOverflow(); SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE void swift_abortWeakRetainOverflow(); +// Halt due to enabling an already enabled dynamic replacement(). +SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE +void swift_abortDynamicReplacementEnabling(); + +// Halt due to disabling an already disabled dynamic replacement(). +SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE +void swift_abortDynamicReplacementDisabling(); + /// This function dumps one line of a stack trace. It is assumed that \p framePC /// is the address of the stack frame at index \p index. If \p shortOutput is /// true, this functions prints only the name of the symbol and offset, ignores diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index d8643ba454727..9c2deaa3e88fa 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -788,6 +788,12 @@ const TypeContextDescriptor *swift_getTypeContextDescriptor(const Metadata *type SWIFT_RUNTIME_EXPORT const HeapObject *swift_getKeyPath(const void *pattern, const void *arguments); +SWIFT_RUNTIME_EXPORT +void swift_enableDynamicReplacementScope(const DynamicReplacementScope *scope); + +SWIFT_RUNTIME_EXPORT +void swift_disableDynamicReplacementScope(const DynamicReplacementScope *scope); + } // end namespace swift #endif // SWIFT_RUNTIME_METADATA_H diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index 46dc7aa9ec389..d1686d7cbe867 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -147,6 +147,14 @@ class ApplySite { FOREACH_IMPL_RETURN(getReferencedFunction()); } + /// Should we optimize this call. + /// Calls to (previous_)dynamic_function_ref have a dynamic target function so + /// we should not optimize them. + bool canOptimize() const { + return !DynamicFunctionRefInst::classof(getCallee()) && + !PreviousDynamicFunctionRefInst::classof(getCallee()); + } + /// Return the type. SILType getType() const { return getSubstCalleeConv().getSILResultType(); } diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 3e97c08302bb7..ce3810bc27f7d 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -556,10 +556,43 @@ class SILBuilder { return createBuiltinBinaryFunction(Loc, Name, OpdTy, SILResultTy, Args); } + // Creates a dynamic_function_ref or function_ref depending on whether f is + // dynamically_replaceable. + FunctionRefBaseInst *createFunctionRefFor(SILLocation Loc, SILFunction *f) { + if (f->isDynamicallyReplaceable()) + return createDynamicFunctionRef(Loc, f); + else return createFunctionRef(Loc, f); + } + + FunctionRefBaseInst *createFunctionRef(SILLocation Loc, SILFunction *f, + SILInstructionKind kind) { + if (kind == SILInstructionKind::FunctionRefInst) + return createFunctionRef(Loc, f); + else if (kind == SILInstructionKind::DynamicFunctionRefInst) + return createDynamicFunctionRef(Loc, f); + else if (kind == SILInstructionKind::PreviousDynamicFunctionRefInst) + return createPreviousDynamicFunctionRef(Loc, f); + assert(false && "Should not get here"); + return nullptr; + } + FunctionRefInst *createFunctionRef(SILLocation Loc, SILFunction *f) { return insert(new (getModule()) FunctionRefInst(getSILDebugLocation(Loc), f)); } + + DynamicFunctionRefInst * + createDynamicFunctionRef(SILLocation Loc, SILFunction *f) { + return insert(new (getModule()) DynamicFunctionRefInst( + getSILDebugLocation(Loc), f)); + } + + PreviousDynamicFunctionRefInst * + createPreviousDynamicFunctionRef(SILLocation Loc, SILFunction *f) { + return insert(new (getModule()) PreviousDynamicFunctionRefInst( + getSILDebugLocation(Loc), f)); + } + AllocGlobalInst *createAllocGlobal(SILLocation Loc, SILGlobalVariable *g) { return insert(new (getModule()) AllocGlobalInst(getSILDebugLocation(Loc), g)); diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 7918bfaffcd92..78cc0440d6d33 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -913,6 +913,24 @@ SILCloner::visitFunctionRefInst(FunctionRefInst *Inst) { getOpLocation(Inst->getLoc()), OpFunction)); } +template +void +SILCloner::visitDynamicFunctionRefInst(DynamicFunctionRefInst *Inst) { + SILFunction *OpFunction = getOpFunction(Inst->getReferencedFunction()); + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction(Inst, getBuilder().createDynamicFunctionRef( + getOpLocation(Inst->getLoc()), OpFunction)); +} + +template +void SILCloner::visitPreviousDynamicFunctionRefInst( + PreviousDynamicFunctionRefInst *Inst) { + SILFunction *OpFunction = getOpFunction(Inst->getReferencedFunction()); + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + recordClonedInstruction(Inst, getBuilder().createPreviousDynamicFunctionRef( + getOpLocation(Inst->getLoc()), OpFunction)); +} + template void SILCloner::visitAllocGlobalInst(AllocGlobalInst *Inst) { diff --git a/include/swift/SIL/SILDeclRef.h b/include/swift/SIL/SILDeclRef.h index e249e3fb17c84..b513fac845385 100644 --- a/include/swift/SIL/SILDeclRef.h +++ b/include/swift/SIL/SILDeclRef.h @@ -252,6 +252,12 @@ struct SILDeclRef { return kind == Kind::IVarInitializer || kind == Kind::IVarDestroyer; } + /// True if the SILDeclRef references an allocating or deallocating entry + /// point. + bool isInitializerOrDestroyer() const { + return kind == Kind::Initializer || kind == Kind::Destroyer; + } + /// \brief True if the function should be treated as transparent. bool isTransparent() const; /// \brief True if the function should have its body serialized. @@ -384,6 +390,8 @@ struct SILDeclRef { /// subclassed. SubclassScope getSubclassScope() const; + bool isDynamicallyReplaceable() const; + private: friend struct llvm::DenseMapInfo; /// Produces a SILDeclRef from an opaque value. diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 981e49a149883..9157fb407a32b 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -47,6 +47,10 @@ enum IsThunk_t { IsReabstractionThunk, IsSignatureOptimizedThunk }; +enum IsDynamicallyReplaceable_t { + IsNotDynamic, + IsDynamic +}; class SILSpecializeAttr final { friend SILFunction; @@ -145,6 +149,12 @@ class SILFunction /// disabled. SILProfiler *Profiler = nullptr; + /// The function this function is meant to replace. Null if this is not a + /// @_dynamicReplacement(for:) function. + SILFunction *ReplacedFunction = nullptr; + + Identifier ObjCReplacementFor; + /// The function's bare attribute. Bare means that the function is SIL-only /// and does not require debug info. unsigned Bare : 1; @@ -182,6 +192,9 @@ class SILFunction /// Whether cross-module references to this function should use weak linking. unsigned IsWeakLinked : 1; + // Whether the implementation can be dynamically replaced. + unsigned IsDynamicReplaceable : 1; + /// If != OptimizationMode::NotSet, the optimization mode specified with an /// function attribute. OptimizationMode OptMode; @@ -273,14 +286,16 @@ class SILFunction ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope classSubclassScope, Inline_t inlineStrategy, EffectsKind E, SILFunction *insertBefore, - const SILDebugScope *debugScope); + const SILDebugScope *debugScope, + IsDynamicallyReplaceable_t isDynamic); static SILFunction * create(SILModule &M, SILLinkage linkage, StringRef name, CanSILFunctionType loweredType, GenericEnvironment *genericEnv, Optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, IsSerialized_t isSerialized, - ProfileCounter entryCount, IsThunk_t isThunk = IsNotThunk, + ProfileCounter entryCount, IsDynamicallyReplaceable_t isDynamic, + IsThunk_t isThunk = IsNotThunk, SubclassScope classSubclassScope = SubclassScope::NotApplicable, Inline_t inlineStrategy = InlineDefault, EffectsKind EffectsKindAttr = EffectsKind::Unspecified, @@ -304,6 +319,38 @@ class SILFunction SILProfiler *getProfiler() const { return Profiler; } + SILFunction *getDynamicallyReplacedFunction() const { + return ReplacedFunction; + } + void setDynamicallyReplacedFunction(SILFunction *f) { + assert(ReplacedFunction == nullptr && "already set"); + assert(!hasObjCReplacement()); + + if (f == nullptr) + return; + ReplacedFunction = f; + ReplacedFunction->incrementRefCount(); + } + + /// This function should only be called when SILFunctions are bulk deleted. + void dropDynamicallyReplacedFunction() { + if (!ReplacedFunction) + return; + ReplacedFunction->decrementRefCount(); + ReplacedFunction = nullptr; + } + + bool hasObjCReplacement() const { + return !ObjCReplacementFor.empty(); + } + + Identifier getObjCReplacement() const { + return ObjCReplacementFor; + } + + void setObjCReplacement(AbstractFunctionDecl *replacedDecl); + void setObjCReplacement(Identifier replacedDecl); + void setProfiler(SILProfiler *InheritedProfiler) { assert(!Profiler && "Function already has a profiler"); Profiler = InheritedProfiler; @@ -517,6 +564,16 @@ class SILFunction IsWeakLinked = value; } + /// Returs whether this function implementation can be dynamically replaced. + IsDynamicallyReplaceable_t isDynamicallyReplaceable() const { + return IsDynamicallyReplaceable_t(IsDynamicReplaceable); + } + void setIsDynamic(IsDynamicallyReplaceable_t value = IsDynamic) { + assert(IsDynamicReplaceable == IsNotDynamic && "already set"); + IsDynamicReplaceable = value; + assert(!Transparent || !IsDynamicReplaceable); + } + /// Get the DeclContext of this function. (Debug info only). DeclContext *getDeclContext() const { return getLocation().getAsDeclContext(); @@ -619,7 +676,10 @@ class SILFunction /// Get this function's transparent attribute. IsTransparent_t isTransparent() const { return IsTransparent_t(Transparent); } - void setTransparent(IsTransparent_t isT) { Transparent = isT; } + void setTransparent(IsTransparent_t isT) { + Transparent = isT; + assert(!Transparent || !IsDynamicReplaceable); + } /// Get this function's serialized attribute. IsSerialized_t isSerialized() const { return IsSerialized_t(Serialized); } diff --git a/include/swift/SIL/SILFunctionBuilder.h b/include/swift/SIL/SILFunctionBuilder.h index 40a8cf7924803..1a587ca362573 100644 --- a/include/swift/SIL/SILFunctionBuilder.h +++ b/include/swift/SIL/SILFunctionBuilder.h @@ -58,13 +58,15 @@ class SILFunctionBuilder { IsTransparent_t isTransparent, IsSerialized_t isSerialized, ProfileCounter entryCount, - IsThunk_t isThunk); + IsThunk_t isThunk, + IsDynamicallyReplaceable_t isDynamic); /// Return the declaration of a function, or create it if it doesn't exist. SILFunction *getOrCreateFunction( SILLocation loc, StringRef name, SILLinkage linkage, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, IsSerialized_t isSerialized, + IsDynamicallyReplaceable_t isDynamic, ProfileCounter entryCount = ProfileCounter(), IsThunk_t isThunk = IsNotThunk, SubclassScope subclassScope = SubclassScope::NotApplicable); @@ -85,6 +87,7 @@ class SILFunctionBuilder { CanSILFunctionType loweredType, GenericEnvironment *genericEnv, Optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, IsSerialized_t isSerialized, + IsDynamicallyReplaceable_t isDynamic, ProfileCounter entryCount = ProfileCounter(), IsThunk_t isThunk = IsNotThunk, SubclassScope subclassScope = SubclassScope::NotApplicable, @@ -92,6 +95,9 @@ class SILFunctionBuilder { EffectsKind EK = EffectsKind::Unspecified, SILFunction *InsertBefore = nullptr, const SILDebugScope *DebugScope = nullptr); + + void addFunctionAttributes(SILFunction *F, DeclAttributes &Attrs, + SILModule &M, SILDeclRef constant = SILDeclRef()); }; } // namespace swift diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 393f9912addb1..a47225b0caf32 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -70,7 +70,7 @@ class Stmt; class StringLiteralExpr; class ValueDecl; class VarDecl; -class FunctionRefInst; +class FunctionRefBaseInst; template class SILClonerWithScopes; @@ -1786,7 +1786,7 @@ class ApplyInstBase : public Base { /// Gets the referenced function if the callee is a function_ref instruction. SILFunction *getReferencedFunction() const { - if (auto *FRI = dyn_cast(getCallee())) + if (auto *FRI = dyn_cast(getCallee())) return FRI->getReferencedFunction(); return nullptr; } @@ -2214,24 +2214,18 @@ class LiteralInst : public SingleValueInstruction { DEFINE_ABSTRACT_SINGLE_VALUE_INST_BOILERPLATE(LiteralInst) }; -/// FunctionRefInst - Represents a reference to a SIL function. -class FunctionRefInst - : public InstructionBase { - friend SILBuilder; +class FunctionRefBaseInst : public LiteralInst { + SILFunction *f; - SILFunction *Function; - /// Construct a FunctionRefInst. - /// - /// \param DebugLoc The location of the reference. - /// \param F The function being referenced. - FunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); +protected: + FunctionRefBaseInst(SILInstructionKind Kind, SILDebugLocation DebugLoc, + SILFunction *F); public: - ~FunctionRefInst(); + ~FunctionRefBaseInst(); /// Return the referenced function. - SILFunction *getReferencedFunction() const { return Function; } + SILFunction *getReferencedFunction() const { return f; } void dropReferencedFunction(); @@ -2244,6 +2238,73 @@ class FunctionRefInst ArrayRef getAllOperands() const { return {}; } MutableArrayRef getAllOperands() { return {}; } + + static bool classof(const SILNode *node) { + return (node->getKind() == SILNodeKind::FunctionRefInst || + node->getKind() == SILNodeKind::DynamicFunctionRefInst || + node->getKind() == SILNodeKind::PreviousDynamicFunctionRefInst); + } + static bool classof(const SingleValueInstruction *node) { + return (node->getKind() == SILInstructionKind::FunctionRefInst || + node->getKind() == SILInstructionKind::DynamicFunctionRefInst || + node->getKind() == SILInstructionKind::PreviousDynamicFunctionRefInst); + } +}; + +/// FunctionRefInst - Represents a reference to a SIL function. +class FunctionRefInst : public FunctionRefBaseInst { + friend SILBuilder; + + /// Construct a FunctionRefInst. + /// + /// \param DebugLoc The location of the reference. + /// \param F The function being referenced. + FunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + +public: + static bool classof(const SILNode *node) { + return node->getKind() == SILNodeKind::FunctionRefInst; + } + static bool classof(const SingleValueInstruction *node) { + return node->getKind() == SILInstructionKind::FunctionRefInst; + } +}; + +class DynamicFunctionRefInst : public FunctionRefBaseInst { + friend SILBuilder; + + /// Construct a DynamicFunctionRefInst. + /// + /// \param DebugLoc The location of the reference. + /// \param F The function being referenced. + DynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + +public: + static bool classof(const SILNode *node) { + return node->getKind() == SILNodeKind::DynamicFunctionRefInst; + } + static bool classof(const SingleValueInstruction *node) { + return node->getKind() == SILInstructionKind::DynamicFunctionRefInst; + } +}; + +class PreviousDynamicFunctionRefInst : public FunctionRefBaseInst { + friend SILBuilder; + + /// Construct a PreviousDynamicFunctionRefInst. + /// + /// \param DebugLoc The location of the reference. + /// \param F The function being referenced. + PreviousDynamicFunctionRefInst(SILDebugLocation DebugLoc, SILFunction *F); + +public: + static bool classof(const SILNode *node) { + return node->getKind() == SILNodeKind::PreviousDynamicFunctionRefInst; + } + static bool classof(const SingleValueInstruction *node) { + return node->getKind() == + SILInstructionKind::PreviousDynamicFunctionRefInst; + } }; /// Component of a KeyPathInst. @@ -7530,6 +7591,9 @@ SILFunction *ApplyInstBase::getCalleeFunction() const { SILValue Callee = getCalleeOrigin(); while (true) { + // Intentionally don't lookup throught dynamic_function_ref and + // previous_dynamic_function_ref as the target of those functions is not + // statically known. if (auto *FRI = dyn_cast(Callee)) return FRI->getReferencedFunction(); diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index 977ab8df05c7a..db2ddaf68f167 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -402,6 +402,10 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction) ABSTRACT_SINGLE_VALUE_INST(LiteralInst, SingleValueInstruction) SINGLE_VALUE_INST(FunctionRefInst, function_ref, LiteralInst, None, DoesNotRelease) + SINGLE_VALUE_INST(DynamicFunctionRefInst, dynamic_function_ref, + LiteralInst, None, DoesNotRelease) + SINGLE_VALUE_INST(PreviousDynamicFunctionRefInst, prev_dynamic_function_ref, + LiteralInst, None, DoesNotRelease) SINGLE_VALUE_INST(GlobalAddrInst, global_addr, LiteralInst, None, DoesNotRelease) SINGLE_VALUE_INST(GlobalValueInst, global_value, diff --git a/include/swift/SIL/TypeSubstCloner.h b/include/swift/SIL/TypeSubstCloner.h index 014e5d0e1eb5a..22adf0c8ac7b4 100644 --- a/include/swift/SIL/TypeSubstCloner.h +++ b/include/swift/SIL/TypeSubstCloner.h @@ -353,8 +353,9 @@ class TypeSubstCloner : public SILClonerWithScopes { ParentFunction = FuncBuilder.getOrCreateFunction( ParentFunction->getLocation(), MangledName, SILLinkage::Shared, ParentFunction->getLoweredFunctionType(), ParentFunction->isBare(), - ParentFunction->isTransparent(), ParentFunction->isSerialized(), 0, - ParentFunction->isThunk(), ParentFunction->getClassSubclassScope()); + ParentFunction->isTransparent(), ParentFunction->isSerialized(), + IsNotDynamic, 0, ParentFunction->isThunk(), + ParentFunction->getClassSubclassScope()); // Increment the ref count for the inlined function, so it doesn't // get deleted before we can emit abstract debug info for it. if (!ParentFunction->isZombie()) { diff --git a/include/swift/SILOptimizer/Utils/Local.h b/include/swift/SILOptimizer/Utils/Local.h index 2ead78178e582..f5cb3dc41cf8c 100644 --- a/include/swift/SILOptimizer/Utils/Local.h +++ b/include/swift/SILOptimizer/Utils/Local.h @@ -640,7 +640,8 @@ struct LLVM_LIBRARY_VISIBILITY FindLocalApplySitesResult { /// /// 1. We discovered that the function_ref never escapes. /// 2. We were able to find either a partial apply or a full apply site. -Optional findLocalApplySites(FunctionRefInst *FRI); +Optional +findLocalApplySites(FunctionRefBaseInst *FRI); } // end namespace swift diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index 88f55425c7f20..c8d9f8f0e1dfc 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/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 = 458; // Last change: enrich FILE_DEPENDENCY records. +const uint16_t SWIFTMODULE_VERSION_MINOR = 462; // Last change: Add dynamicReplacement(for:) using DeclIDField = BCFixed<31>; @@ -1576,6 +1576,14 @@ namespace decls_block { >; #include "swift/AST/Attr.def" + using DynamicReplacementDeclAttrLayout = BCRecordLayout< + DynamicReplacement_DECL_ATTR, + BCFixed<1>, // implicit flag + DeclIDField, // replaced function + BCVBR<4>, // # of arguments (+1) or zero if no name + BCArray + >; + } /// Returns the encoding kind for the given decl. diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 288b36c1db81f..65166fcdc83ea 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -770,6 +770,12 @@ namespace { OS << " @objc"; if (VD->isDynamic()) OS << " dynamic"; + if (auto *attr = + VD->getAttrs().getAttribute()) { + OS << " @_dynamicReplacement(for: \""; + OS << attr->getReplacedFunctionName(); + OS << "\")"; + } } void printCommon(NominalTypeDecl *NTD, const char *Name, diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index e289c9442099c..1e947be19f457 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -3076,10 +3076,29 @@ class Verifier : public ASTWalker { Out << "Property and accessor do not match for 'final'\n"; abort(); } - if (FD->isDynamic() != storageDecl->isDynamic()) { + if (FD->isDynamic() != storageDecl->isDynamic() && + // We allow a non dynamic setter if there is a dynamic modify, + // observer, or mutable addressor. + !(FD->isSetter() && + (storageDecl->getWriteImpl() == WriteImplKind::Modify || + storageDecl->getWriteImpl() == + WriteImplKind::StoredWithObservers || + storageDecl->getWriteImpl() == WriteImplKind::MutableAddress) && + storageDecl->isDynamic() && !storageDecl->isObjC()) && + // We allow a non dynamic getter if there is a dynamic read. + !(FD->isGetter() && + (storageDecl->getReadImpl() == ReadImplKind::Read || + storageDecl->getReadImpl() == ReadImplKind::Address) && + storageDecl->isDynamic() && !storageDecl->isObjC())) { Out << "Property and accessor do not match for 'dynamic'\n"; abort(); } + if (FD->isDynamic()) { + if (FD->isObjC() != storageDecl->isObjC()) { + Out << "Property and accessor do not match for '@objc'\n"; + abort(); + } + } } auto storedAccessor = storageDecl->getAccessor(FD->getAccessorKind()); diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 23a231a32d4ea..e6d61330e5aff 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -546,6 +546,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } + case DAK_DynamicReplacement: { + Printer.printAttrName("@_dynamicReplacement(for: \""); + auto *attr = cast(this); + Printer << attr->getReplacedFunctionName() << "\")"; + break; + } + case DAK_Count: llvm_unreachable("exceed declaration attribute kinds"); @@ -612,6 +619,8 @@ StringRef DeclAttribute::getAttrName() const { case DAK_ObjC: case DAK_ObjCRuntimeName: return "objc"; + case DAK_DynamicReplacement: + return "_dynamicReplacement"; case DAK_RestatedObjCConformance: return "_restatedObjCConformance"; case DAK_Inline: { @@ -776,6 +785,50 @@ ObjCAttr *ObjCAttr::clone(ASTContext &context) const { return attr; } +DynamicReplacementAttr::DynamicReplacementAttr(SourceLoc atLoc, + SourceRange baseRange, + DeclName name, + SourceRange parenRange) + : DeclAttribute(DAK_DynamicReplacement, atLoc, baseRange, + /*Implicit=*/false), + ReplacedFunctionName(name), ReplacedFunction(nullptr) { + Bits.DynamicReplacementAttr.HasTrailingLocationInfo = true; + getTrailingLocations()[0] = parenRange.Start; + getTrailingLocations()[1] = parenRange.End; +} + +DynamicReplacementAttr * +DynamicReplacementAttr::create(ASTContext &Ctx, SourceLoc AtLoc, + SourceLoc DynReplLoc, SourceLoc LParenLoc, + DeclName ReplacedFunction, SourceLoc RParenLoc) { + void *mem = Ctx.Allocate(totalSizeToAlloc(2), + alignof(DynamicReplacementAttr)); + return new (mem) DynamicReplacementAttr( + AtLoc, SourceRange(DynReplLoc, RParenLoc), ReplacedFunction, + SourceRange(LParenLoc, RParenLoc)); +} + +DynamicReplacementAttr *DynamicReplacementAttr::create(ASTContext &Ctx, + DeclName name) { + return new (Ctx) DynamicReplacementAttr(name); +} + +DynamicReplacementAttr * +DynamicReplacementAttr::create(ASTContext &Ctx, DeclName name, + AbstractFunctionDecl *f) { + auto res = new (Ctx) DynamicReplacementAttr(name); + res->setReplacedFunction(f); + return res; +} + +SourceLoc DynamicReplacementAttr::getLParenLoc() const { + return getTrailingLocations()[0]; +} + +SourceLoc DynamicReplacementAttr::getRParenLoc() const { + return getTrailingLocations()[1]; +} + AvailableAttr * AvailableAttr::createPlatformAgnostic(ASTContext &C, StringRef Message, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 5596f1b7dbdcd..066f7c32cd82f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1419,6 +1419,7 @@ static bool isPolymorphic(const AbstractStorageDecl *storage) { if (storage->isDynamic()) return true; + // Imported declarations behave like they are dynamic, even if they're // not marked as such explicitly. if (storage->isObjC() && storage->hasClangNode()) @@ -1534,6 +1535,11 @@ getDirectWriteAccessStrategy(const AbstractStorageDecl *storage) { llvm_unreachable("bad impl kind"); } +static AccessStrategy +getOpaqueReadAccessStrategy(const AbstractStorageDecl *storage, bool dispatch); +static AccessStrategy +getOpaqueWriteAccessStrategy(const AbstractStorageDecl *storage, bool dispatch); + static AccessStrategy getDirectReadWriteAccessStrategy(const AbstractStorageDecl *storage) { switch (storage->getReadWriteImpl()) { @@ -1541,8 +1547,14 @@ getDirectReadWriteAccessStrategy(const AbstractStorageDecl *storage) { assert(isa(storage) && cast(storage)->isLet() && "mutation of a immutable variable that isn't a let"); return AccessStrategy::getStorage(); - case ReadWriteImplKind::Stored: + case ReadWriteImplKind::Stored: { + // If the storage isDynamic (and not @objc) use the accessors. + if (storage->isDynamic() && !storage->isObjC()) + return AccessStrategy::getMaterializeToTemporary( + getOpaqueReadAccessStrategy(storage, false), + getOpaqueWriteAccessStrategy(storage, false)); return AccessStrategy::getStorage(); + } case ReadWriteImplKind::MutableAddress: return AccessStrategy::getAccessor(AccessorKind::MutableAddress, /*dispatch*/ false); @@ -1698,7 +1710,7 @@ bool AbstractStorageDecl::requiresOpaqueModifyCoroutine() const { // Dynamic storage suppresses the modify coroutine. // If we add a Swift-native concept of `dynamic`, this should be restricted // to the ObjC-supported concept. - if (isDynamic()) + if (isObjCDynamic()) return false; // Requirements of ObjC protocols don't support the modify coroutine. @@ -5293,7 +5305,7 @@ static bool requiresNewVTableEntry(const AbstractFunctionDecl *decl) { // Final members are always be called directly. // Dynamic methods are always accessed by objc_msgSend(). - if (decl->isFinal() || decl->isDynamic() || decl->hasClangNode()) + if (decl->isFinal() || decl->isObjCDynamic() || decl->hasClangNode()) return false; // Initializers are not normally inherited, but required initializers can @@ -5316,7 +5328,8 @@ static bool requiresNewVTableEntry(const AbstractFunctionDecl *decl) { } auto base = decl->getOverriddenDecl(); - if (!base || base->hasClangNode() || base->isDynamic()) + + if (!base || base->hasClangNode() || base->isObjCDynamic()) return true; // As above, convenience initializers are not formally overridable in Swift diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 7aff1bf6238a5..74422f893638f 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -111,6 +111,9 @@ static bool isFunctionAttr(Node::Kind kind) { case Node::Kind::OutlinedVariable: case Node::Kind::OutlinedBridgedMethod: case Node::Kind::MergedFunction: + case Node::Kind::DynamicallyReplaceableFunctionImpl: + case Node::Kind::DynamicallyReplaceableFunctionKey: + case Node::Kind::DynamicallyReplaceableFunctionVar: return true; default: return false; @@ -1933,6 +1936,9 @@ NodePointer Demangler::demangleThunkOrSpecialization() { case 'a': return createNode(Node::Kind::PartialApplyObjCForwarder); case 'A': return createNode(Node::Kind::PartialApplyForwarder); case 'm': return createNode(Node::Kind::MergedFunction); + case 'X': return createNode(Node::Kind::DynamicallyReplaceableFunctionVar); + case 'x': return createNode(Node::Kind::DynamicallyReplaceableFunctionKey); + case 'I': return createNode(Node::Kind::DynamicallyReplaceableFunctionImpl); case 'C': { NodePointer type = popNode(Node::Kind::Type); return createWithChild(Node::Kind::CoroutineContinuationPrototype, type); diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index add5341d8c619..311524c6dcc5c 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -493,6 +493,9 @@ class NodePrinter { case Node::Kind::DependentProtocolConformanceInherited: case Node::Kind::DependentProtocolConformanceRoot: case Node::Kind::ProtocolConformanceRef: + case Node::Kind::DynamicallyReplaceableFunctionKey: + case Node::Kind::DynamicallyReplaceableFunctionImpl: + case Node::Kind::DynamicallyReplaceableFunctionVar: return false; } printer_unreachable("bad node kind"); @@ -1521,6 +1524,21 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << "type symbolic reference 0x"; Printer.writeHex(Node->getIndex()); return nullptr; + case Node::Kind::DynamicallyReplaceableFunctionKey: + if (!Options.ShortenThunk) { + Printer << "dynamically replaceable key for "; + } + return nullptr; + case Node::Kind::DynamicallyReplaceableFunctionImpl: + if (!Options.ShortenThunk) { + Printer << "dynamically replaceable thunk for "; + } + return nullptr; + case Node::Kind::DynamicallyReplaceableFunctionVar: + if (!Options.ShortenThunk) { + Printer << "dynamically replaceable variable for "; + } + return nullptr; case Node::Kind::ProtocolSymbolicReference: Printer << "protocol symbolic reference 0x"; Printer.writeHex(Node->getIndex()); diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index b16c443824f64..958f1204dfcf6 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -832,6 +832,18 @@ void Remangler::mangleMergedFunction(Node *node) { Out << "Tm"; } +void Remangler::mangleDynamicallyReplaceableFunctionImpl(Node *node) { + Out << "TI"; +} + +void Remangler::mangleDynamicallyReplaceableFunctionKey(Node *node) { + Out << "Tx"; +} + +void Remangler::mangleDynamicallyReplaceableFunctionVar(Node *node) { + Out << "TX"; +} + void Remangler::mangleDirectness(Node *node) { auto getChar = [](Directness d) -> char { switch (d) { diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 486117f9769e1..dab4bc99c3391 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -1255,6 +1255,9 @@ void Remangler::mangleGlobal(Node *node) { case Node::Kind::VTableAttribute: case Node::Kind::DirectMethodReferenceAttribute: case Node::Kind::MergedFunction: + case Node::Kind::DynamicallyReplaceableFunctionKey: + case Node::Kind::DynamicallyReplaceableFunctionImpl: + case Node::Kind::DynamicallyReplaceableFunctionVar: mangleInReverseOrder = true; break; default: @@ -1587,6 +1590,18 @@ void Remangler::mangleMergedFunction(Node *node) { Buffer << "Tm"; } +void Remangler::mangleDynamicallyReplaceableFunctionImpl(Node *node) { + Buffer << "TI"; +} + +void Remangler::mangleDynamicallyReplaceableFunctionKey(Node *node) { + Buffer << "Tx"; +} + +void Remangler::mangleDynamicallyReplaceableFunctionVar(Node *node) { + Buffer << "TX"; +} + void Remangler::manglePostfixOperator(Node *node) { mangleIdentifierImpl(node, /*isOperator*/ true); Buffer << "oP"; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index e0117462cac74..edee2f41fe34d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -268,6 +268,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.DebugConstraintSolver |= Args.hasArg(OPT_debug_constraints); Opts.NamedLazyMemberLoading &= !Args.hasArg(OPT_disable_named_lazy_member_loading); Opts.DebugGenericSignatures |= Args.hasArg(OPT_debug_generic_signatures); + Opts.EnableImplicitDynamic |= Args.hasArg(OPT_enable_implicit_dynamic); if (Args.hasArg(OPT_verify_syntax_tree)) { Opts.BuildSyntaxTree = true; diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 62815868ae3e6..7314205693e93 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -49,6 +49,7 @@ #include "llvm/Support/SaveAndRestore.h" #include "llvm/Transforms/Utils/ModuleUtils.h" +#include "Callee.h" #include "ConstantBuilder.h" #include "Explosion.h" #include "FixedTypeInfo.h" @@ -1059,8 +1060,10 @@ void IRGenerator::emitGlobalTopLevel(bool emitForParallelEmission) { // Emit SIL functions. for (SILFunction &f : PrimaryIGM->getSILModule()) { // Eagerly emit functions that are externally visible. Functions with code - // coverage instrumentation must also be eagerly emitted. + // coverage instrumentation must also be eagerly emitted. So must functions + // that are a dynamic replacement for another. if (!f.isPossiblyUsedExternally() && + !f.getDynamicallyReplacedFunction() && !hasCodeCoverageInstrumentation(f, PrimaryIGM->getSILModule())) continue; @@ -1271,6 +1274,134 @@ void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type, LazyTypeContextDescriptors.push_back(type); } } +static std::string getDynamicReplacementSection(IRGenModule &IGM) { + std::string sectionName; + switch (IGM.TargetInfo.OutputObjectFormat) { + case llvm::Triple::MachO: + sectionName = "__TEXT, __swift5_replace, regular, no_dead_strip"; + break; + case llvm::Triple::ELF: + sectionName = "swift5_replace"; + break; + case llvm::Triple::COFF: + sectionName = ".sw5repl"; + break; + default: + llvm_unreachable("Don't know how to emit field records table for " + "the selected object format."); + } + return sectionName; +} + +llvm::GlobalVariable *IRGenModule::getGlobalForDynamicallyReplaceableThunk( + LinkEntity &entity, llvm::Type *type, ForDefinition_t forDefinition) { + return cast( + getAddrOfLLVMVariable(entity, forDefinition, DebugTypeInfo())); +} + +/// Creates a dynamic replacement chain entry for \p SILFn that contains either +/// the implementation function pointer \p or a nullptr, the next pointer of the +/// chain entry is set to nullptr. +/// struct ChainEntry { +/// void *funPtr; +/// struct ChainEntry *next; +/// } +static llvm::GlobalVariable *getChainEntryForDynamicReplacement( + IRGenModule &IGM, SILFunction *SILFn, + llvm::Function *implFunction = nullptr, + ForDefinition_t forDefinition = ForDefinition) { + + LinkEntity entity = + LinkEntity::forDynamicallyReplaceableFunctionVariable(SILFn); + auto linkEntry = IGM.getGlobalForDynamicallyReplaceableThunk( + entity, IGM.DynamicReplacementLinkEntryTy, forDefinition); + if (!forDefinition) + return linkEntry; + + auto *funPtr = + implFunction ? llvm::ConstantExpr::getBitCast(implFunction, IGM.Int8PtrTy) + : llvm::ConstantExpr::getNullValue(IGM.Int8PtrTy); + auto *nextEntry = + llvm::ConstantExpr::getNullValue(IGM.DynamicReplacementLinkEntryPtrTy); + llvm::Constant *fields[] = {funPtr, nextEntry}; + auto *entry = + llvm::ConstantStruct::get(IGM.DynamicReplacementLinkEntryTy, fields); + linkEntry->setInitializer(entry); + return linkEntry; +} + +void IRGenerator::emitDynamicReplacements() { + if (DynamicReplacements.empty()) + return; + + auto &IGM = *getPrimaryIGM(); + + // struct ReplacementScope { + // uint32t flags; // unused + // uint32t numReplacements; + // struct Entry { + // RelativeIndirectablePointer replacedFunctionKey; + // RelativeDirectPointer newFunction; + // RelativeDirectPointer replacement; + // uint32_t flags; // unused. + // }[0] + // }; + ConstantInitBuilder builder(IGM); + auto replacementScope = builder.beginStruct(); + replacementScope.addInt32(0); // unused flags. + replacementScope.addInt32(DynamicReplacements.size()); + + auto replacementsArray = + replacementScope.beginArray(); + for (auto *newFunc : DynamicReplacements) { + auto replacementLinkEntry = + getChainEntryForDynamicReplacement(IGM, newFunc); + // TODO: replacementLinkEntry->setZeroSection() + auto *origFunc = newFunc->getDynamicallyReplacedFunction(); + assert(origFunc); + auto keyRef = IGM.getAddrOfLLVMVariableOrGOTEquivalent( + LinkEntity::forDynamicallyReplaceableFunctionKey(origFunc)); + + llvm::Constant *newFnPtr = llvm::ConstantExpr::getBitCast( + IGM.getAddrOfSILFunction(newFunc, NotForDefinition), IGM.Int8PtrTy); + + auto replacement = replacementsArray.beginStruct(); + replacement.addRelativeAddress(keyRef); // tagged relative reference. + replacement.addRelativeAddress(newFnPtr); // direct relative reference. + replacement.addRelativeAddress( + replacementLinkEntry); // direct relative reference. + replacement.addInt32(0); // unused flags. + replacement.finishAndAddTo(replacementsArray); + } + replacementsArray.finishAndAddTo(replacementScope); + + auto var = replacementScope.finishAndCreateGlobal( + "\x01l_unnamed_dynamic_replacements", IGM.getPointerAlignment(), + /*isConstant*/ true, llvm::GlobalValue::PrivateLinkage); + IGM.setTrueConstGlobal(var); + IGM.addUsedGlobal(var); + + // Emit the data for automatic replacement to happen on load. + // struct AutomaticReplacements { + // uint32t flags; // unused + // uint32t numReplacements; + // struct Entry { + // RelativeDirectPointer replacements; + // uint32_t flags; // unused. + // }[0] + // }; + auto autoReplacements = builder.beginStruct(); + autoReplacements.addInt32(0); // unused flags. + autoReplacements.addInt32(1); // number of replacement entries. + auto autoReplacementsArray = autoReplacements.beginArray(); + autoReplacementsArray.addRelativeAddress(var); + autoReplacementsArray.finishAndAddTo(autoReplacements); + auto autoReplVar = autoReplacements.finishAndCreateGlobal( + "\x01l_auto_dynamic_replacements", IGM.getPointerAlignment(), + /*isConstant*/ true, llvm::GlobalValue::PrivateLinkage); + autoReplVar->setSection(getDynamicReplacementSection(IGM)); + IGM.addUsedGlobal(autoReplVar); +} void IRGenerator::emitEagerClassInitialization() { if (ClassesForEagerInitialization.empty()) @@ -1893,16 +2024,169 @@ static clang::GlobalDecl getClangGlobalDeclForFunction(const clang::Decl *decl) return clang::GlobalDecl(cast(decl)); } +static void addLLVMFunctionAttributes(SILFunction *f, Signature &signature) { + auto &attrs = signature.getMutableAttributes(); + switch (f->getInlineStrategy()) { + case NoInline: + attrs = attrs.addAttribute(signature.getType()->getContext(), + llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoInline); + break; + case AlwaysInline: + // FIXME: We do not currently transfer AlwaysInline since doing so results + // in test failures, which must be investigated first. + case InlineDefault: + break; + } + + if (isReadOnlyFunction(f)) { + attrs = attrs.addAttribute(signature.getType()->getContext(), + llvm::AttributeList::FunctionIndex, + llvm::Attribute::ReadOnly); + } +} + +/// Create a key entry for a dynamic function replacement. A key entry refers to +/// the link entry for the dynamic replaceable function. +/// struct KeyEntry { +/// RelativeDirectPointer linkEntry; +/// int32_t flags; +/// } +static llvm::GlobalVariable *createGlobalForDynamicReplacementFunctionKey( + IRGenModule &IGM, SILFunction *SILFn, llvm::GlobalVariable *linkEntry) { + LinkEntity keyEntity = + LinkEntity::forDynamicallyReplaceableFunctionKey(SILFn); + auto key = IGM.getGlobalForDynamicallyReplaceableThunk( + keyEntity, IGM.DynamicReplacementKeyTy, ForDefinition); + + ConstantInitBuilder builder(IGM); + auto B = builder.beginStruct(IGM.DynamicReplacementKeyTy); + B.addRelativeAddress(linkEntry); + B.addInt32(0); + B.finishAndSetAsInitializer(key); + key->setConstant(true); + IGM.setTrueConstGlobal(key); + return key; +} + +/// Emit the thunk that dispatches to the dynamically replaceable function. +static void emitDynamicallyReplaceableThunk(IRGenModule &IGM, + SILFunction *SILFn, + llvm::Function *dispatchFn, + llvm::Function *implFn, + Signature &signature) { + + // Create and initialize the first link entry for the chain of replacements. + // The first implementation is initialized with 'implFn'. + auto linkEntry = getChainEntryForDynamicReplacement(IGM, SILFn, implFn); + + // Create the key data structure. This is used from other modules to refer to + // the chain of replacements. + createGlobalForDynamicReplacementFunctionKey(IGM, SILFn, linkEntry); + + // We should never inline the implementation function. + implFn->addFnAttr(llvm::Attribute::NoInline); + + // Load the function and dispatch to it forwarding our arguments. + llvm::BasicBlock *entryBB = + llvm::BasicBlock::Create(IGM.getLLVMContext(), "entry", dispatchFn); + IRBuilder B(IGM.getLLVMContext(), false); + B.SetInsertPoint(entryBB); + if (IGM.DebugInfo) + IGM.DebugInfo->emitArtificialFunction(B, dispatchFn); + llvm::Constant *indices[] = {llvm::ConstantInt::get(IGM.Int32Ty, 0), + llvm::ConstantInt::get(IGM.Int32Ty, 0)}; + auto *fnPtr = B.CreateLoad( + llvm::ConstantExpr::getInBoundsGetElementPtr(nullptr, linkEntry, indices), + IGM.getPointerAlignment()); + auto *typeFnPtr = B.CreateBitOrPointerCast(fnPtr, implFn->getType()); + SmallVector forwardedArgs; + for (auto &arg : dispatchFn->args()) + forwardedArgs.push_back(&arg); + auto *Res = + B.CreateCall(FunctionPointer(typeFnPtr, signature), forwardedArgs); + if (implFn->getReturnType()->isVoidTy()) + B.CreateRetVoid(); + else + B.CreateRet(Res); +} + +/// Calls the previous implementation before this dynamic replacement became +/// active. +void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) { + assert(f->getDynamicallyReplacedFunction()); + + auto entity = LinkEntity::forSILFunction(f, true); + + Signature signature = getSignature(f->getLoweredFunctionType()); + addLLVMFunctionAttributes(f, signature); + + LinkInfo implLink = LinkInfo::get(*this, entity, ForDefinition); + auto implFn = + createFunction(*this, implLink, signature, nullptr /*insertBefore*/, + f->getOptimizationMode()); + implFn->addFnAttr(llvm::Attribute::NoInline); + + auto linkEntry = + getChainEntryForDynamicReplacement(*this, f, nullptr, NotForDefinition); + + // Load the function and dispatch to it forwarding our arguments. + llvm::BasicBlock *entryBB = + llvm::BasicBlock::Create(getLLVMContext(), "entry", implFn); + IRBuilder B(getLLVMContext(), false); + B.SetInsertPoint(entryBB); + llvm::Constant *indices[] = {llvm::ConstantInt::get(Int32Ty, 0), + llvm::ConstantInt::get(Int32Ty, 0)}; + + auto *fnPtr = B.CreateLoad( + llvm::ConstantExpr::getInBoundsGetElementPtr(nullptr, linkEntry, indices), + getPointerAlignment()); + auto *typeFnPtr = B.CreateBitOrPointerCast(fnPtr, implFn->getType()); + + SmallVector forwardedArgs; + for (auto &arg : implFn->args()) + forwardedArgs.push_back(&arg); + auto *Res = + B.CreateCall(FunctionPointer(typeFnPtr, signature), forwardedArgs); + + if (implFn->getReturnType()->isVoidTy()) + B.CreateRetVoid(); + else + B.CreateRet(Res); +} + /// Find the entry point for a SIL function. -llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f, - ForDefinition_t forDefinition) { - LinkEntity entity = LinkEntity::forSILFunction(f); +llvm::Function *IRGenModule::getAddrOfSILFunction( + SILFunction *f, ForDefinition_t forDefinition, + bool isDynamicallyReplaceableImplementation, + bool shouldCallPreviousImplementation) { + assert(forDefinition || !isDynamicallyReplaceableImplementation); + assert(!forDefinition || !shouldCallPreviousImplementation); + + LinkEntity entity = + LinkEntity::forSILFunction(f, shouldCallPreviousImplementation); // Check whether we've created the function already. // FIXME: We should integrate this into the LinkEntity cache more cleanly. - llvm::Function *fn = Module.getFunction(f->getName()); + llvm::Function *fn = Module.getFunction(entity.mangleAsString()); if (fn) { - if (forDefinition) updateLinkageForDefinition(*this, fn, entity); + if (forDefinition) { + updateLinkageForDefinition(*this, fn, entity); + if (isDynamicallyReplaceableImplementation) { + // Create the dynamically replacement thunk. + LinkEntity implEntity = LinkEntity::forSILFunction(f, true); + auto existingImpl = Module.getFunction(implEntity.mangleAsString()); + assert(!existingImpl); + (void) existingImpl; + Signature signature = getSignature(f->getLoweredFunctionType()); + addLLVMFunctionAttributes(f, signature); + LinkInfo implLink = LinkInfo::get(*this, implEntity, forDefinition); + auto implFn = createFunction(*this, implLink, signature, fn, + f->getOptimizationMode()); + emitDynamicallyReplaceableThunk(*this, f, fn, implFn, signature); + return implFn; + } + } return fn; } @@ -1916,7 +2200,8 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f, } bool isDefinition = f->isDefinition(); - bool hasOrderNumber = isDefinition; + bool hasOrderNumber = + isDefinition && !shouldCallPreviousImplementation; unsigned orderNumber = ~0U; llvm::Function *insertBefore = nullptr; @@ -1953,28 +2238,10 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f, } Signature signature = getSignature(f->getLoweredFunctionType()); - auto &attrs = signature.getMutableAttributes(); + addLLVMFunctionAttributes(f, signature); LinkInfo link = LinkInfo::get(*this, entity, forDefinition); - switch (f->getInlineStrategy()) { - case NoInline: - attrs = attrs.addAttribute(signature.getType()->getContext(), - llvm::AttributeList::FunctionIndex, - llvm::Attribute::NoInline); - break; - case AlwaysInline: - // FIXME: We do not currently transfer AlwaysInline since doing so results - // in test failures, which must be investigated first. - case InlineDefault: - break; - } - - if (isReadOnlyFunction(f)) { - attrs = attrs.addAttribute(signature.getType()->getContext(), - llvm::AttributeList::FunctionIndex, - llvm::Attribute::ReadOnly); - } fn = createFunction(*this, link, signature, insertBefore, f->getOptimizationMode()); @@ -1990,6 +2257,16 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(SILFunction *f, if (hasOrderNumber) { EmittedFunctionsByOrder.insert(orderNumber, fn); } + + if (isDynamicallyReplaceableImplementation && forDefinition) { + LinkEntity implEntity = LinkEntity::forSILFunction(f, true); + LinkInfo implLink = LinkInfo::get(*this, implEntity, forDefinition); + auto implFn = createFunction(*this, implLink, signature, insertBefore, + f->getOptimizationMode()); + + emitDynamicallyReplaceableThunk(*this, f, fn, implFn, signature); + return implFn; + } return fn; } diff --git a/lib/IRGen/GenKeyPath.cpp b/lib/IRGen/GenKeyPath.cpp index 425906d0bbf53..1143f1f93554d 100644 --- a/lib/IRGen/GenKeyPath.cpp +++ b/lib/IRGen/GenKeyPath.cpp @@ -120,8 +120,8 @@ getAccessorForComputedComponent(IRGenModule &IGM, // If it's only externally available, we need a local thunk to relative- // reference. if (requirements.empty() && - !LinkEntity::forSILFunction(accessor).isAvailableExternally(IGM)) { - + !LinkEntity::forSILFunction(accessor, false).isAvailableExternally(IGM)) { + return IGM.getAddrOfSILFunction(accessor, NotForDefinition); } auto accessorFn = IGM.getAddrOfSILFunction(accessor, NotForDefinition); @@ -920,7 +920,7 @@ emitKeyPathComponent(IRGenModule &IGM, case KeyPathPatternComponent::ComputedPropertyId::Function: { idKind = KeyPathComponentHeader::Pointer; auto idRef = IGM.getAddrOfLLVMVariableOrGOTEquivalent( - LinkEntity::forSILFunction(id.getFunction())); + LinkEntity::forSILFunction(id.getFunction(), false)); idValue = idRef.getValue(); // If we got an indirect reference, we'll need to resolve it at diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index c6a2bec87a8b0..6807ba4c10034 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1511,7 +1511,7 @@ namespace { auto flags = getMethodDescriptorFlags(func); // Remember if the declaration was dynamic. - if (func->isDynamic()) + if (func->isObjCDynamic()) flags = flags.withIsDynamic(true); // TODO: final? open? diff --git a/lib/IRGen/GenObjC.cpp b/lib/IRGen/GenObjC.cpp index 601f51ea4187e..27e73b7fa324b 100644 --- a/lib/IRGen/GenObjC.cpp +++ b/lib/IRGen/GenObjC.cpp @@ -959,8 +959,9 @@ void irgen::emitObjCPartialApplication(IRGenFunction &IGF, /// Create the LLVM function declaration for a thunk that acts like /// an Objective-C method for a Swift method implementation. -static llvm::Constant *findSwiftAsObjCThunk(IRGenModule &IGM, SILDeclRef ref) { - SILFunction *SILFn = IGM.getSILModule().lookUpFunction(ref); +static llvm::Constant *findSwiftAsObjCThunk(IRGenModule &IGM, SILDeclRef ref, + SILFunction *&SILFn) { + SILFn = IGM.getSILModule().lookUpFunction(ref); assert(SILFn && "no IR function for swift-as-objc thunk"); auto fn = IGM.getAddrOfSILFunction(SILFn, NotForDefinition); ApplyIRLinkage({llvm::GlobalValue::InternalLinkage, @@ -977,7 +978,8 @@ static llvm::Constant *findSwiftAsObjCThunk(IRGenModule &IGM, SILDeclRef ref) { /// /// Returns a value of type i8*. static llvm::Constant *getObjCGetterPointer(IRGenModule &IGM, - AbstractStorageDecl *property) { + AbstractStorageDecl *property, + SILFunction *&silFn) { // Protocol properties have no impl. if (isa(property->getDeclContext())) return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); @@ -985,7 +987,7 @@ static llvm::Constant *getObjCGetterPointer(IRGenModule &IGM, SILDeclRef getter = SILDeclRef(property->getGetter(), SILDeclRef::Kind::Func) .asForeign(); - return findSwiftAsObjCThunk(IGM, getter); + return findSwiftAsObjCThunk(IGM, getter, silFn); } /// Produce a function pointer, suitable for invocation by @@ -993,7 +995,8 @@ static llvm::Constant *getObjCGetterPointer(IRGenModule &IGM, /// /// Returns a value of type i8*. static llvm::Constant *getObjCSetterPointer(IRGenModule &IGM, - AbstractStorageDecl *property) { + AbstractStorageDecl *property, + SILFunction *&silFn) { // Protocol properties have no impl. if (isa(property->getDeclContext())) return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); @@ -1003,8 +1006,7 @@ static llvm::Constant *getObjCSetterPointer(IRGenModule &IGM, SILDeclRef setter = SILDeclRef(property->getSetter(), SILDeclRef::Kind::Func) .asForeign(); - - return findSwiftAsObjCThunk(IGM, setter); + return findSwiftAsObjCThunk(IGM, setter, silFn); } /// Produce a function pointer, suitable for invocation by @@ -1012,7 +1014,8 @@ static llvm::Constant *getObjCSetterPointer(IRGenModule &IGM, /// /// Returns a value of type i8*. static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM, - FuncDecl *method) { + FuncDecl *method, + SILFunction *&silFn) { // Protocol methods have no impl. if (isa(method->getDeclContext())) return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); @@ -1020,7 +1023,7 @@ static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM, SILDeclRef declRef = SILDeclRef(method, SILDeclRef::Kind::Func) .asForeign(); - return findSwiftAsObjCThunk(IGM, declRef); + return findSwiftAsObjCThunk(IGM, declRef, silFn); } /// Produce a function pointer, suitable for invocation by @@ -1028,7 +1031,8 @@ static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM, /// /// Returns a value of type i8*. static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM, - ConstructorDecl *constructor) { + ConstructorDecl *constructor, + SILFunction *&silFn) { // Protocol methods have no impl. if (isa(constructor->getDeclContext())) return llvm::ConstantPointerNull::get(IGM.Int8PtrTy); @@ -1036,7 +1040,7 @@ static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM, SILDeclRef declRef = SILDeclRef(constructor, SILDeclRef::Kind::Initializer) .asForeign(); - return findSwiftAsObjCThunk(IGM, declRef); + return findSwiftAsObjCThunk(IGM, declRef, silFn); } /// Produce a function pointer, suitable for invocation by @@ -1044,11 +1048,12 @@ static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM, /// /// Returns a value of type i8*. static llvm::Constant *getObjCMethodPointer(IRGenModule &IGM, - DestructorDecl *destructor) { + DestructorDecl *destructor, + SILFunction *&silFn) { SILDeclRef declRef = SILDeclRef(destructor, SILDeclRef::Kind::Deallocator) .asForeign(); - return findSwiftAsObjCThunk(IGM, declRef); + return findSwiftAsObjCThunk(IGM, declRef, silFn); } static SILDeclRef getObjCMethodRef(AbstractFunctionDecl *method) { @@ -1153,13 +1158,13 @@ static llvm::Constant *getObjCEncodingForMethodType(IRGenModule &IGM, /// Emit the components of an Objective-C method descriptor: its selector, /// type encoding, and IMP pointer. -void irgen::emitObjCMethodDescriptorParts(IRGenModule &IGM, - AbstractFunctionDecl *method, - bool extendedEncoding, - bool concrete, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +SILFunction *irgen::emitObjCMethodDescriptorParts(IRGenModule &IGM, + AbstractFunctionDecl *method, + bool extendedEncoding, + bool concrete, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl) { Selector selector(method); /// The first element is the selector. @@ -1174,31 +1179,32 @@ void irgen::emitObjCMethodDescriptorParts(IRGenModule &IGM, /// The third element is the method implementation pointer. if (!concrete) { impl = nullptr; - return; + return nullptr; } - + SILFunction *silFn = nullptr; if (auto func = dyn_cast(method)) - impl = getObjCMethodPointer(IGM, func); + impl = getObjCMethodPointer(IGM, func, silFn); else if (auto ctor = dyn_cast(method)) - impl = getObjCMethodPointer(IGM, ctor); + impl = getObjCMethodPointer(IGM, ctor, silFn); else - impl = getObjCMethodPointer(IGM, cast(method)); + impl = getObjCMethodPointer(IGM, cast(method), silFn); + return silFn; } /// Emit the components of an Objective-C method descriptor for a /// property getter method. -void irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, - VarDecl *property, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +SILFunction *irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, + VarDecl *property, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl) { Selector getterSel(property, Selector::ForGetter); selectorRef = IGM.getAddrOfObjCMethodName(getterSel.str()); auto clangType = getObjCPropertyType(IGM, property); if (clangType.isNull()) { atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); - return; + return nullptr; } auto &clangASTContext = IGM.getClangASTContext(); @@ -1212,27 +1218,31 @@ void irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, TypeStr += "@0:"; TypeStr += llvm::itostr(PtrSize.getValue()); atEncoding = IGM.getAddrOfGlobalString(TypeStr.c_str()); - impl = getObjCGetterPointer(IGM, property); + SILFunction *silFn = nullptr; + impl = getObjCGetterPointer(IGM, property, silFn); + return silFn; } /// Emit the components of an Objective-C method descriptor for a /// subscript getter method. -void irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, - SubscriptDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +SILFunction *irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, + SubscriptDecl *subscript, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl) { Selector getterSel(subscript, Selector::ForGetter); selectorRef = IGM.getAddrOfObjCMethodName(getterSel.str()); atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); - impl = getObjCGetterPointer(IGM, subscript); + SILFunction *silFn = nullptr; + impl = getObjCGetterPointer(IGM, subscript, silFn); + return silFn; } -void irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, - AbstractStorageDecl *decl, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +SILFunction *irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, + AbstractStorageDecl *decl, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl) { if (auto sub = dyn_cast(decl)) { return emitObjCGetterDescriptorParts(IGM, sub, selectorRef, atEncoding, impl); @@ -1242,15 +1252,16 @@ void irgen::emitObjCGetterDescriptorParts(IRGenModule &IGM, selectorRef, atEncoding, impl); } llvm_unreachable("unknown storage!"); + return nullptr; } /// Emit the components of an Objective-C method descriptor for a /// property getter method. -void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, - VarDecl *property, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +SILFunction *irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, + VarDecl *property, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl) { assert(property->isSettable(property->getDeclContext()) && "not a settable property?!"); @@ -1268,7 +1279,7 @@ void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, clangType = getObjCPropertyType(IGM, property); if (clangType.isNull()) { atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); - return; + return nullptr; } clang::CharUnits sz = clangASTContext.getObjCEncodingTypeSize(clangType); if (!sz.isZero()) @@ -1280,30 +1291,33 @@ void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, clangASTContext.getObjCEncodingForType(clangType, TypeStr); TypeStr += llvm::itostr(ParmOffset); atEncoding = IGM.getAddrOfGlobalString(TypeStr.c_str()); - - impl = getObjCSetterPointer(IGM, property); + SILFunction *silFn = nullptr; + impl = getObjCSetterPointer(IGM, property, silFn); + return silFn; } /// Emit the components of an Objective-C method descriptor for a /// subscript getter method. -void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, - SubscriptDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +SILFunction *irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, + SubscriptDecl *subscript, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl) { assert(subscript->isSettable() && "not a settable subscript?!"); Selector setterSel(subscript, Selector::ForSetter); selectorRef = IGM.getAddrOfObjCMethodName(setterSel.str()); atEncoding = llvm::ConstantPointerNull::get(IGM.Int8PtrTy); - impl = getObjCSetterPointer(IGM, subscript); + SILFunction *silFn = nullptr; + impl = getObjCSetterPointer(IGM, subscript, silFn); + return silFn; } -void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, - AbstractStorageDecl *decl, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl) { +SILFunction *irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, + AbstractStorageDecl *decl, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl) { if (auto sub = dyn_cast(decl)) { return emitObjCSetterDescriptorParts(IGM, sub, selectorRef, atEncoding, impl); @@ -1313,6 +1327,7 @@ void irgen::emitObjCSetterDescriptorParts(IRGenModule &IGM, selectorRef, atEncoding, impl); } llvm_unreachable("unknown storage!"); + return nullptr; } static void buildMethodDescriptor(ConstantArrayBuilder &descriptors, @@ -1336,11 +1351,17 @@ void irgen::emitObjCMethodDescriptor(IRGenModule &IGM, ConstantArrayBuilder &descriptors, AbstractFunctionDecl *method) { llvm::Constant *selectorRef, *atEncoding, *impl; - emitObjCMethodDescriptorParts(IGM, method, + auto silFn = emitObjCMethodDescriptorParts(IGM, method, /*extended*/ false, /*concrete*/ true, selectorRef, atEncoding, impl); buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl); + + if (silFn && silFn->hasObjCReplacement()) { + auto replacedSelector = + IGM.getAddrOfObjCMethodName(silFn->getObjCReplacement().str()); + buildMethodDescriptor(descriptors, replacedSelector, atEncoding, impl); + } } void irgen::emitObjCIVarInitDestroyDescriptor(IRGenModule &IGM, @@ -1397,18 +1418,28 @@ void irgen::emitObjCGetterDescriptor(IRGenModule &IGM, ConstantArrayBuilder &descriptors, AbstractStorageDecl *storage) { llvm::Constant *selectorRef, *atEncoding, *impl; - emitObjCGetterDescriptorParts(IGM, storage, - selectorRef, atEncoding, impl); + auto *silFn = emitObjCGetterDescriptorParts(IGM, storage, selectorRef, + atEncoding, impl); buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl); + if (silFn && silFn->hasObjCReplacement()) { + auto replacedSelector = + IGM.getAddrOfObjCMethodName(silFn->getObjCReplacement().str()); + buildMethodDescriptor(descriptors, replacedSelector, atEncoding, impl); + } } void irgen::emitObjCSetterDescriptor(IRGenModule &IGM, ConstantArrayBuilder &descriptors, AbstractStorageDecl *storage) { llvm::Constant *selectorRef, *atEncoding, *impl; - emitObjCSetterDescriptorParts(IGM, storage, - selectorRef, atEncoding, impl); + auto *silFn = emitObjCSetterDescriptorParts(IGM, storage, selectorRef, + atEncoding, impl); buildMethodDescriptor(descriptors, selectorRef, atEncoding, impl); + if (silFn && silFn->hasObjCReplacement()) { + auto replacedSelector = + IGM.getAddrOfObjCMethodName(silFn->getObjCReplacement().str()); + buildMethodDescriptor(descriptors, replacedSelector, atEncoding, impl); + } } bool irgen::requiresObjCMethodDescriptor(FuncDecl *method) { diff --git a/lib/IRGen/GenObjC.h b/lib/IRGen/GenObjC.h index b5f8e8f9ee485..6f00b503abedd 100644 --- a/lib/IRGen/GenObjC.h +++ b/lib/IRGen/GenObjC.h @@ -107,57 +107,57 @@ namespace irgen { /// Build the components of an Objective-C method descriptor for the given /// method or constructor implementation. - void emitObjCMethodDescriptorParts(IRGenModule &IGM, - AbstractFunctionDecl *method, - bool extendedEncoding, - bool concrete, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + SILFunction *emitObjCMethodDescriptorParts(IRGenModule &IGM, + AbstractFunctionDecl *method, + bool extendedEncoding, + bool concrete, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl); /// Build the components of an Objective-C method descriptor for the given /// property's method implementations. - void emitObjCGetterDescriptorParts(IRGenModule &IGM, - VarDecl *property, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + SILFunction *emitObjCGetterDescriptorParts(IRGenModule &IGM, + VarDecl *property, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl); /// Build the components of an Objective-C method descriptor for the given /// subscript's method implementations. - void emitObjCGetterDescriptorParts(IRGenModule &IGM, - SubscriptDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); - - void emitObjCGetterDescriptorParts(IRGenModule &IGM, - AbstractStorageDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + SILFunction *emitObjCGetterDescriptorParts(IRGenModule &IGM, + SubscriptDecl *subscript, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl); + + SILFunction *emitObjCGetterDescriptorParts(IRGenModule &IGM, + AbstractStorageDecl *subscript, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl); /// Build the components of an Objective-C method descriptor for the given /// property's method implementations. - void emitObjCSetterDescriptorParts(IRGenModule &IGM, - VarDecl *property, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + SILFunction *emitObjCSetterDescriptorParts(IRGenModule &IGM, + VarDecl *property, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl); /// Build the components of an Objective-C method descriptor for the given /// subscript's method implementations. - void emitObjCSetterDescriptorParts(IRGenModule &IGM, - SubscriptDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); - - void emitObjCSetterDescriptorParts(IRGenModule &IGM, - AbstractStorageDecl *subscript, - llvm::Constant *&selectorRef, - llvm::Constant *&atEncoding, - llvm::Constant *&impl); + SILFunction *emitObjCSetterDescriptorParts(IRGenModule &IGM, + SubscriptDecl *subscript, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl); + + SILFunction *emitObjCSetterDescriptorParts(IRGenModule &IGM, + AbstractStorageDecl *subscript, + llvm::Constant *&selectorRef, + llvm::Constant *&atEncoding, + llvm::Constant *&impl); /// Build an Objective-C method descriptor for the given method, /// constructor, or destructor implementation. diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 2237042ed3b27..589edae6f3c18 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -822,6 +822,7 @@ performIRGeneration(IRGenOptions &Opts, ModuleDecl *M, IGM.emitBuiltinReflectionMetadata(); IGM.emitReflectionMetadataVersion(); irgen.emitEagerClassInitialization(); + irgen.emitDynamicReplacements(); } // Emit symbols for eliminated dead methods. @@ -981,6 +982,8 @@ static void performParallelIRGeneration( irgen.emitSwiftProtocols(); + irgen.emitDynamicReplacements(); + irgen.emitProtocolConformances(); irgen.emitTypeMetadataRecords(); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index ca380534d41c5..6f2448f701c5a 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -458,6 +458,23 @@ IRGenModule::IRGenModule(IRGenerator &irgen, IsSwiftErrorInRegister = clang::CodeGen::swiftcall::isSwiftErrorLoweredInRegister( ClangCodeGen->CGM()); + + DynamicReplacementsTy = + llvm::StructType::get(getLLVMContext(), {Int8PtrPtrTy, Int8PtrTy}); + DynamicReplacementsPtrTy = DynamicReplacementsTy->getPointerTo(DefaultAS); + + DynamicReplacementLinkEntryTy = + llvm::StructType::create(getLLVMContext(), "swift.dyn_repl_link_entry"); + DynamicReplacementLinkEntryPtrTy = + DynamicReplacementLinkEntryTy->getPointerTo(DefaultAS); + llvm::Type *linkEntryFields[] = { + Int8PtrTy, // function pointer. + DynamicReplacementLinkEntryPtrTy // next. + }; + DynamicReplacementLinkEntryTy->setBody(linkEntryFields); + + DynamicReplacementKeyTy = createStructType(*this, "swift.dyn_repl_key", + {RelativeAddressTy, Int32Ty}); } IRGenModule::~IRGenModule() { diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 9bdb1728b57ce..8985b8110ed46 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -235,6 +235,8 @@ class IRGenerator { llvm::SmallPtrSet LazilyEmittedFunctions; + llvm::SetVector DynamicReplacements; + struct FieldTypeMetadata { IRGenModule *IGM; std::vector fieldTypes; @@ -338,6 +340,9 @@ class IRGenerator { void emitEagerClassInitialization(); + // Emit the code to replace dynamicReplacement(for:) functions. + void emitDynamicReplacements(); + /// Checks if the metadata of \p Nominal can be emitted lazily. /// /// If yes, \p Nominal is added to eligibleLazyMetadata and true is returned. @@ -348,6 +353,8 @@ class IRGenerator { void addLazyFunction(SILFunction *f); + void addDynamicReplacement(SILFunction *f) { DynamicReplacements.insert(f); } + void forceLocalEmitOfLazyFunction(SILFunction *f) { DefaultIGMForFunction[f] = CurrentIGM; } @@ -640,6 +647,13 @@ class IRGenModule { llvm::PointerType *WitnessTablePtrPtrTy; /// i8*** llvm::Type *FloatTy; llvm::Type *DoubleTy; + llvm::StructType *DynamicReplacementsTy; // { i8**, i8* } + llvm::PointerType *DynamicReplacementsPtrTy; + + llvm::StructType *DynamicReplacementLinkEntryTy; // %link_entry = { i8*, %link_entry*} + llvm::PointerType + *DynamicReplacementLinkEntryPtrTy; // %link_entry* + llvm::StructType *DynamicReplacementKeyTy; // { i32, i32} llvm::GlobalVariable *TheTrivialPropertyDescriptor = nullptr; @@ -1317,11 +1331,17 @@ private: \ Address getAddrOfObjCClassRef(ClassDecl *D); llvm::Constant *getAddrOfMetaclassObject(ClassDecl *D, ForDefinition_t forDefinition); + llvm::Function *getAddrOfObjCMetadataUpdateFunction(ClassDecl *D, ForDefinition_t forDefinition); - llvm::Function *getAddrOfSILFunction(SILFunction *f, - ForDefinition_t forDefinition); + llvm::Function * + getAddrOfSILFunction(SILFunction *f, ForDefinition_t forDefinition, + bool isDynamicallyReplaceableImplementation = false, + bool shouldCallPreviousImplementation = false); + + void emitDynamicReplacementOriginalFunctionThunk(SILFunction *f); + llvm::Function *getAddrOfContinuationPrototype(CanSILFunctionType fnType); Address getAddrOfSILGlobalVariable(SILGlobalVariable *var, const TypeInfo &ti, @@ -1384,6 +1404,10 @@ private: \ void ensureRelativeSymbolCollocation(SILWitnessTable &wt); + llvm::GlobalVariable * + getGlobalForDynamicallyReplaceableThunk(LinkEntity &entity, llvm::Type *type, + ForDefinition_t forDefinition); + private: llvm::Constant * getAddrOfSharedContextDescriptor(LinkEntity entity, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 0f633dfaad29c..fde8a2392070f 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -915,7 +915,10 @@ class IRGenSILFunction : void visitPartialApplyInst(PartialApplyInst *i); void visitBuiltinInst(BuiltinInst *i); + void visitFunctionRefBaseInst(FunctionRefBaseInst *i); void visitFunctionRefInst(FunctionRefInst *i); + void visitDynamicFunctionRefInst(DynamicFunctionRefInst *i); + void visitPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *i); void visitAllocGlobalInst(AllocGlobalInst *i); void visitGlobalAddrInst(GlobalAddrInst *i); void visitGlobalValueInst(GlobalValueInst *i); @@ -1207,12 +1210,13 @@ llvm::Value *LoweredValue::getSingletonExplosion(IRGenFunction &IGF, llvm_unreachable("bad kind"); } -IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, - SILFunction *f) - : IRGenFunction(IGM, IGM.getAddrOfSILFunction(f, ForDefinition), - f->getOptimizationMode(), - f->getDebugScope(), f->getLocation()), - CurSILFn(f) { +IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f) + : IRGenFunction(IGM, + IGM.getAddrOfSILFunction(f, ForDefinition, + f->isDynamicallyReplaceable()), + f->getOptimizationMode(), f->getDebugScope(), + f->getLocation()), + CurSILFn(f) { // Apply sanitizer attributes to the function. // TODO: Check if the function is supposed to be excluded from ASan either by // being in the external file or via annotations. @@ -1237,6 +1241,11 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, if (f->getLoweredFunctionType()->isCoroutine()) { CurFn->addFnAttr(llvm::Attribute::NoInline); } + // Emit the thunk that calls the previous implementation if this is a dynamic + // replacement. + if (f->getDynamicallyReplacedFunction()) { + IGM.emitDynamicReplacementOriginalFunctionThunk(f); + } } IRGenSILFunction::~IRGenSILFunction() { @@ -1618,6 +1627,9 @@ void IRGenSILFunction::emitSILFunction() { assert(!CurSILFn->empty() && "function has no basic blocks?!"); + if (CurSILFn->getDynamicallyReplacedFunction()) + IGM.IRGen.addDynamicReplacement(CurSILFn); + // Configure the dominance resolver. // TODO: consider re-using a dom analysis from the PassManager // TODO: consider using a cheaper analysis at -O0 @@ -1831,10 +1843,12 @@ void IRGenSILFunction::visitSILBasicBlock(SILBasicBlock *BB) { assert(Builder.hasPostTerminatorIP() && "SIL bb did not terminate block?!"); } -void IRGenSILFunction::visitFunctionRefInst(FunctionRefInst *i) { +void IRGenSILFunction::visitFunctionRefBaseInst(FunctionRefBaseInst *i) { auto fn = i->getReferencedFunction(); - llvm::Constant *fnPtr = IGM.getAddrOfSILFunction(fn, NotForDefinition); + llvm::Constant *fnPtr = IGM.getAddrOfSILFunction( + fn, NotForDefinition, false /*isDynamicallyReplaceableImplementation*/, + isa(i)); auto sig = IGM.getSignature(fn->getLoweredFunctionType()); @@ -1848,6 +1862,19 @@ void IRGenSILFunction::visitFunctionRefInst(FunctionRefInst *i) { setLoweredFunctionPointer(i, fp); } +void IRGenSILFunction::visitFunctionRefInst(FunctionRefInst *i) { + visitFunctionRefBaseInst(i); +} + +void IRGenSILFunction::visitDynamicFunctionRefInst(DynamicFunctionRefInst *i) { + visitFunctionRefBaseInst(i); +} + +void IRGenSILFunction::visitPreviousDynamicFunctionRefInst( + PreviousDynamicFunctionRefInst *i) { + visitFunctionRefBaseInst(i); +} + void IRGenSILFunction::visitAllocGlobalInst(AllocGlobalInst *i) { SILGlobalVariable *var = i->getReferencedGlobal(); SILType loweredTy = var->getLoweredType(); diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index ef73be30401b0..7cbb5c58ef816 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -264,8 +264,65 @@ std::string LinkEntity::mangleAsString() const { return Result; } - case Kind::SILFunction: - return getSILFunction()->getName(); + case Kind::SILFunction: { + std::string Result(getSILFunction()->getName()); + if (isDynamicallyReplaceable()) { + Result.append("TI"); + } + return Result; + } + case Kind::DynamicallyReplaceableFunctionImpl: { + assert(isa(getDecl())); + std::string Result; + if (auto *Constructor = dyn_cast(getDecl())) { + Result = mangler.mangleConstructorEntity(Constructor, true, + /*isCurried=*/false); + } else { + Result = mangler.mangleEntity(getDecl(), /*isCurried=*/false); + } + Result.append("TI"); + return Result; + } + + case Kind::DynamicallyReplaceableFunctionVariable: { + std::string Result(getSILFunction()->getName()); + Result.append("TX"); + return Result; + } + + case Kind::DynamicallyReplaceableFunctionKey: { + std::string Result(getSILFunction()->getName()); + Result.append("Tx"); + return Result; + } + + + case Kind::DynamicallyReplaceableFunctionVariableAST: { + assert(isa(getDecl())); + std::string Result; + if (auto *Constructor = dyn_cast(getDecl())) { + Result = mangler.mangleConstructorEntity(Constructor, true, + /*isCurried=*/false); + } else { + Result = mangler.mangleEntity(getDecl(), /*isCurried=*/false); + } + Result.append("TX"); + return Result; + } + + case Kind::DynamicallyReplaceableFunctionKeyAST: { + assert(isa(getDecl())); + std::string Result; + if (auto *Constructor = dyn_cast(getDecl())) { + Result = mangler.mangleConstructorEntity(Constructor, true, + /*isCurried=*/false); + } else { + Result = mangler.mangleEntity(getDecl(), /*isCurried=*/false); + } + Result.append("Tx"); + return Result; + } + case Kind::SILGlobalVariable: return getSILGlobalVariable()->getName(); @@ -491,9 +548,20 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { case Kind::GenericProtocolWitnessTableInstantiationFunction: return SILLinkage::Private; + case Kind::DynamicallyReplaceableFunctionKey: case Kind::SILFunction: return getSILFunction()->getEffectiveSymbolLinkage(); + case Kind::DynamicallyReplaceableFunctionImpl: + case Kind::DynamicallyReplaceableFunctionKeyAST: + return getSILLinkage(getDeclLinkage(getDecl()), forDefinition); + + + case Kind::DynamicallyReplaceableFunctionVariable: + return getSILFunction()->getEffectiveSymbolLinkage(); + case Kind::DynamicallyReplaceableFunctionVariableAST: + return getSILLinkage(getDeclLinkage(getDecl()), forDefinition); + case Kind::SILGlobalVariable: return getSILGlobalVariable()->getLinkage(); @@ -623,6 +691,9 @@ bool LinkEntity::isAvailableExternally(IRGenModule &IGM) const { case Kind::TypeMetadataPattern: case Kind::DefaultAssociatedConformanceAccessor: return false; + case Kind::DynamicallyReplaceableFunctionKey: + case Kind::DynamicallyReplaceableFunctionVariable: + return true; case Kind::SILFunction: return ::isAvailableExternally(IGM, getSILFunction()); @@ -647,6 +718,9 @@ bool LinkEntity::isAvailableExternally(IRGenModule &IGM) const { case Kind::ReflectionFieldDescriptor: case Kind::ReflectionAssociatedTypeDescriptor: case Kind::CoroutineContinuationPrototype: + case Kind::DynamicallyReplaceableFunctionVariableAST: + case Kind::DynamicallyReplaceableFunctionImpl: + case Kind::DynamicallyReplaceableFunctionKeyAST: llvm_unreachable("Relative reference to unsupported link entity"); } llvm_unreachable("bad link entity kind"); @@ -727,7 +801,10 @@ llvm::Type *LinkEntity::getDefaultDeclarationType(IRGenModule &IGM) const { case Kind::MethodDescriptorInitializer: case Kind::MethodDescriptorAllocator: return IGM.MethodDescriptorStructTy; - + case Kind::DynamicallyReplaceableFunctionKey: + return IGM.DynamicReplacementKeyTy; + case Kind::DynamicallyReplaceableFunctionVariable: + return IGM.DynamicReplacementLinkEntryTy; default: llvm_unreachable("declaration LLVM type not specified"); } @@ -768,6 +845,8 @@ Alignment LinkEntity::getAlignment(IRGenModule &IGM) const { case Kind::ProtocolWitnessTablePattern: case Kind::ObjCMetaclass: case Kind::SwiftMetaclassStub: + case Kind::DynamicallyReplaceableFunctionVariable: + case Kind::DynamicallyReplaceableFunctionKey: return IGM.getPointerAlignment(); case Kind::SILFunction: return Alignment(1); @@ -782,7 +861,8 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const { if (getSILGlobalVariable()->getDecl()) return getSILGlobalVariable()->getDecl()->isWeakImported(module); return false; - + case Kind::DynamicallyReplaceableFunctionKey: + case Kind::DynamicallyReplaceableFunctionVariable: case Kind::SILFunction: { // For imported functions check the Clang declaration. if (auto clangOwner = getSILFunction()->getClangNodeOwner()) @@ -833,6 +913,9 @@ bool LinkEntity::isWeakImported(ModuleDecl *module) const { case Kind::ProtocolDescriptor: case Kind::ProtocolRequirementsBaseDescriptor: case Kind::AssociatedTypeDescriptor: + case Kind::DynamicallyReplaceableFunctionKeyAST: + case Kind::DynamicallyReplaceableFunctionVariableAST: + case Kind::DynamicallyReplaceableFunctionImpl: return getDecl()->isWeakImported(module); // TODO: Revisit some of the below, for weak conformances. @@ -905,12 +988,17 @@ const SourceFile *LinkEntity::getSourceFileForEmission() const { case Kind::AssociatedTypeDescriptor: case Kind::AssociatedConformanceDescriptor: case Kind::DefaultAssociatedConformanceAccessor: + case Kind::DynamicallyReplaceableFunctionVariableAST: + case Kind::DynamicallyReplaceableFunctionKeyAST: + case Kind::DynamicallyReplaceableFunctionImpl: sf = getSourceFileForDeclContext(getDecl()->getDeclContext()); if (!sf) return nullptr; break; case Kind::SILFunction: + case Kind::DynamicallyReplaceableFunctionVariable: + case Kind::DynamicallyReplaceableFunctionKey: sf = getSourceFileForDeclContext(getSILFunction()->getDeclContext()); if (!sf) return nullptr; diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 879a45e9f71e9..da1c2fc181ef5 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -2683,11 +2683,11 @@ void LoadableByAddress::run() { } // Scan the module for all references of the modified functions: - llvm::SetVector funcRefs; + llvm::SetVector funcRefs; for (SILFunction &CurrF : *getModule()) { for (SILBasicBlock &BB : CurrF) { for (SILInstruction &I : BB) { - if (auto *FRI = dyn_cast(&I)) { + if (auto *FRI = dyn_cast(&I)) { SILFunction *RefF = FRI->getReferencedFunction(); if (modFuncs.count(RefF) != 0) { // Go over the uses and add them to lists to modify @@ -2792,11 +2792,11 @@ void LoadableByAddress::run() { // Note: We don't need to update the witness tables and vtables // They just contain a pointer to the function // The pointer does not change - for (FunctionRefInst *instr : funcRefs) { + for (auto *instr : funcRefs) { SILFunction *F = instr->getReferencedFunction(); SILBuilderWithScope refBuilder(instr); - FunctionRefInst *newInstr = - refBuilder.createFunctionRef(instr->getLoc(), F); + SingleValueInstruction *newInstr = + refBuilder.createFunctionRef(instr->getLoc(), F, instr->getKind()); instr->replaceAllUsesWith(newInstr); instr->getParent()->erase(instr); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 1b10de128be43..4832f92328cf3 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1464,6 +1464,62 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } + + case DAK_DynamicReplacement: { + // Parse the leading '('. + if (Tok.isNot(tok::l_paren)) { + diagnose(Loc, diag::attr_expected_lparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return false; + } + + SourceLoc LParenLoc = consumeToken(tok::l_paren); + DeclName replacedFunction; + { + SyntaxParsingContext ContentContext( + SyntaxContext, SyntaxKind::NamedAttributeStringArgument); + + // Parse 'for'. + if (Tok.getText() != "for") { + diagnose(Loc, diag::attr_dynamic_replacement_expected_for); + return false; + } + auto ForLoc = consumeToken(); + + // Parse ':'. + if (Tok.getText() != ":") { + diagnose(ForLoc, diag::attr_dynamic_replacement_expected_colon); + return false; + } + auto ColonLoc = consumeToken(tok::colon); + { + SyntaxParsingContext ContentContext(SyntaxContext, + SyntaxKind::DeclName); + + DeclNameLoc loc; + replacedFunction = parseUnqualifiedDeclName( + true, loc, diag::attr_dynamic_replacement_expected_function, + /*allowOperators*/ true, /*allowZeroArgCompoundNames*/ true, + /*allowDeinitAndSubscript*/ true); + } + } + + // Parse the matching ')'. + SourceLoc RParenLoc; + bool Invalid = parseMatchingToken( + tok::r_paren, RParenLoc, diag::attr_dynamic_replacement_expected_rparen, + LParenLoc); + if (Invalid) { + return false; + } + + + DynamicReplacementAttr *attr = DynamicReplacementAttr::create( + Context, AtLoc, Loc, LParenLoc, replacedFunction, RParenLoc); + Attributes.add(attr); + break; + } + case DAK_Specialize: { if (Tok.isNot(tok::l_paren)) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -2809,7 +2865,10 @@ Parser::parseDecl(ParseDeclOptions Flags, if (auto SF = CurDeclContext->getParentSourceFile()) { if (!getScopeInfo().isInactiveConfigBlock()) { for (auto Attr : Attributes) { - if (isa(Attr) || isa(Attr)) + if (isa(Attr) || + /* Pre Swift 5 dymamic implied @objc */ + (!Context.LangOpts.isSwiftVersionAtLeast(5) && + isa(Attr))) SF->AttrsRequiringFoundation.insert(Attr); } } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 860ef8057a2e6..17c73ed7a9448 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -2122,7 +2122,8 @@ DeclName Parser::parseUnqualifiedDeclName(bool afterDot, DeclNameLoc &loc, const Diagnostic &diag, bool allowOperators, - bool allowZeroArgCompoundNames) { + bool allowZeroArgCompoundNames, + bool allowDeinitAndSubscript) { // Consume the base name. DeclBaseName baseName; SourceLoc baseNameLoc; @@ -2137,6 +2138,10 @@ DeclName Parser::parseUnqualifiedDeclName(bool afterDot, // not as a keyword. if (Tok.is(tok::kw_init)) baseName = DeclBaseName::createConstructor(); + else if (allowDeinitAndSubscript &&Tok.is(tok::kw_deinit)) + baseName = DeclBaseName::createDestructor(); + else if (allowDeinitAndSubscript &&Tok.is(tok::kw_subscript)) + baseName = DeclBaseName::createSubscript(); else baseName = Context.getIdentifier(Tok.getText()); Tok.setKind(tok::identifier); diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index e7df5b1af046b..2f040a9541e29 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -901,7 +901,11 @@ void SILParser::convertRequirements(SILFunction *F, static bool parseDeclSILOptional(bool *isTransparent, IsSerialized_t *isSerialized, bool *isCanonical, - IsThunk_t *isThunk, bool *isGlobalInit, + IsThunk_t *isThunk, + IsDynamicallyReplaceable_t *isDynamic, + SILFunction **dynamicallyReplacedFunction, + Identifier *objCReplacementFor, + bool *isGlobalInit, Inline_t *inlineStrategy, OptimizationMode *optimizationMode, bool *isLet, bool *isWeakLinked, @@ -909,7 +913,8 @@ static bool parseDeclSILOptional(bool *isTransparent, SmallVectorImpl *Semantics, SmallVectorImpl *SpecAttrs, ValueDecl **ClangDecl, - EffectsKind *MRK, SILParser &SP) { + EffectsKind *MRK, SILParser &SP, + SILModule &M) { while (SP.P.consumeIf(tok::l_square)) { if (isLet && SP.P.Tok.is(tok::kw_let)) { *isLet = true; @@ -924,6 +929,8 @@ static bool parseDeclSILOptional(bool *isTransparent, *isTransparent = true; else if (isSerialized && SP.P.Tok.getText() == "serialized") *isSerialized = IsSerialized; + else if (isDynamic && SP.P.Tok.getText() == "dynamically_replacable") + *isDynamic = IsDynamic; else if (isSerialized && SP.P.Tok.getText() == "serializable") *isSerialized = IsSerializable; else if (isCanonical && SP.P.Tok.getText() == "canonical") @@ -959,7 +966,41 @@ static bool parseDeclSILOptional(bool *isTransparent, *MRK = EffectsKind::ReadWrite; else if (MRK && SP.P.Tok.getText() == "releasenone") *MRK = EffectsKind::ReleaseNone; - else if (Semantics && SP.P.Tok.getText() == "_semantics") { + else if (dynamicallyReplacedFunction && SP.P.Tok.getText() == "dynamic_replacement_for") { + SP.P.consumeToken(tok::identifier); + if (SP.P.Tok.getKind() != tok::string_literal) { + SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); + return true; + } + // Drop the double quotes. + StringRef replacedFunc = SP.P.Tok.getText().drop_front().drop_back(); + SILFunction *Func = M.lookUpFunction(replacedFunc.str()); + if (!Func) { + Identifier Id = SP.P.Context.getIdentifier(replacedFunc); + SP.P.diagnose(SP.P.Tok, diag::sil_dynamically_replaced_func_not_found, + Id); + return true; + } + *dynamicallyReplacedFunction = Func; + SP.P.consumeToken(tok::string_literal); + + SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); + continue; + } else if (objCReplacementFor && + SP.P.Tok.getText() == "objc_replacement_for") { + SP.P.consumeToken(tok::identifier); + if (SP.P.Tok.getKind() != tok::string_literal) { + SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); + return true; + } + // Drop the double quotes. + StringRef replacedFunc = SP.P.Tok.getText().drop_front().drop_back(); + *objCReplacementFor = SP.P.Context.getIdentifier(replacedFunc); + SP.P.consumeToken(tok::string_literal); + + SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); + continue; + } else if (Semantics && SP.P.Tok.getText() == "_semantics") { SP.P.consumeToken(tok::identifier); if (SP.P.Tok.getKind() != tok::string_literal) { SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); @@ -973,8 +1014,7 @@ static bool parseDeclSILOptional(bool *isTransparent, SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); continue; - } - else if (SpecAttrs && SP.P.Tok.getText() == "_specialize") { + } else if (SpecAttrs && SP.P.Tok.getText() == "_specialize") { SourceLoc AtLoc = SP.P.Tok.getLoc(); SourceLoc Loc(AtLoc); @@ -2555,6 +2595,22 @@ bool SILParser::parseSILInstruction(SILBuilder &B) { ResultVal = B.createFunctionRef(InstLoc, Fn); break; } + case SILInstructionKind::DynamicFunctionRefInst: { + SILFunction *Fn; + if (parseSILFunctionRef(InstLoc, Fn) || + parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createDynamicFunctionRef(InstLoc, Fn); + break; + } + case SILInstructionKind::PreviousDynamicFunctionRefInst: { + SILFunction *Fn; + if (parseSILFunctionRef(InstLoc, Fn) || + parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createPreviousDynamicFunctionRef(InstLoc, Fn); + break; + } case SILInstructionKind::BuiltinInst: { if (P.Tok.getKind() != tok::string_literal) { P.diagnose(P.Tok, diag::expected_tok_in_sil_instr,"builtin name"); @@ -5209,6 +5265,7 @@ bool SILParserTUState::parseDeclSIL(Parser &P) { bool isTransparent = false; IsSerialized_t isSerialized = IsNotSerialized; bool isCanonical = false; + IsDynamicallyReplaceable_t isDynamic = IsNotDynamic; IsThunk_t isThunk = IsNotThunk; bool isGlobalInit = false, isWeakLinked = false; bool isWithoutActuallyEscapingThunk = false; @@ -5218,13 +5275,15 @@ bool SILParserTUState::parseDeclSIL(Parser &P) { SmallVector SpecAttrs; ValueDecl *ClangDecl = nullptr; EffectsKind MRK = EffectsKind::Unspecified; + SILFunction *DynamicallyReplacedFunction = nullptr; + Identifier objCReplacementFor; if (parseSILLinkage(FnLinkage, P) || parseDeclSILOptional(&isTransparent, &isSerialized, &isCanonical, - &isThunk, &isGlobalInit, - &inlineStrategy, &optimizationMode, nullptr, - &isWeakLinked, &isWithoutActuallyEscapingThunk, - &Semantics, &SpecAttrs, - &ClangDecl, &MRK, FunctionState) || + &isThunk, &isDynamic, &DynamicallyReplacedFunction, + &objCReplacementFor, &isGlobalInit, &inlineStrategy, + &optimizationMode, nullptr, &isWeakLinked, + &isWithoutActuallyEscapingThunk, &Semantics, + &SpecAttrs, &ClangDecl, &MRK, FunctionState, M) || P.parseToken(tok::at_sign, diag::expected_sil_function_name) || P.parseIdentifier(FnName, FnNameLoc, diag::expected_sil_function_name) || P.parseToken(tok::colon, diag::expected_sil_type)) @@ -5249,6 +5308,11 @@ bool SILParserTUState::parseDeclSIL(Parser &P) { FunctionState.F->setSerialized(IsSerialized_t(isSerialized)); FunctionState.F->setWasDeserializedCanonical(isCanonical); FunctionState.F->setThunk(IsThunk_t(isThunk)); + FunctionState.F->setIsDynamic(isDynamic); + FunctionState.F->setDynamicallyReplacedFunction( + DynamicallyReplacedFunction); + if (!objCReplacementFor.empty()) + FunctionState.F->setObjCReplacement(objCReplacementFor); FunctionState.F->setGlobalInit(isGlobalInit); FunctionState.F->setWeakLinked(isWeakLinked); FunctionState.F->setWithoutActuallyEscapingThunk( @@ -5431,8 +5495,9 @@ bool SILParserTUState::parseSILGlobal(Parser &P) { SILParser State(P); if (parseSILLinkage(GlobalLinkage, P) || parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, - nullptr, nullptr, &isLet, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, State) || + nullptr, nullptr, nullptr, nullptr, nullptr, &isLet, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + State, M) || P.parseToken(tok::at_sign, diag::expected_sil_value_name) || P.parseIdentifier(GlobalName, NameLoc, diag::expected_sil_value_name) || P.parseToken(tok::colon, diag::expected_sil_type)) @@ -5480,7 +5545,8 @@ bool SILParserTUState::parseSILProperty(Parser &P) { IsSerialized_t Serialized = IsNotSerialized; if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, SP)) + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + SP, M)) return true; ValueDecl *VD; @@ -5548,7 +5614,8 @@ bool SILParserTUState::parseSILVTable(Parser &P) { IsSerialized_t Serialized = IsNotSerialized; if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, VTableState)) + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + VTableState, M)) return true; // Parse the class name. @@ -6070,7 +6137,8 @@ bool SILParserTUState::parseSILWitnessTable(Parser &P) { IsSerialized_t isSerialized = IsNotSerialized; if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, WitnessState)) + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + WitnessState, M)) return true; Scope S(&P, ScopeKind::TopLevel); diff --git a/lib/ParseSIL/SILParserFunctionBuilder.h b/lib/ParseSIL/SILParserFunctionBuilder.h index 23273e9ef3537..1714056902008 100644 --- a/lib/ParseSIL/SILParserFunctionBuilder.h +++ b/lib/ParseSIL/SILParserFunctionBuilder.h @@ -26,9 +26,9 @@ class LLVM_LIBRARY_VISIBILITY SILParserFunctionBuilder { SILFunction *createFunctionForForwardReference(StringRef name, CanSILFunctionType ty, SILLocation loc) { - auto *result = - builder.createFunction(SILLinkage::Private, name, ty, nullptr, loc, - IsNotBare, IsNotTransparent, IsNotSerialized); + auto *result = builder.createFunction( + SILLinkage::Private, name, ty, nullptr, loc, IsNotBare, + IsNotTransparent, IsNotSerialized, IsNotDynamic); result->setDebugScope(new (builder.mod) SILDebugScope(loc, result)); return result; } diff --git a/lib/SIL/Linker.cpp b/lib/SIL/Linker.cpp index 3405ef485d8f2..ee670819dadbd 100644 --- a/lib/SIL/Linker.cpp +++ b/lib/SIL/Linker.cpp @@ -173,6 +173,16 @@ void SILLinkerVisitor::visitFunctionRefInst(FunctionRefInst *FRI) { maybeAddFunctionToWorklist(FRI->getReferencedFunction()); } +void SILLinkerVisitor::visitDynamicFunctionRefInst( + DynamicFunctionRefInst *FRI) { + maybeAddFunctionToWorklist(FRI->getReferencedFunction()); +} + +void SILLinkerVisitor::visitPreviousDynamicFunctionRefInst( + PreviousDynamicFunctionRefInst *FRI) { + maybeAddFunctionToWorklist(FRI->getReferencedFunction()); +} + // Eagerly visiting all used conformances leads to a large blowup // in the amount of SIL we read in. For optimization purposes we can defer // reading in most conformances until we need them for devirtualization. diff --git a/lib/SIL/Linker.h b/lib/SIL/Linker.h index 093578e3a2761..12ad57ae57fff 100644 --- a/lib/SIL/Linker.h +++ b/lib/SIL/Linker.h @@ -66,6 +66,8 @@ class SILLinkerVisitor : public SILInstructionVisitor { void visitTryApplyInst(TryApplyInst *TAI); void visitPartialApplyInst(PartialApplyInst *PAI); void visitFunctionRefInst(FunctionRefInst *FRI); + void visitDynamicFunctionRefInst(DynamicFunctionRefInst *FRI); + void visitPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *FRI); void visitProtocolConformance(ProtocolConformanceRef C, const Optional &Member); void visitApplySubstitutions(SubstitutionMap subs); diff --git a/lib/SIL/OperandOwnership.cpp b/lib/SIL/OperandOwnership.cpp index f7843d1cb7de2..d7befb7a0824a 100644 --- a/lib/SIL/OperandOwnership.cpp +++ b/lib/SIL/OperandOwnership.cpp @@ -123,6 +123,8 @@ NO_OPERAND_INST(AllocGlobal) NO_OPERAND_INST(AllocStack) NO_OPERAND_INST(FloatLiteral) NO_OPERAND_INST(FunctionRef) +NO_OPERAND_INST(DynamicFunctionRef) +NO_OPERAND_INST(PreviousDynamicFunctionRef) NO_OPERAND_INST(GlobalAddr) NO_OPERAND_INST(GlobalValue) NO_OPERAND_INST(IntegerLiteral) diff --git a/lib/SIL/SILDeclRef.cpp b/lib/SIL/SILDeclRef.cpp index 25badfac15839..da1f85bdca4c2 100644 --- a/lib/SIL/SILDeclRef.cpp +++ b/lib/SIL/SILDeclRef.cpp @@ -41,8 +41,9 @@ swift::getMethodDispatch(AbstractFunctionDecl *method) { auto dc = method->getDeclContext(); if (dc->getSelfClassDecl()) { - if (method->isDynamic()) + if (method->isObjCDynamic()) { return MethodDispatch::Class; + } // Final methods can be statically referenced. if (method->isFinal()) @@ -87,8 +88,9 @@ bool swift::requiresForeignToNativeThunk(ValueDecl *vd) { bool swift::requiresForeignEntryPoint(ValueDecl *vd) { assert(!isa(vd)); - if (vd->isDynamic()) + if (vd->isObjCDynamic()) { return true; + } if (vd->isObjC() && isa(vd->getDeclContext())) return true; @@ -777,7 +779,7 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const { if (overridden.kind == SILDeclRef::Kind::Initializer) { return SILDeclRef(); } - if (overridden.getDecl()->isDynamic()) { + if (overridden.getDecl()->isObjCDynamic()) { return SILDeclRef(); } @@ -785,8 +787,9 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const { auto *asd = accessor->getStorage(); if (asd->hasClangNode()) return SILDeclRef(); - if (asd->isDynamic()) + if (asd->isObjCDynamic()) { return SILDeclRef(); + } } // If we overrode a decl from an extension, it won't be in a vtable @@ -933,3 +936,19 @@ unsigned SILDeclRef::getParameterListCount() const { llvm_unreachable("Unhandled ValueDecl for SILDeclRef"); } } + +bool SILDeclRef::isDynamicallyReplaceable() const { + if (isStoredPropertyInitializer()) + return false; + + if (kind == SILDeclRef::Kind::Destroyer || + kind == SILDeclRef::Kind::Initializer) { + return false; + } + + if (!hasDecl()) + return false; + + auto decl = getDecl(); + return decl->isDynamic() && !decl->isObjC(); +} diff --git a/lib/SIL/SILFunction.cpp b/lib/SIL/SILFunction.cpp index cf6817fcc4a24..067d0b1354a4c 100644 --- a/lib/SIL/SILFunction.cpp +++ b/lib/SIL/SILFunction.cpp @@ -56,14 +56,16 @@ void SILFunction::addSpecializeAttr(SILSpecializeAttr *Attr) { } } -SILFunction *SILFunction::create( - SILModule &M, SILLinkage linkage, StringRef name, - CanSILFunctionType loweredType, GenericEnvironment *genericEnv, - Optional loc, IsBare_t isBareSILFunction, - IsTransparent_t isTrans, IsSerialized_t isSerialized, - ProfileCounter entryCount, IsThunk_t isThunk, - SubclassScope classSubclassScope, Inline_t inlineStrategy, EffectsKind E, - SILFunction *insertBefore, const SILDebugScope *debugScope) { +SILFunction * +SILFunction::create(SILModule &M, SILLinkage linkage, StringRef name, + CanSILFunctionType loweredType, + GenericEnvironment *genericEnv, Optional loc, + IsBare_t isBareSILFunction, IsTransparent_t isTrans, + IsSerialized_t isSerialized, ProfileCounter entryCount, + IsDynamicallyReplaceable_t isDynamic, IsThunk_t isThunk, + SubclassScope classSubclassScope, Inline_t inlineStrategy, + EffectsKind E, SILFunction *insertBefore, + const SILDebugScope *debugScope) { // Get a StringMapEntry for the function. As a sop to error cases, // allow the name to have an empty string. llvm::StringMapEntry *entry = nullptr; @@ -77,7 +79,7 @@ SILFunction *SILFunction::create( auto fn = new (M) SILFunction(M, linkage, name, loweredType, genericEnv, loc, isBareSILFunction, isTrans, isSerialized, entryCount, isThunk, classSubclassScope, - inlineStrategy, E, insertBefore, debugScope); + inlineStrategy, E, insertBefore, debugScope, isDynamic); if (entry) entry->setValue(fn); return fn; @@ -92,7 +94,8 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name, SubclassScope classSubclassScope, Inline_t inlineStrategy, EffectsKind E, SILFunction *InsertBefore, - const SILDebugScope *DebugScope) + const SILDebugScope *DebugScope, + IsDynamicallyReplaceable_t isDynamic) : Module(Module), Name(Name), LoweredType(LoweredType), GenericEnv(genericEnv), SpecializationInfo(nullptr), DebugScope(DebugScope), Bare(isBareSILFunction), Transparent(isTrans), @@ -100,8 +103,9 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name, ClassSubclassScope(unsigned(classSubclassScope)), GlobalInitFlag(false), InlineStrategy(inlineStrategy), Linkage(unsigned(Linkage)), HasCReferences(false), IsWeakLinked(false), - OptMode(OptimizationMode::NotSet), EffectsKindAttr(E), - EntryCount(entryCount) { + IsDynamicReplaceable(isDynamic), OptMode(OptimizationMode::NotSet), + EffectsKindAttr(E), EntryCount(entryCount) { + assert(!Transparent || !IsDynamicReplaceable); validateSubclassScope(classSubclassScope, isThunk, nullptr); if (InsertBefore) @@ -124,6 +128,11 @@ SILFunction::~SILFunction() { // an allocator that may recycle freed memory. dropAllReferences(); + if (ReplacedFunction) { + ReplacedFunction->decrementRefCount(); + ReplacedFunction = nullptr; + } + auto &M = getModule(); for (auto &BB : *this) { for (auto I = BB.begin(), E = BB.end(); I != E;) { @@ -485,6 +494,9 @@ SILFunction::isPossiblyUsedExternally() const { if (linkage == SILLinkage::Hidden && hasCReferences()) return true; + if (ReplacedFunction) + return true; + return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule()); } @@ -513,6 +525,24 @@ bool SILFunction::shouldVerifyOwnership() const { return !hasSemanticsAttr("verify.ownership.sil.never"); } +static Identifier getIdentifierForObjCSelector(ObjCSelector selector, ASTContext &Ctxt) { + SmallVector buffer; + auto str = selector.getString(buffer); + return Ctxt.getIdentifier(str); +} + +void SILFunction::setObjCReplacement(AbstractFunctionDecl *replacedFunc) { + assert(ReplacedFunction == nullptr && ObjCReplacementFor.empty()); + assert(replacedFunc != nullptr); + ObjCReplacementFor = getIdentifierForObjCSelector( + replacedFunc->getObjCSelector(), getASTContext()); +} + +void SILFunction::setObjCReplacement(Identifier replacedFunc) { + assert(ReplacedFunction == nullptr && ObjCReplacementFor.empty()); + ObjCReplacementFor = replacedFunc; +} + // See swift/Basic/Statistic.h for declaration: this enables tracing // SILFunctions, is defined here to avoid too much layering violation / circular // linkage dependency. diff --git a/lib/SIL/SILFunctionBuilder.cpp b/lib/SIL/SILFunctionBuilder.cpp index 71213aa008870..ec5fbacbbae4e 100644 --- a/lib/SIL/SILFunctionBuilder.cpp +++ b/lib/SIL/SILFunctionBuilder.cpp @@ -11,14 +11,15 @@ //===----------------------------------------------------------------------===// #include "swift/SIL/SILFunctionBuilder.h" - +#include "swift/AST/Decl.h" using namespace swift; SILFunction *SILFunctionBuilder::getOrCreateFunction( SILLocation loc, StringRef name, SILLinkage linkage, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, IsSerialized_t isSerialized, - ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope) { + IsDynamicallyReplaceable_t isDynamic, ProfileCounter entryCount, + IsThunk_t isThunk, SubclassScope subclassScope) { assert(!type->isNoEscape() && "Function decls always have escaping types."); if (auto fn = mod.lookUpFunction(name)) { assert(fn->getLoweredFunctionType() == type); @@ -29,13 +30,16 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction( auto fn = SILFunction::create(mod, linkage, name, type, nullptr, loc, isBareSILFunction, isTransparent, isSerialized, - entryCount, isThunk, subclassScope); + entryCount, isDynamic, isThunk, subclassScope); fn->setDebugScope(new (mod) SILDebugScope(loc, fn)); return fn; } -static void addFunctionAttributes(SILFunction *F, DeclAttributes &Attrs, - SILModule &M) { +void SILFunctionBuilder::addFunctionAttributes(SILFunction *F, + DeclAttributes &Attrs, + SILModule &M, + SILDeclRef constant) { + for (auto *A : Attrs.getAttributes()) F->addSemanticsAttr(cast(A)->Value); @@ -57,6 +61,38 @@ static void addFunctionAttributes(SILFunction *F, DeclAttributes &Attrs, // @_silgen_name and @_cdecl functions may be called from C code somewhere. if (Attrs.hasAttribute() || Attrs.hasAttribute()) F->setHasCReferences(true); + + // Propagate @_dynamicReplacement(for:). + if (constant.isNull()) + return; + auto *decl = constant.getDecl(); + + // Only emit replacements for the objc entry point of objc methods. + if (decl->isObjC() && + F->getLoweredFunctionType()->getExtInfo().getRepresentation() != + SILFunctionTypeRepresentation::ObjCMethod) + return; + + auto *replacedFuncAttr = Attrs.getAttribute(); + if (!replacedFuncAttr) + return; + + auto *replacedDecl = replacedFuncAttr->getReplacedFunction(); + assert(replacedDecl); + + if (decl->isObjC()) { + F->setObjCReplacement(replacedDecl); + return; + } + + if (constant.isInitializerOrDestroyer()) + return; + + SILDeclRef declRef(replacedDecl, constant.kind, false); + auto *replacedFunc = + getOrCreateFunction(replacedDecl, declRef, NotForDefinition); + assert(replacedFunc->getLoweredFunctionType() == F->getLoweredFunctionType()); + F->setDynamicallyReplacedFunction(replacedFunc); } SILFunction * @@ -99,10 +135,16 @@ SILFunctionBuilder::getOrCreateFunction(SILLocation loc, SILDeclRef constant, inlineStrategy = AlwaysInline; StringRef name = mod.allocateCopy(nameTmp); - auto *F = - SILFunction::create(mod, linkage, name, constantType, nullptr, None, - IsNotBare, IsTrans, IsSer, entryCount, IsNotThunk, - constant.getSubclassScope(), inlineStrategy, EK); + IsDynamicallyReplaceable_t IsDyn = IsNotDynamic; + if (constant.isDynamicallyReplaceable()) { + IsDyn = IsDynamic; + IsTrans = IsNotTransparent; + } + + auto *F = SILFunction::create(mod, linkage, name, constantType, nullptr, None, + IsNotBare, IsTrans, IsSer, entryCount, IsDyn, + IsNotThunk, constant.getSubclassScope(), + inlineStrategy, EK); F->setDebugScope(new (mod) SILDebugScope(loc, F)); F->setGlobalInit(constant.isGlobal()); @@ -120,7 +162,7 @@ SILFunctionBuilder::getOrCreateFunction(SILLocation loc, SILDeclRef constant, // Add attributes for e.g. computed properties. addFunctionAttributes(F, storage->getAttrs(), mod); } - addFunctionAttributes(F, decl->getAttrs(), mod); + addFunctionAttributes(F, decl->getAttrs(), mod, constant); } return F; @@ -129,21 +171,24 @@ SILFunctionBuilder::getOrCreateFunction(SILLocation loc, SILDeclRef constant, SILFunction *SILFunctionBuilder::getOrCreateSharedFunction( SILLocation loc, StringRef name, CanSILFunctionType type, IsBare_t isBareSILFunction, IsTransparent_t isTransparent, - IsSerialized_t isSerialized, ProfileCounter entryCount, IsThunk_t isThunk) { + IsSerialized_t isSerialized, ProfileCounter entryCount, IsThunk_t isThunk, + IsDynamicallyReplaceable_t isDynamic) { return getOrCreateFunction(loc, name, SILLinkage::Shared, type, isBareSILFunction, isTransparent, isSerialized, - entryCount, isThunk, SubclassScope::NotApplicable); + isDynamic, entryCount, isThunk, + SubclassScope::NotApplicable); } SILFunction *SILFunctionBuilder::createFunction( SILLinkage linkage, StringRef name, CanSILFunctionType loweredType, GenericEnvironment *genericEnv, Optional loc, IsBare_t isBareSILFunction, IsTransparent_t isTrans, - IsSerialized_t isSerialized, ProfileCounter entryCount, IsThunk_t isThunk, - SubclassScope subclassScope, Inline_t inlineStrategy, EffectsKind EK, - SILFunction *InsertBefore, const SILDebugScope *DebugScope) { + IsSerialized_t isSerialized, IsDynamicallyReplaceable_t isDynamic, + ProfileCounter entryCount, IsThunk_t isThunk, SubclassScope subclassScope, + Inline_t inlineStrategy, EffectsKind EK, SILFunction *InsertBefore, + const SILDebugScope *DebugScope) { return SILFunction::create(mod, linkage, name, loweredType, genericEnv, loc, isBareSILFunction, isTrans, isSerialized, - entryCount, isThunk, subclassScope, inlineStrategy, - EK, InsertBefore, DebugScope); + entryCount, isDynamic, isThunk, subclassScope, + inlineStrategy, EK, InsertBefore, DebugScope); } diff --git a/lib/SIL/SILInstruction.cpp b/lib/SIL/SILInstruction.cpp index ba098895021ef..159c25208d33a 100644 --- a/lib/SIL/SILInstruction.cpp +++ b/lib/SIL/SILInstruction.cpp @@ -155,7 +155,7 @@ void SILInstruction::dropAllReferences() { // If we have a function ref inst, we need to especially drop its function // argument so that it gets a proper ref decrement. - if (auto *FRI = dyn_cast(this)) { + if (auto *FRI = dyn_cast(this)) { if (!FRI->getReferencedFunction()) return; FRI->dropReferencedFunction(); @@ -451,6 +451,15 @@ namespace { auto *X = cast(LHS); return X->getReferencedFunction() == RHS->getReferencedFunction(); } + bool visitDynamicFunctionRefInst(const DynamicFunctionRefInst *RHS) { + auto *X = cast(LHS); + return X->getReferencedFunction() == RHS->getReferencedFunction(); + } + bool visitPreviousDynamicFunctionRefInst( + const PreviousDynamicFunctionRefInst *RHS) { + auto *X = cast(LHS); + return X->getReferencedFunction() == RHS->getReferencedFunction(); + } bool visitAllocGlobalInst(const AllocGlobalInst *RHS) { auto *X = cast(LHS); diff --git a/lib/SIL/SILInstructions.cpp b/lib/SIL/SILInstructions.cpp index 21a1fe669dd4a..0ed07ad76cc91 100644 --- a/lib/SIL/SILInstructions.cpp +++ b/lib/SIL/SILInstructions.cpp @@ -500,7 +500,7 @@ BeginApplyInst::create(SILDebugLocation loc, SILValue callee, } bool swift::doesApplyCalleeHaveSemantics(SILValue callee, StringRef semantics) { - if (auto *FRI = dyn_cast(callee)) + if (auto *FRI = dyn_cast(callee)) if (auto *F = FRI->getReferencedFunction()) return F->hasSemanticsAttr(semantics); return false; @@ -577,21 +577,40 @@ TryApplyInst *TryApplyInst::create( normalBB, errorBB, specializationInfo); } -FunctionRefInst::FunctionRefInst(SILDebugLocation Loc, SILFunction *F) - : InstructionBase(Loc, F->getLoweredType()), - Function(F) { +FunctionRefBaseInst::FunctionRefBaseInst(SILInstructionKind Kind, + SILDebugLocation DebugLoc, + SILFunction *F) + : LiteralInst(Kind, DebugLoc, F->getLoweredType()), f(F) { F->incrementRefCount(); } -FunctionRefInst::~FunctionRefInst() { - if (Function) +void FunctionRefBaseInst::dropReferencedFunction() { + if (auto *Function = getReferencedFunction()) Function->decrementRefCount(); + f = nullptr; } -void FunctionRefInst::dropReferencedFunction() { - if (Function) - Function->decrementRefCount(); - Function = nullptr; +FunctionRefBaseInst::~FunctionRefBaseInst() { + if (getReferencedFunction()) + getReferencedFunction()->decrementRefCount(); +} + +FunctionRefInst::FunctionRefInst(SILDebugLocation Loc, SILFunction *F) + : FunctionRefBaseInst(SILInstructionKind::FunctionRefInst, Loc, F) { + assert(!F->isDynamicallyReplaceable()); +} + +DynamicFunctionRefInst::DynamicFunctionRefInst(SILDebugLocation Loc, + SILFunction *F) + : FunctionRefBaseInst(SILInstructionKind::DynamicFunctionRefInst, Loc, F) { + assert(F->isDynamicallyReplaceable()); +} + +PreviousDynamicFunctionRefInst::PreviousDynamicFunctionRefInst( + SILDebugLocation Loc, SILFunction *F) + : FunctionRefBaseInst(SILInstructionKind::PreviousDynamicFunctionRefInst, + Loc, F) { + assert(!F->isDynamicallyReplaceable()); } AllocGlobalInst::AllocGlobalInst(SILDebugLocation Loc, diff --git a/lib/SIL/SILModule.cpp b/lib/SIL/SILModule.cpp index ad2870c215b5f..40a72e7d705a6 100644 --- a/lib/SIL/SILModule.cpp +++ b/lib/SIL/SILModule.cpp @@ -111,8 +111,10 @@ SILModule::~SILModule() { // need to worry about sil_witness_tables since witness tables reference each // other via protocol conformances and sil_vtables don't reference each other // at all. - for (SILFunction &F : *this) + for (SILFunction &F : *this) { F.dropAllReferences(); + F.dropDynamicallyReplacedFunction(); + } } std::unique_ptr @@ -436,6 +438,7 @@ void SILModule::eraseFunction(SILFunction *F) { // This opens dead-function-removal opportunities for called functions. // (References are not needed anymore.) F->dropAllReferences(); + F->dropDynamicallyReplacedFunction(); } void SILModule::invalidateFunctionInSILCache(SILFunction *F) { diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index 87fb19c274363..dad73ea50d58c 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -832,6 +832,14 @@ class SILPrinter : public SILInstructionVisitor { *this << " // function_ref " << demangleSymbol(FRI->getReferencedFunction()->getName()) << "\n"; + else if (auto *FRI = dyn_cast(I)) + *this << " // dynamic_function_ref " + << demangleSymbol(FRI->getReferencedFunction()->getName()) + << "\n"; + else if (auto *FRI = dyn_cast(I)) + *this << " // prev_dynamic_function_ref " + << demangleSymbol(FRI->getReferencedFunction()->getName()) + << "\n"; *this << " "; @@ -1130,7 +1138,16 @@ class SILPrinter : public SILInstructionVisitor { FRI->getReferencedFunction()->printName(PrintState.OS); *this << " : " << FRI->getType(); } - + void visitDynamicFunctionRefInst(DynamicFunctionRefInst *FRI) { + FRI->getReferencedFunction()->printName(PrintState.OS); + *this << " : " << FRI->getType(); + } + void + visitPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *FRI) { + FRI->getReferencedFunction()->printName(PrintState.OS); + *this << " : " << FRI->getType(); + } + void visitBuiltinInst(BuiltinInst *BI) { *this << QuotedString(BI->getName().str()); printSubstitutions(BI->getSubstitutions()); @@ -2269,6 +2286,9 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { break; case IsReabstractionThunk: OS << "[reabstraction_thunk] "; break; } + if (isDynamicallyReplaceable()) { + OS << "[dynamically_replacable] "; + } if (isWithoutActuallyEscapingThunk()) OS << "[without_actually_escaping] "; @@ -2299,6 +2319,18 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { else if (getEffectsKind() == EffectsKind::ReleaseNone) OS << "[releasenone] "; + if (auto *replacedFun = getDynamicallyReplacedFunction()) { + OS << "[dynamic_replacement_for \""; + OS << replacedFun->getName(); + OS << "\"] "; + } + + if (hasObjCReplacement()) { + OS << "[objc_replacement_for \""; + OS << getObjCReplacement().str(); + OS << "\"] "; + } + for (auto &Attr : getSemanticsAttrs()) OS << "[_semantics \"" << Attr << "\"] "; diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp index 3253b6831ea17..c8d41a78da3e5 100644 --- a/lib/SIL/SILVerifier.cpp +++ b/lib/SIL/SILVerifier.cpp @@ -1391,7 +1391,7 @@ class SILVerifier : public SILVerifierBase { verifyLLVMIntrinsic(BI, BI->getIntrinsicInfo().ID); } - void checkFunctionRefInst(FunctionRefInst *FRI) { + void checkFunctionRefBaseInst(FunctionRefBaseInst *FRI) { auto fnType = requireObjectType(SILFunctionType, FRI, "result of function_ref"); require(!fnType->getExtInfo().hasContext(), @@ -1402,6 +1402,22 @@ class SILVerifier : public SILVerifierBase { SILFunction *RefF = FRI->getReferencedFunction(); + if (isa(FRI)) + require( + !RefF->isDynamicallyReplaceable(), + "function_ref cannot reference a [dynamically_replaceable] function"); + else if (isa(FRI)) { + require(!RefF->isDynamicallyReplaceable(), + "previous_function_ref cannot reference a " + "[dynamically_replaceable] function"); + require(RefF->getDynamicallyReplacedFunction(), + "previous_function_ref must reference a " + "[dynamic_replacement_for:...] function"); + } else if (isa(FRI)) + require(RefF->isDynamicallyReplaceable(), + "dynamic_function_ref cannot reference a " + "[dynamically_replaceable] function"); + // In canonical SIL, direct reference to a shared_external declaration // is an error; we should have deserialized a body. In raw SIL, we may // not have deserialized the body yet. @@ -1427,6 +1443,18 @@ class SILVerifier : public SILVerifierBase { verifySILFunctionType(fnType); } + void checkFunctionRefInst(FunctionRefInst *FRI) { + checkFunctionRefBaseInst(FRI); + } + + void checkDynamicFunctionRefInst(DynamicFunctionRefInst *FRI) { + checkFunctionRefBaseInst(FRI); + } + + void checkPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *FRI) { + checkFunctionRefBaseInst(FRI); + } + void checkAllocGlobalInst(AllocGlobalInst *AGI) { if (F.isSerialized()) { SILGlobalVariable *RefG = AGI->getReferencedGlobal(); diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp index 4af17f2fcc94e..205b16fd42c5f 100644 --- a/lib/SIL/TypeLowering.cpp +++ b/lib/SIL/TypeLowering.cpp @@ -957,7 +957,8 @@ namespace { SILValue emitCopyValue(SILBuilder &B, SILLocation loc, SILValue value) const override { - if (isa(value)) + if (isa(value) || isa(value) || + isa(value)) return value; if (B.getFunction().hasQualifiedOwnership()) diff --git a/lib/SIL/ValueOwnership.cpp b/lib/SIL/ValueOwnership.cpp index 77842324f317c..23e829da741f0 100644 --- a/lib/SIL/ValueOwnership.cpp +++ b/lib/SIL/ValueOwnership.cpp @@ -104,6 +104,8 @@ CONSTANT_OWNERSHIP_INST(Trivial, ObjCMethod) CONSTANT_OWNERSHIP_INST(Trivial, ExistentialMetatype) CONSTANT_OWNERSHIP_INST(Trivial, FloatLiteral) CONSTANT_OWNERSHIP_INST(Trivial, FunctionRef) +CONSTANT_OWNERSHIP_INST(Trivial, DynamicFunctionRef) +CONSTANT_OWNERSHIP_INST(Trivial, PreviousDynamicFunctionRef) CONSTANT_OWNERSHIP_INST(Trivial, GlobalAddr) CONSTANT_OWNERSHIP_INST(Trivial, IndexAddr) CONSTANT_OWNERSHIP_INST(Trivial, IndexRawPointer) diff --git a/lib/SILGen/LValue.h b/lib/SILGen/LValue.h index c65e3454d81cb..53eead6eba534 100644 --- a/lib/SILGen/LValue.h +++ b/lib/SILGen/LValue.h @@ -437,7 +437,8 @@ class LValue { bool isSuper, SGFAccessKind accessKind, AccessStrategy accessStrategy, - CanType formalRValueType); + CanType formalRValueType, + bool isOnSelf = false); void addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc, SubscriptDecl *subscript, @@ -448,7 +449,8 @@ class LValue { AccessStrategy accessStrategy, CanType formalRValueType, PreparedArguments &&indices, - Expr *indexExprForDiagnostics); + Expr *indexExprForDiagnostics, + bool isOnSelfParameter = false); /// Add a subst-to-orig reabstraction component. That is, given /// that this l-value trafficks in values following the substituted diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 5458c0f6da789..4ac51440b6ee0 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -441,8 +441,8 @@ SILFunction *SILGenModule::emitTopLevelFunction(SILLocation Loc) { SILGenFunctionBuilder builder(*this); return builder.createFunction( SILLinkage::Public, SWIFT_ENTRY_POINT_FUNCTION, topLevelType, nullptr, - Loc, IsBare, IsNotTransparent, IsNotSerialized, ProfileCounter(), - IsNotThunk, SubclassScope::NotApplicable); + Loc, IsBare, IsNotTransparent, IsNotSerialized, IsNotDynamic, + ProfileCounter(), IsNotThunk, SubclassScope::NotApplicable); } SILFunction *SILGenModule::getEmittedFunction(SILDeclRef constant, @@ -1058,9 +1058,9 @@ SILFunction *SILGenModule::emitLazyGlobalInitializer(StringRef funcName, auto initSILType = getLoweredType(initType).castTo(); SILGenFunctionBuilder builder(*this); - auto *f = builder.createFunction(SILLinkage::Private, funcName, initSILType, - nullptr, SILLocation(binding), IsNotBare, - IsNotTransparent, IsNotSerialized); + auto *f = builder.createFunction( + SILLinkage::Private, funcName, initSILType, nullptr, SILLocation(binding), + IsNotBare, IsNotTransparent, IsNotSerialized, IsNotDynamic); f->setDebugScope(new (M) SILDebugScope(RegularLocation(binding), f)); auto dc = binding->getDeclContext(); SILGenFunction(*this, *f, dc).emitLazyGlobalInitializer(binding, pbdEntry); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index a51e1d2818d09..72adefbeaf910 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -264,6 +264,10 @@ class Callee { /// A direct standalone function call, referenceable by a FunctionRefInst. StandaloneFunction, + /// A direct standalone function call, referenceable by a + /// PreviousDynamicFunctionRefInst. + StandaloneFunctionDynamicallyReplaceableImpl, + /// Enum case constructor call. EnumElement, @@ -344,17 +348,16 @@ class Callee { /// Constructor for Callee::forDirect. Callee(SILGenFunction &SGF, SILDeclRef standaloneFunction, - AbstractionPattern origFormalType, - CanAnyFunctionType substFormalType, - SubstitutionMap subs, SILLocation l) - : kind(Kind::StandaloneFunction), Constant(standaloneFunction), - OrigFormalInterfaceType(origFormalType), - SubstFormalInterfaceType(getSubstFormalInterfaceType(substFormalType, - subs)), - Substitutions(subs), - Loc(l) - { - } + AbstractionPattern origFormalType, CanAnyFunctionType substFormalType, + SubstitutionMap subs, SILLocation l, + bool callDynamicallyReplaceableImpl = false) + : kind(callDynamicallyReplaceableImpl + ? Kind::StandaloneFunctionDynamicallyReplaceableImpl + : Kind::StandaloneFunction), + Constant(standaloneFunction), OrigFormalInterfaceType(origFormalType), + SubstFormalInterfaceType( + getSubstFormalInterfaceType(substFormalType, subs)), + Substitutions(subs), Loc(l) {} /// Constructor called by all for* factory methods except forDirect and /// forIndirect. @@ -377,10 +380,13 @@ class Callee { } static Callee forDirect(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap subs, - SILLocation l) { + SILLocation l, + bool callPreviousDynamicReplaceableImpl = false) { auto &ci = SGF.getConstantInfo(c); - return Callee(SGF, c, ci.FormalPattern, ci.FormalType, subs, l); + return Callee(SGF, c, ci.FormalPattern, ci.FormalType, subs, l, + callPreviousDynamicReplaceableImpl); } + static Callee forEnumElement(SILGenFunction &SGF, SILDeclRef c, SubstitutionMap subs, SILLocation l) { @@ -485,6 +491,7 @@ class Callee { return 1; case Kind::StandaloneFunction: + case Kind::StandaloneFunctionDynamicallyReplaceableImpl: case Kind::EnumElement: case Kind::ClassMethod: case Kind::SuperMethod: @@ -500,6 +507,7 @@ class Callee { switch (kind) { case Kind::IndirectValue: case Kind::StandaloneFunction: + case Kind::StandaloneFunctionDynamicallyReplaceableImpl: case Kind::EnumElement: return false; case Kind::WitnessMethod: @@ -586,6 +594,12 @@ class Callee { SILValue ref = SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo); return ManagedValue::forUnmanaged(ref); } + case Kind::StandaloneFunctionDynamicallyReplaceableImpl: { + auto constantInfo = SGF.getConstantInfo(*constant); + SILValue ref = + SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo, true); + return ManagedValue::forUnmanaged(ref); + } case Kind::EnumElement: llvm_unreachable("Should have been curried"); case Kind::ClassMethod: { @@ -685,6 +699,7 @@ class Callee { assert(Substitutions.empty()); return createCalleeTypeInfo(SGF, constant, IndirectValue.getType()); + case Kind::StandaloneFunctionDynamicallyReplaceableImpl: case Kind::StandaloneFunction: { auto constantInfo = SGF.getConstantInfo(*constant); return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType()); @@ -737,6 +752,7 @@ class Callee { case Kind::SuperMethod: case Kind::WitnessMethod: case Kind::DynamicMethod: + case Kind::StandaloneFunctionDynamicallyReplaceableImpl: return None; } llvm_unreachable("bad callee kind"); @@ -745,6 +761,22 @@ class Callee { } // end anonymous namespace +/// Is this a call to the dynamically replaced function inside of a +/// '@_dynamicReplacement(for:)' function. +bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF, + AbstractFunctionDecl *afd, + bool &isObjCReplacementSelfCall) { + if (auto *func = + dyn_cast_or_null(SGF.FunctionDC->getAsDecl())) { + auto *repl = func->getAttrs().getAttribute(); + if (repl && repl->getReplacedFunction() == afd) { + isObjCReplacementSelfCall = afd->isObjC(); + return true; + } + } + return false; +} + //===----------------------------------------------------------------------===// // SILGenApply ASTVisitor //===----------------------------------------------------------------------===// @@ -1031,6 +1063,26 @@ class SILGenApply : public Lowering::ExprVisitor { setSelfParam(std::move(selfArgSource), thisCallSite); } + // Directly dispatch to calls of the replaced function inside of + // '@_dynamicReplacement(for:)' methods. + bool isObjCReplacementCall = false; + if (isCallToReplacedInDynamicReplacement(SGF, afd, isObjCReplacementCall) && + thisCallSite->getArg()->isSelfExprOf( + cast(SGF.FunctionDC->getAsDecl()), false)) { + auto constant = SILDeclRef(afd, kind).asForeign( + !isObjCReplacementCall && requiresForeignEntryPoint(e->getDecl())); + auto subs = e->getDeclRef().getSubstitutions(); + if (isObjCReplacementCall) + setCallee(Callee::forDirect(SGF, constant, subs, e)); + else + setCallee(Callee::forDirect( + SGF, + SILDeclRef(cast(SGF.FunctionDC->getAsDecl()), + kind), + subs, e, true)); + return; + } + auto constant = SILDeclRef(afd, kind) .asForeign(requiresForeignEntryPoint(afd)); @@ -1082,8 +1134,27 @@ class SILGenApply : public Lowering::ExprVisitor { !captureInfo.hasGenericParamCaptures()) subs = SubstitutionMap(); - setCallee(Callee::forDirect(SGF, constant, subs, e)); - + // Check whether we have to dispatch to the original implementation of a + // dynamically_replaceable inside of a dynamic_replacement(for:) function. + ApplyExpr *thisCallSite = callSites.back(); + bool isObjCReplacementSelfCall = false; + bool isSelfCallToReplacedInDynamicReplacement = + isCallToReplacedInDynamicReplacement( + SGF, cast(constant.getDecl()), + isObjCReplacementSelfCall) && + (afd->getDeclContext()->isModuleScopeContext() || + thisCallSite->getArg()->isSelfExprOf( + cast(SGF.FunctionDC->getAsDecl()), false)); + + if (isSelfCallToReplacedInDynamicReplacement && !isObjCReplacementSelfCall) + setCallee(Callee::forDirect( + SGF, + SILDeclRef(cast(SGF.FunctionDC->getAsDecl()), + constant.kind), + subs, e, true)); + else + setCallee(Callee::forDirect(SGF, constant, subs, e)); + // If the decl ref requires captures, emit the capture params. if (!captureInfo.getCaptures().empty()) { SmallVector captures; @@ -1393,7 +1464,17 @@ class SILGenApply : public Lowering::ExprVisitor { ? SILDeclRef::Kind::Allocator : SILDeclRef::Kind::Initializer); - constant = constant.asForeign(requiresForeignEntryPoint(ctorRef->getDecl())); + bool isObjCReplacementSelfCall = false; + bool isSelfCallToReplacedInDynamicReplacement = + isCallToReplacedInDynamicReplacement( + SGF, cast(constant.getDecl()), + isObjCReplacementSelfCall) && + arg->isSelfExprOf( + cast(SGF.FunctionDC->getAsDecl()), false); + + constant = + constant.asForeign(!isObjCReplacementSelfCall && + requiresForeignEntryPoint(ctorRef->getDecl())); // Determine the callee. This is normally the allocating // entry point, unless we're delegating to an ObjC initializer. @@ -1402,15 +1483,24 @@ class SILGenApply : public Lowering::ExprVisitor { setCallee(Callee::forWitnessMethod( SGF, self.getType().getASTType(), constant, subs, expr)); - } else if ((useAllocatingCtor || constant.isForeign) - && getMethodDispatch(ctorRef->getDecl()) == MethodDispatch::Class) { + } else if ((useAllocatingCtor || constant.isForeign) && + !isSelfCallToReplacedInDynamicReplacement && + getMethodDispatch(ctorRef->getDecl()) == MethodDispatch::Class) { // Dynamic dispatch to the initializer. Scope S(SGF, expr); setCallee(Callee::forClassMethod( SGF, constant, subs, fn)); } else { // Directly call the peer constructor. - setCallee(Callee::forDirect(SGF, constant, subs, fn)); + if (isObjCReplacementSelfCall || + !isSelfCallToReplacedInDynamicReplacement) + setCallee(Callee::forDirect(SGF, constant, subs, fn)); + else + setCallee(Callee::forDirect( + SGF, + SILDeclRef(cast(SGF.FunctionDC->getAsDecl()), + constant.kind), + subs, fn, true)); } setSelfParam(std::move(selfArgSource), expr); @@ -5540,9 +5630,20 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &SGF, ArgumentSource &selfValue, bool isSuper, bool isDirectUse, - SubstitutionMap subs) { + SubstitutionMap subs, + bool isOnSelfParameter) { auto *decl = cast(constant.getDecl()); + bool isObjCReplacementSelfCall = false; + if (isOnSelfParameter && isCallToReplacedInDynamicReplacement( + SGF, decl, isObjCReplacementSelfCall)) { + return Callee::forDirect( + SGF, + SILDeclRef(cast(SGF.FunctionDC->getAsDecl()), + constant.kind), + subs, loc, true); + } + // The accessor might be a local function that does not capture any // generic parameters, in which case we don't want to pass in any // substitutions. @@ -5598,13 +5699,14 @@ emitSpecializedAccessorFunctionRef(SILGenFunction &SGF, SubstitutionMap substitutions, ArgumentSource &selfValue, bool isSuper, - bool isDirectUse) + bool isDirectUse, + bool isOnSelfParameter) { // Get the accessor function. The type will be a polymorphic function if // the Self type is generic. Callee callee = getBaseAccessorFunctionRef(SGF, loc, constant, selfValue, isSuper, isDirectUse, - substitutions); + substitutions, isOnSelfParameter); // Collect captures if the accessor has them. auto accessorFn = cast(constant.getDecl()); @@ -5890,18 +5992,18 @@ SILDeclRef SILGenModule::getAccessorDeclRef(AccessorDecl *accessor) { } /// Emit a call to a getter. -RValue SILGenFunction:: -emitGetAccessor(SILLocation loc, SILDeclRef get, - SubstitutionMap substitutions, - ArgumentSource &&selfValue, - bool isSuper, bool isDirectUse, - PreparedArguments &&subscriptIndices, SGFContext c) { +RValue SILGenFunction::emitGetAccessor(SILLocation loc, SILDeclRef get, + SubstitutionMap substitutions, + ArgumentSource &&selfValue, bool isSuper, + bool isDirectUse, + PreparedArguments &&subscriptIndices, + SGFContext c, bool isOnSelfParameter) { // Scope any further writeback just within this operation. FormalEvaluationScope writebackScope(*this); - Callee getter = emitSpecializedAccessorFunctionRef(*this, loc, get, - substitutions, selfValue, - isSuper, isDirectUse); + Callee getter = emitSpecializedAccessorFunctionRef( + *this, loc, get, substitutions, selfValue, isSuper, isDirectUse, + isOnSelfParameter); bool hasSelf = (bool)selfValue; CanAnyFunctionType accessType = getter.getSubstFormalType(); @@ -5927,13 +6029,14 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set, ArgumentSource &&selfValue, bool isSuper, bool isDirectUse, PreparedArguments &&subscriptIndices, - ArgumentSource &&setValue) { + ArgumentSource &&setValue, + bool isOnSelfParameter) { // Scope any further writeback just within this operation. FormalEvaluationScope writebackScope(*this); - Callee setter = emitSpecializedAccessorFunctionRef(*this, loc, set, - substitutions, selfValue, - isSuper, isDirectUse); + Callee setter = emitSpecializedAccessorFunctionRef( + *this, loc, set, substitutions, selfValue, isSuper, isDirectUse, + isOnSelfParameter); bool hasSelf = (bool)selfValue; CanAnyFunctionType accessType = setter.getSubstFormalType(); @@ -5972,20 +6075,17 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set, /// The first return value is the address, which will always be an /// l-value managed value. The second return value is the owner /// pointer, if applicable. -std::pair SILGenFunction:: -emitAddressorAccessor(SILLocation loc, SILDeclRef addressor, - SubstitutionMap substitutions, - ArgumentSource &&selfValue, - bool isSuper, bool isDirectUse, - PreparedArguments &&subscriptIndices, - SILType addressType) { +std::pair SILGenFunction::emitAddressorAccessor( + SILLocation loc, SILDeclRef addressor, SubstitutionMap substitutions, + ArgumentSource &&selfValue, bool isSuper, bool isDirectUse, + PreparedArguments &&subscriptIndices, SILType addressType, + bool isOnSelfParameter) { // Scope any further writeback just within this operation. FormalEvaluationScope writebackScope(*this); - Callee callee = - emitSpecializedAccessorFunctionRef(*this, loc, addressor, - substitutions, selfValue, - isSuper, isDirectUse); + Callee callee = emitSpecializedAccessorFunctionRef( + *this, loc, addressor, substitutions, selfValue, isSuper, isDirectUse, + isOnSelfParameter); bool hasSelf = (bool)selfValue; CanAnyFunctionType accessType = callee.getSubstFormalType(); @@ -6064,11 +6164,12 @@ SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor, ArgumentSource &&selfValue, bool isSuper, bool isDirectUse, PreparedArguments &&subscriptIndices, - SmallVectorImpl &yields) { + SmallVectorImpl &yields, + bool isOnSelfParameter) { Callee callee = emitSpecializedAccessorFunctionRef(*this, loc, accessor, substitutions, selfValue, - isSuper, isDirectUse); + isSuper, isDirectUse, isOnSelfParameter); // We're already in a full formal-evaluation scope. // Make a dead writeback scope; applyCoroutine won't try to pop this. diff --git a/lib/SILGen/SILGenBridging.cpp b/lib/SILGen/SILGenBridging.cpp index 5f63ffbf7eef2..eba396c655630 100644 --- a/lib/SILGen/SILGenBridging.cpp +++ b/lib/SILGen/SILGenBridging.cpp @@ -601,8 +601,8 @@ ManagedValue SILGenFunction::emitFuncToBlock(SILLocation loc, auto storage = emitTemporaryAllocation(loc, storageAddrTy); auto capture = B.createProjectBlockStorage(loc, storage); B.createStore(loc, fn, capture, StoreOwnershipQualifier::Init); - auto invokeFn = B.createFunctionRef(loc, thunk); - + auto invokeFn = B.createFunctionRefFor(loc, thunk); + auto stackBlock = B.createInitBlockStorageHeader(loc, storage, invokeFn, SILType::getPrimitiveObjectType(loweredBlockTy), subs); @@ -959,7 +959,7 @@ SILGenFunction::emitBlockToFunc(SILLocation loc, } // Create it in the current function. - auto thunkValue = B.createFunctionRef(loc, thunk); + auto thunkValue = B.createFunctionRefFor(loc, thunk); ManagedValue thunkedFn = B.createPartialApply( loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), interfaceSubs, block, @@ -1433,7 +1433,7 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) { // If @objc was inferred based on the Swift 3 @objc inference rules, emit // a call to Builtin.swift3ImplicitObjCEntrypoint() to enable runtime // logging of the uses of such entrypoints. - if (attr->isSwift3Inferred() && !decl->isDynamic()) { + if (attr->isSwift3Inferred() && !decl->isObjCDynamic()) { // Get the starting source location of the declaration so we can say // exactly where to stick '@objc'. SourceLoc objcInsertionLoc = diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index 983fb97e0a5ec..cb9d2aed6ec44 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -712,7 +712,7 @@ ManagedValue SILGenBuilder::createManagedOptionalNone(SILLocation loc, ManagedValue SILGenBuilder::createManagedFunctionRef(SILLocation loc, SILFunction *f) { - return ManagedValue::forUnmanaged(createFunctionRef(loc, f)); + return ManagedValue::forUnmanaged(createFunctionRefFor(loc, f)); } ManagedValue SILGenBuilder::createTupleElementAddr(SILLocation Loc, diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index f985f2bfc1800..951706ff4fd55 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -904,9 +904,9 @@ static SILValue getBehaviorInitStorageFn(SILGenFunction &SGF, SILGenFunctionBuilder builder(SGF); thunkFn = builder.getOrCreateFunction( SILLocation(behaviorVar), behaviorInitName, SILLinkage::PrivateExternal, - initConstantTy, IsBare, IsTransparent, IsSerialized); + initConstantTy, IsBare, IsTransparent, IsSerialized, IsNotDynamic); } - return SGF.B.createFunctionRef(behaviorVar, thunkFn); + return SGF.B.createFunctionRefFor(behaviorVar, thunkFn); } static SILValue getBehaviorSetterFn(SILGenFunction &SGF, VarDecl *behaviorVar) { @@ -915,7 +915,7 @@ static SILValue getBehaviorSetterFn(SILGenFunction &SGF, VarDecl *behaviorVar) { // TODO: The setter may need to be a thunk, to implode tuples or perform // reabstractions. - return SGF.B.createFunctionRef(behaviorVar, setFn); + return SGF.B.createFunctionRefFor(behaviorVar, setFn); } static Type getInitializationTypeInContext( diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index c58306dc9b9cd..2c5118a9f99e1 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -2690,7 +2690,7 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM, SILGenFunctionBuilder builder(SGM); auto thunk = builder.getOrCreateSharedFunction( loc, name, signature, IsBare, IsNotTransparent, IsNotSerialized, - ProfileCounter(), IsThunk); + ProfileCounter(), IsThunk, IsNotDynamic); if (!thunk->empty()) return thunk; @@ -2830,7 +2830,7 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM, SILGenFunctionBuilder builder(SGM); auto thunk = builder.getOrCreateSharedFunction( loc, name, signature, IsBare, IsNotTransparent, IsNotSerialized, - ProfileCounter(), IsThunk); + ProfileCounter(), IsThunk, IsNotDynamic); if (!thunk->empty()) return thunk; @@ -2989,7 +2989,7 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, SILGenFunctionBuilder builder(SGM); equals = builder.getOrCreateSharedFunction( loc, name, signature, IsBare, IsNotTransparent, IsNotSerialized, - ProfileCounter(), IsThunk); + ProfileCounter(), IsThunk, IsNotDynamic); if (!equals->empty()) { return; } @@ -3154,9 +3154,9 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM, auto name = Mangle::ASTMangler().mangleKeyPathHashHelper(indexTypes, genericSig); SILGenFunctionBuilder builder(SGM); - hash = builder.getOrCreateSharedFunction(loc, name, signature, IsBare, - IsNotTransparent, IsNotSerialized, - ProfileCounter(), IsThunk); + hash = builder.getOrCreateSharedFunction( + loc, name, signature, IsBare, IsNotTransparent, IsNotSerialized, + ProfileCounter(), IsThunk, IsNotDynamic); if (!hash->empty()) { return; } diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index ac949ba86513d..28f75902c94d7 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -140,7 +140,7 @@ SILGenFunction::emitSiblingMethodRef(SILLocation loc, // If the method is dynamic, access it through runtime-hookable virtual // dispatch (viz. objc_msgSend for now). if (methodConstant.hasDecl() - && methodConstant.getDecl()->isDynamic()) { + && methodConstant.getDecl()->isObjCDynamic()) { methodValue = emitDynamicMethodRef( loc, methodConstant, SGM.Types.getConstantInfo(methodConstant).SILFnType) @@ -495,7 +495,8 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { ctx); auto NSStringFromClassFn = builder.getOrCreateFunction( mainClass, "NSStringFromClass", SILLinkage::PublicExternal, - NSStringFromClassType, IsBare, IsTransparent, IsNotSerialized); + NSStringFromClassType, IsBare, IsTransparent, IsNotSerialized, + IsNotDynamic); auto NSStringFromClass = B.createFunctionRef(mainClass, NSStringFromClassFn); SILValue metaTy = B.createMetatype(mainClass, SILType::getPrimitiveObjectType(mainClassMetaty)); @@ -584,7 +585,8 @@ void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) { SILGenFunctionBuilder builder(SGM); auto NSApplicationMainFn = builder.getOrCreateFunction( mainClass, "NSApplicationMain", SILLinkage::PublicExternal, - NSApplicationMainType, IsBare, IsTransparent, IsNotSerialized); + NSApplicationMainType, IsBare, IsTransparent, IsNotSerialized, + IsNotDynamic); auto NSApplicationMain = B.createFunctionRef(mainClass, NSApplicationMainFn); SILValue args[] = { argc, argv }; diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index d017ed0e9c02b..b8a9f5d56f3b8 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1159,9 +1159,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction SILValue emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant) { return emitGlobalFunctionRef(loc, constant, getConstantInfo(constant)); } - SILValue emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, - SILConstantInfo constantInfo); - + SILValue + emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, + SILConstantInfo constantInfo, + bool callPreviousDynamicReplaceableImpl = false); + /// Returns a reference to a function value that dynamically dispatches /// the function in a runtime-modifiable way. ManagedValue emitDynamicMethodRef(SILLocation loc, SILDeclRef constant, @@ -1229,16 +1231,18 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction RValue emitGetAccessor(SILLocation loc, SILDeclRef getter, SubstitutionMap substitutions, - ArgumentSource &&optionalSelfValue, - bool isSuper, bool isDirectAccessorUse, - PreparedArguments &&optionalSubscripts, SGFContext C); + ArgumentSource &&optionalSelfValue, bool isSuper, + bool isDirectAccessorUse, + PreparedArguments &&optionalSubscripts, SGFContext C, + bool isOnSelfParameter); void emitSetAccessor(SILLocation loc, SILDeclRef setter, SubstitutionMap substitutions, ArgumentSource &&optionalSelfValue, bool isSuper, bool isDirectAccessorUse, PreparedArguments &&optionalSubscripts, - ArgumentSource &&value); + ArgumentSource &&value, + bool isOnSelfParameter); bool maybeEmitMaterializeForSetThunk(ProtocolConformanceRef conformance, SILLinkage linkage, @@ -1248,21 +1252,19 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction AccessorDecl *witness, SubstitutionMap witnessSubs); - std::pair - emitAddressorAccessor(SILLocation loc, SILDeclRef addressor, - SubstitutionMap substitutions, - ArgumentSource &&optionalSelfValue, - bool isSuper, bool isDirectAccessorUse, - PreparedArguments &&optionalSubscripts, - SILType addressType); - - CleanupHandle - emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor, - SubstitutionMap substitutions, - ArgumentSource &&optionalSelfValue, - bool isSuper, bool isDirectAccessorUse, - PreparedArguments &&optionalSubscripts, - SmallVectorImpl &yields); + std::pair emitAddressorAccessor( + SILLocation loc, SILDeclRef addressor, SubstitutionMap substitutions, + ArgumentSource &&optionalSelfValue, bool isSuper, + bool isDirectAccessorUse, PreparedArguments &&optionalSubscripts, + SILType addressType, bool isOnSelfParameter); + + CleanupHandle emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor, + SubstitutionMap substitutions, + ArgumentSource &&optionalSelfValue, + bool isSuper, bool isDirectAccessorUse, + PreparedArguments &&optionalSubscripts, + SmallVectorImpl &yields, + bool isOnSelfParameter); RValue emitApplyConversionFunction(SILLocation loc, Expr *funcExpr, diff --git a/lib/SILGen/SILGenGlobalVariable.cpp b/lib/SILGen/SILGenGlobalVariable.cpp index c875c5e3efc7e..88f9572292c56 100644 --- a/lib/SILGen/SILGenGlobalVariable.cpp +++ b/lib/SILGen/SILGenGlobalVariable.cpp @@ -67,7 +67,7 @@ SILGenFunction::emitGlobalVariableRef(SILLocation loc, VarDecl *var) { SILFunction *accessorFn = SGM.getFunction( SILDeclRef(var, SILDeclRef::Kind::GlobalAccessor), NotForDefinition); - SILValue accessor = B.createFunctionRef(loc, accessorFn); + SILValue accessor = B.createFunctionRefFor(loc, accessorFn); auto accessorTy = accessor->getType().castTo(); (void)accessorTy; assert(!accessorTy->isPolymorphic() @@ -247,7 +247,7 @@ static void emitOnceCall(SILGenFunction &SGF, VarDecl *global, rawPointerSILTy); // Emit a reference to the function to execute. - SILValue onceFuncRef = SGF.B.createFunctionRef(global, onceFunc); + SILValue onceFuncRef = SGF.B.createFunctionRefFor(global, onceFunc); // Call Builtin.once. SILValue onceArgs[] = {onceTokenAddr, onceFuncRef}; diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 7493847ed0ed6..07cab322103f9 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1218,23 +1218,23 @@ namespace { SILDeclRef Accessor; bool IsSuper; bool IsDirectAccessorUse; + bool IsOnSelfParameter; SubstitutionMap Substitutions; public: AccessorBasedComponent(PathComponent::KindTy kind, - AbstractStorageDecl *decl, - SILDeclRef accessor, + AbstractStorageDecl *decl, SILDeclRef accessor, bool isSuper, bool isDirectAccessorUse, SubstitutionMap substitutions, - CanType baseFormalType, - LValueTypeData typeData, + CanType baseFormalType, LValueTypeData typeData, Expr *indexExprForDiagnostics, - PreparedArguments &&indices) - : super(kind, decl, baseFormalType, typeData, - indexExprForDiagnostics, std::move(indices)), - Accessor(accessor), IsSuper(isSuper), - IsDirectAccessorUse(isDirectAccessorUse), - Substitutions(substitutions) {} + PreparedArguments &&indices, + bool isOnSelfParameter = false) + : super(kind, decl, baseFormalType, typeData, indexExprForDiagnostics, + std::move(indices)), + Accessor(accessor), IsSuper(isSuper), + IsDirectAccessorUse(isDirectAccessorUse), + IsOnSelfParameter(isOnSelfParameter), Substitutions(substitutions) {} AccessorBasedComponent(const AccessorBasedComponent &copied, SILGenFunction &SGF, @@ -1243,6 +1243,7 @@ namespace { Accessor(copied.Accessor), IsSuper(copied.IsSuper), IsDirectAccessorUse(copied.IsDirectAccessorUse), + IsOnSelfParameter(copied.IsOnSelfParameter), Substitutions(copied.Substitutions) {} AccessorDecl *getAccessorDecl() const { @@ -1261,11 +1262,12 @@ namespace { CanType baseFormalType, LValueTypeData typeData, Expr *subscriptIndexExpr, - PreparedArguments &&indices) + PreparedArguments &&indices, + bool isOnSelfParameter) : AccessorBasedComponent(GetterSetterKind, decl, accessor, isSuper, isDirectAccessorUse, substitutions, baseFormalType, typeData, subscriptIndexExpr, - std::move(indices)) + std::move(indices), isOnSelfParameter) { assert(getAccessorDecl()->isGetterOrSetter()); } @@ -1294,8 +1296,9 @@ namespace { dest.dropLastComponent(*this); return emitAssignWithSetter(SGF, loc, std::move(dest), baseFormalType, - isSuper, setter, isDirectAccessorUse, - subs, std::move(indices), std::move(value)); + isSuper, setter, isDirectAccessorUse, subs, + std::move(indices), std::move(value), + IsOnSelfParameter); } static void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, @@ -1304,7 +1307,8 @@ namespace { bool isDirectAccessorUse, SubstitutionMap subs, PreparedArguments &&indices, - ArgumentSource &&value) { + ArgumentSource &&value, + bool isSelfParameter) { ArgumentSource self = [&] { if (!baseLV.isValid()) { return ArgumentSource(); @@ -1317,9 +1321,9 @@ namespace { } }(); - return SGF.emitSetAccessor(loc, setter, subs, std::move(self), - isSuper, isDirectAccessorUse, - std::move(indices), std::move(value)); + return SGF.emitSetAccessor(loc, setter, subs, std::move(self), isSuper, + isDirectAccessorUse, std::move(indices), + std::move(value), isSelfParameter); } void set(SILGenFunction &SGF, SILLocation loc, @@ -1334,9 +1338,8 @@ namespace { return SGF.emitSetAccessor(loc, setter, Substitutions, std::move(args.base), IsSuper, - IsDirectAccessorUse, - std::move(args.Indices), - std::move(value)); + IsDirectAccessorUse, std::move(args.Indices), + std::move(value), IsOnSelfParameter); } ManagedValue project(SILGenFunction &SGF, SILLocation loc, @@ -1355,11 +1358,10 @@ namespace { auto args = std::move(*this).prepareAccessorArgs(SGF, loc, base, getter); - - return SGF.emitGetAccessor(loc, getter, Substitutions, - std::move(args.base), IsSuper, - IsDirectAccessorUse, - std::move(args.Indices), c); + + return SGF.emitGetAccessor( + loc, getter, Substitutions, std::move(args.base), IsSuper, + IsDirectAccessorUse, std::move(args.Indices), c, IsOnSelfParameter); } std::unique_ptr @@ -1390,6 +1392,7 @@ namespace { AccessStrategy WriteStrategy; LValueOptions Options; bool IsSuper; + bool IsOnSelfParameter; public: MaterializeToTemporaryComponent(AbstractStorageDecl *storage, @@ -1400,12 +1403,14 @@ namespace { CanType baseFormalType, LValueTypeData typeData, Expr *indexExprForDiagnostics, - PreparedArguments &&indices) + PreparedArguments &&indices, + bool isOnSelfParameter) : AccessComponent(MaterializeToTemporaryKind, storage, baseFormalType, typeData, indexExprForDiagnostics, std::move(indices)), Substitutions(subs), ReadStrategy(readStrategy), WriteStrategy(writeStrategy), - Options(options), IsSuper(isSuper) {} + Options(options), IsSuper(isSuper), IsOnSelfParameter(isOnSelfParameter) + {} std::unique_ptr @@ -1418,7 +1423,8 @@ namespace { ReadStrategy, WriteStrategy, BaseFormalType, getTypeData(), IndexExprForDiagnostics, - std::move(clonedIndices)); + std::move(clonedIndices), + IsOnSelfParameter); return std::unique_ptr(clone); } @@ -1494,11 +1500,12 @@ namespace { CanType baseFormalType, LValueTypeData typeData, SILType substFieldType, Expr *indexExprForDiagnostics, - PreparedArguments &&indices) + PreparedArguments &&indices, bool isOnSelfParameter) : AccessorBasedComponent(AddressorKind, decl, accessor, isSuper, isDirectAccessorUse, substitutions, baseFormalType, typeData, - indexExprForDiagnostics, std::move(indices)), + indexExprForDiagnostics, std::move(indices), + isOnSelfParameter), SubstFieldType(substFieldType) { assert(getAccessorDecl()->isAnyAddressor()); @@ -1516,9 +1523,9 @@ namespace { auto args = std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor); result = SGF.emitAddressorAccessor( - loc, Accessor, Substitutions, std::move(args.base), - IsSuper, IsDirectAccessorUse, - std::move(args.Indices), SubstFieldType); + loc, Accessor, Substitutions, std::move(args.base), IsSuper, + IsDirectAccessorUse, std::move(args.Indices), SubstFieldType, + IsOnSelfParameter); } switch (getAccessorDecl()->getAddressorKind()) { @@ -1597,19 +1604,17 @@ namespace { class CoroutineAccessorComponent : public AccessorBasedComponent { public: - CoroutineAccessorComponent(AbstractStorageDecl *decl, - SILDeclRef accessor, + CoroutineAccessorComponent(AbstractStorageDecl *decl, SILDeclRef accessor, bool isSuper, bool isDirectAccessorUse, SubstitutionMap substitutions, - CanType baseFormalType, - LValueTypeData typeData, + CanType baseFormalType, LValueTypeData typeData, Expr *indexExprForDiagnostics, - PreparedArguments &&indices) - : AccessorBasedComponent(CoroutineAccessorKind, - decl, accessor, isSuper, isDirectAccessorUse, - substitutions, baseFormalType, typeData, - indexExprForDiagnostics, std::move(indices)) { - } + PreparedArguments &&indices, + bool isOnSelfParameter) + : AccessorBasedComponent( + CoroutineAccessorKind, decl, accessor, isSuper, + isDirectAccessorUse, substitutions, baseFormalType, typeData, + indexExprForDiagnostics, std::move(indices), isOnSelfParameter) {} using AccessorBasedComponent::AccessorBasedComponent; @@ -1624,10 +1629,10 @@ namespace { std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor); auto peekedIndices = args.Indices.copyForDiagnostics(); SmallVector yields; - auto endApplyHandle = - SGF.emitCoroutineAccessor( + auto endApplyHandle = SGF.emitCoroutineAccessor( loc, Accessor, Substitutions, std::move(args.base), IsSuper, - IsDirectAccessorUse, std::move(args.Indices), yields); + IsDirectAccessorUse, std::move(args.Indices), yields, + IsOnSelfParameter); // Push a writeback that ends the access. std::unique_ptr @@ -2437,34 +2442,35 @@ void LValue::addNonMemberVarComponent(SILGenFunction &SGF, SILLocation loc, SGF.SGM.Types.getLoweredType(Storage->getType()).getAddressType(); LV.add(Storage, addressor, /*isSuper=*/false, isDirect, getSubs(), - CanType(), typeData, storageType, - nullptr, PreparedArguments()); + CanType(), typeData, storageType, nullptr, + PreparedArguments(), + /* isOnSelfParameter */ false); } void emitUsingCoroutineAccessor(SILDeclRef accessor, bool isDirect, LValueTypeData typeData) { - LV.add(Storage, accessor, - /*isSuper*/ false, isDirect, getSubs(), - CanType(), typeData, - nullptr, PreparedArguments()); + LV.add( + Storage, accessor, + /*isSuper*/ false, isDirect, getSubs(), CanType(), typeData, nullptr, + PreparedArguments(), /*isOnSelfParameter*/ false); } void emitUsingGetterSetter(SILDeclRef accessor, bool isDirect, LValueTypeData typeData) { - LV.add(Storage, accessor, - /*isSuper=*/false, isDirect, getSubs(), - CanType(), typeData, - nullptr, PreparedArguments()); + LV.add( + Storage, accessor, + /*isSuper=*/false, isDirect, getSubs(), CanType(), typeData, nullptr, + PreparedArguments(), /* isOnSelfParameter */ false); } void emitUsingMaterialization(AccessStrategy readStrategy, AccessStrategy writeStrategy, LValueTypeData typeData) { - LV.add(Storage, /*super*/false, - getSubs(), Options, - readStrategy, writeStrategy, - /*base type*/CanType(), typeData, - nullptr, PreparedArguments()); + LV.add( + Storage, /*super*/ false, getSubs(), Options, readStrategy, + writeStrategy, + /*base type*/ CanType(), typeData, nullptr, PreparedArguments(), + /* isOnSelfParameter */ false); } void emitUsingStorage(LValueTypeData typeData) { @@ -2800,16 +2806,57 @@ static CanType getBaseFormalType(Expr *baseExpr) { return baseExpr->getType()->getWithoutSpecifierType()->getCanonicalType(); } +bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF, + AbstractFunctionDecl *afd, + bool &isObjCReplacementSelfCall); + +static bool isCallToSelfOfCurrentFunction(SILGenFunction &SGF, LookupExpr *e) { + return SGF.FunctionDC->getAsDecl() && + isa(SGF.FunctionDC->getAsDecl()) && + e->getBase()->isSelfExprOf( + cast(SGF.FunctionDC->getAsDecl()), false); +} + +static bool isCurrentFunctionReadAccess(SILGenFunction &SGF) { + auto *contextAccessorDecl = + dyn_cast_or_null(SGF.FunctionDC->getAsDecl()); + return contextAccessorDecl && + contextAccessorDecl->getAccessorKind() == AccessorKind::Read; +} + LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, SGFAccessKind accessKind, LValueOptions options) { // MemberRefExpr can refer to type and function members, but the only case // that can be an lvalue is a VarDecl. VarDecl *var = cast(e->getMember().getDecl()); + + auto accessSemantics = e->getAccessSemantics(); AccessStrategy strategy = - var->getAccessStrategy(e->getAccessSemantics(), + var->getAccessStrategy(accessSemantics, getFormalAccessKind(accessKind), SGF.FunctionDC); + bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e); + + bool isContextRead = isCurrentFunctionReadAccess(SGF); + + // If we are inside _read, calling self.get, and the _read we are inside of is + // the same as the as self's variable and the current function is a + // dynamic replacement directly call the implementation. + if (isContextRead && isOnSelfParameter && strategy.hasAccessor() && + strategy.getAccessor() == AccessorKind::Get && + var->getAccessor(AccessorKind::Read)) { + bool isObjC = false; + auto readAccessor = + SGF.SGM.getAccessorDeclRef(var->getAccessor(AccessorKind::Read)); + if (isCallToReplacedInDynamicReplacement( + SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) { + accessSemantics = AccessSemantics::DirectToImplementation; + strategy = var->getAccessStrategy( + accessSemantics, getFormalAccessKind(accessKind), SGF.FunctionDC); + } + } + LValue lv = visitRec(e->getBase(), getBaseAccessKind(SGF.SGM, var, accessKind, strategy, getBaseFormalType(e->getBase())), @@ -2817,10 +2864,9 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, assert(lv.isValid()); CanType substFormalRValueType = getSubstFormalRValueType(e); - lv.addMemberVarComponent(SGF, e, var, - e->getMember().getSubstitutions(), - options, e->isSuper(), accessKind, - strategy, substFormalRValueType); + lv.addMemberVarComponent(SGF, e, var, e->getMember().getSubstitutions(), + options, e->isSuper(), accessKind, strategy, + substFormalRValueType, isOnSelfParameter); return lv; } @@ -2837,26 +2883,23 @@ struct MemberStorageAccessEmitter : AccessEmitter { LValueOptions Options; SILLocation Loc; bool IsSuper; + bool IsOnSelfParameter; // Is self the self parameter in context. CanType BaseFormalType; SubstitutionMap Subs; Expr *IndexExprForDiagnostics; PreparedArguments Indices; MemberStorageAccessEmitter(SILGenFunction &SGF, SILLocation loc, - StorageType *storage, - SubstitutionMap subs, - bool isSuper, - SGFAccessKind accessKind, - CanType formalRValueType, - LValueOptions options, - LValue &lv, - Expr *indexExprForDiagnostics, - PreparedArguments &&indices) - : super(SGF, storage, accessKind, formalRValueType), - LV(lv), Options(options), Loc(loc), IsSuper(isSuper), - BaseFormalType(lv.getSubstFormalType()), Subs(subs), - IndexExprForDiagnostics(indexExprForDiagnostics), - Indices(std::move(indices)) {} + StorageType *storage, SubstitutionMap subs, + bool isSuper, SGFAccessKind accessKind, + CanType formalRValueType, LValueOptions options, + LValue &lv, Expr *indexExprForDiagnostics, + PreparedArguments &&indices, bool isSelf = false) + : super(SGF, storage, accessKind, formalRValueType), LV(lv), + Options(options), Loc(loc), IsSuper(isSuper), IsOnSelfParameter(isSelf), + BaseFormalType(lv.getSubstFormalType()), Subs(subs), + IndexExprForDiagnostics(indexExprForDiagnostics), + Indices(std::move(indices)) {} void emitUsingAddressor(SILDeclRef addressor, bool isDirect, LValueTypeData typeData) { @@ -2865,32 +2908,31 @@ struct MemberStorageAccessEmitter : AccessEmitter { LV.add(Storage, addressor, IsSuper, isDirect, Subs, BaseFormalType, typeData, varStorageType, - IndexExprForDiagnostics, std::move(Indices)); + IndexExprForDiagnostics, std::move(Indices), + IsOnSelfParameter); } void emitUsingCoroutineAccessor(SILDeclRef accessor, bool isDirect, LValueTypeData typeData) { - LV.add(Storage, accessor, IsSuper, isDirect, - Subs, BaseFormalType, typeData, - IndexExprForDiagnostics, - std::move(Indices)); + LV.add( + Storage, accessor, IsSuper, isDirect, Subs, BaseFormalType, typeData, + IndexExprForDiagnostics, std::move(Indices), IsOnSelfParameter); } void emitUsingGetterSetter(SILDeclRef accessor, bool isDirect, LValueTypeData typeData) { - LV.add(Storage, accessor, IsSuper, isDirect, - Subs, BaseFormalType, typeData, - IndexExprForDiagnostics, std::move(Indices)); + LV.add( + Storage, accessor, IsSuper, isDirect, Subs, BaseFormalType, typeData, + IndexExprForDiagnostics, std::move(Indices), IsOnSelfParameter); } void emitUsingMaterialization(AccessStrategy readStrategy, AccessStrategy writeStrategy, LValueTypeData typeData) { - LV.add(Storage, IsSuper, Subs, Options, - readStrategy, writeStrategy, - BaseFormalType, typeData, - IndexExprForDiagnostics, - std::move(Indices)); + LV.add( + Storage, IsSuper, Subs, Options, readStrategy, writeStrategy, + BaseFormalType, typeData, IndexExprForDiagnostics, std::move(Indices), + IsOnSelfParameter); } }; } // end anonymous namespace @@ -2902,7 +2944,8 @@ void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, bool isSuper, SGFAccessKind accessKind, AccessStrategy strategy, - CanType formalRValueType) { + CanType formalRValueType, + bool isOnSelfParameter) { struct MemberVarAccessEmitter : MemberStorageAccessEmitter { using MemberStorageAccessEmitter::MemberStorageAccessEmitter; @@ -2953,7 +2996,8 @@ void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc, } } emitter(SGF, loc, var, subs, isSuper, accessKind, formalRValueType, options, *this, - /*indices for diags*/ nullptr, /*indices*/ PreparedArguments()); + /*indices for diags*/ nullptr, /*indices*/ PreparedArguments(), + isOnSelfParameter); emitter.emitUsingStrategy(strategy); } @@ -2964,11 +3008,32 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, auto decl = cast(e->getDecl().getDecl()); auto subs = e->getDecl().getSubstitutions(); + auto accessSemantics = e->getAccessSemantics(); auto strategy = decl->getAccessStrategy(accessSemantics, getFormalAccessKind(accessKind), SGF.FunctionDC); - + + bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e); + bool isContextRead = isCurrentFunctionReadAccess(SGF); + + // If we are inside _read, calling self.get, and the _read we are inside of is + // the same as the as self's variable and the current function is a + // dynamic replacement directly call the implementation. + if (isContextRead && isOnSelfParameter && strategy.hasAccessor() && + strategy.getAccessor() == AccessorKind::Get && + decl->getAccessor(AccessorKind::Read)) { + bool isObjC = false; + auto readAccessor = + SGF.SGM.getAccessorDeclRef(decl->getAccessor(AccessorKind::Read)); + if (isCallToReplacedInDynamicReplacement( + SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) { + accessSemantics = AccessSemantics::DirectToImplementation; + strategy = decl->getAccessStrategy( + accessSemantics, getFormalAccessKind(accessKind), SGF.FunctionDC); + } + } + LValue lv = visitRec(e->getBase(), getBaseAccessKind(SGF.SGM, decl, accessKind, strategy, getBaseFormalType(e->getBase())), @@ -2982,7 +3047,7 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, lv.addMemberSubscriptComponent(SGF, e, decl, subs, options, e->isSuper(), accessKind, strategy, formalRValueType, std::move(indices), - indexExpr); + indexExpr, isOnSelfParameter); return lv; } @@ -3039,7 +3104,8 @@ void LValue::addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc, AccessStrategy strategy, CanType formalRValueType, PreparedArguments &&indices, - Expr *indexExprForDiagnostics) { + Expr *indexExprForDiagnostics, + bool isOnSelfParameter) { struct MemberSubscriptAccessEmitter : MemberStorageAccessEmitter { @@ -3052,9 +3118,9 @@ void LValue::addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc, void emitUsingBehaviorStorage() { llvm_unreachable("subscripts never have behaviors"); } - } emitter(SGF, loc, decl, subs, isSuper, - accessKind, formalRValueType, options, *this, - indexExprForDiagnostics, std::move(indices)); + } emitter(SGF, loc, decl, subs, isSuper, accessKind, formalRValueType, + options, *this, indexExprForDiagnostics, std::move(indices), + isOnSelfParameter); emitter.emitUsingStrategy(strategy); } diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index fa7a8de8f198b..af6f728aa56dc 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -3167,7 +3167,7 @@ static ManagedValue createThunk(SILGenFunction &SGF, } // Create it in our current function. - auto thunkValue = SGF.B.createFunctionRef(loc, thunk); + auto thunkValue = SGF.B.createFunctionRefFor(loc, thunk); ManagedValue thunkedFn = SGF.B.createPartialApply(loc, thunkValue, SILType::getPrimitiveObjectType(substFnType), @@ -3275,7 +3275,7 @@ SILGenFunction::createWithoutActuallyEscapingClosure( } // Create it in our current function. - auto thunkValue = B.createFunctionRef(loc, thunk); + auto thunkValue = B.createFunctionRefFor(loc, thunk); SILValue noEscapeValue = noEscapingFunctionValue.ensurePlusOne(*this, loc).forward(*this); SingleValueInstruction *thunkedFn = B.createPartialApply( @@ -3552,7 +3552,7 @@ SILGenFunction::emitVTableThunk(SILDeclRef derived, forwardFunctionArguments(*this, loc, fTy, substArgs, args); // Create the call. - auto implRef = B.createFunctionRef(loc, implFn); + auto implRef = B.createFunctionRefFor(loc, implFn); SILValue implResult = emitApplyWithRethrow(loc, implRef, SILType::getPrimitiveObjectType(fTy), subs, args); @@ -3586,7 +3586,7 @@ static WitnessDispatchKind getWitnessDispatchKind(SILDeclRef witness) { return WitnessDispatchKind::Static; // If the witness is dynamic, go through dynamic dispatch. - if (decl->isDynamic()) { + if (decl->isObjCDynamic()) { // For initializers we still emit a static allocating thunk around // the dynamic initializing entry point. if (witness.kind == SILDeclRef::Kind::Allocator) @@ -3958,7 +3958,7 @@ SILGenFunction::emitCanonicalFunctionThunk(SILLocation loc, ManagedValue fn, } // Create it in the current function. - auto thunkValue = B.createFunctionRef(loc, thunk); + auto thunkValue = B.createFunctionRefFor(loc, thunk); ManagedValue thunkedFn = B.createPartialApply( loc, thunkValue, SILType::getPrimitiveObjectType(substFnTy), interfaceSubs, {fn}, diff --git a/lib/SILGen/SILGenThunk.cpp b/lib/SILGen/SILGenThunk.cpp index 7a143bf086d3f..baaa856691049 100644 --- a/lib/SILGen/SILGenThunk.cpp +++ b/lib/SILGen/SILGenThunk.cpp @@ -54,7 +54,7 @@ SILFunction *SILGenModule::getDynamicThunk(SILDeclRef constant, SILGenFunctionBuilder builder(*this); auto F = builder.getOrCreateFunction( constant.getDecl(), name, SILLinkage::Shared, constantTy, IsBare, - IsTransparent, IsSerializable, ProfileCounter(), IsThunk); + IsTransparent, IsSerializable, IsNotDynamic, ProfileCounter(), IsThunk); if (F->empty()) { // Emit the thunk if we haven't yet. @@ -76,14 +76,14 @@ SILGenFunction::emitDynamicMethodRef(SILLocation loc, SILDeclRef constant, if (constant.isForeignToNativeThunk()) { if (!SGM.hasFunction(constant)) SGM.emitForeignToNativeThunk(constant); - return ManagedValue::forUnmanaged( - B.createFunctionRef(loc, SGM.getFunction(constant, NotForDefinition))); + return ManagedValue::forUnmanaged(B.createFunctionRefFor( + loc, SGM.getFunction(constant, NotForDefinition))); } // Otherwise, we need a dynamic dispatch thunk. SILFunction *F = SGM.getDynamicThunk(constant, constantTy); - return ManagedValue::forUnmanaged(B.createFunctionRef(loc, F)); + return ManagedValue::forUnmanaged(B.createFunctionRefFor(loc, F)); } static ManagedValue getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, @@ -110,8 +110,9 @@ static ManagedValue getNextUncurryLevelRef(SILGenFunction &SGF, SILLocation loc, if (auto *func = dyn_cast(vd)) { if (getMethodDispatch(func) == MethodDispatch::Class) { // Use the dynamic thunk if dynamic. - if (vd->isDynamic()) + if (vd->isObjCDynamic()) { return SGF.emitDynamicMethodRef(loc, next, constantInfo.SILFnType); + } auto methodTy = SGF.SGM.Types.getConstantOverrideType(next); SILValue result = @@ -237,9 +238,10 @@ void SILGenModule::emitNativeToForeignThunk(SILDeclRef thunk) { postEmitFunction(thunk, f); } -SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc, - SILDeclRef constant, - SILConstantInfo constantInfo) { +SILValue +SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant, + SILConstantInfo constantInfo, + bool callPreviousDynamicReplaceableImpl) { assert(constantInfo == getConstantInfo(constant)); // Builtins must be fully applied at the point of reference. @@ -265,7 +267,10 @@ SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc, auto f = SGM.getFunction(constant, NotForDefinition); assert(f->getLoweredFunctionType() == constantInfo.SILFnType); - return B.createFunctionRef(loc, f); + if (callPreviousDynamicReplaceableImpl) + return B.createPreviousDynamicFunctionRef(loc, f); + else + return B.createFunctionRefFor(loc, f); } SILFunction *SILGenModule:: @@ -294,5 +299,5 @@ getOrCreateReabstractionThunk(CanSILFunctionType thunkType, SILGenFunctionBuilder builder(*this); return builder.getOrCreateSharedFunction( loc, name, thunkDeclType, IsBare, IsTransparent, IsSerializable, - ProfileCounter(), IsReabstractionThunk); + ProfileCounter(), IsReabstractionThunk, IsNotDynamic); } diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 170c8cc3702bb..dce91b4984427 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -80,7 +80,7 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, // If the member is dynamic, reference its dynamic dispatch thunk so that // it will be redispatched, funneling the method call through the runtime // hook point. - if (derivedDecl->isDynamic() + if (derivedDecl->isObjCDynamic() && derived.kind != SILDeclRef::Kind::Allocator) { implFn = getDynamicThunk(derived, Types.getConstantInfo(derived).SILFnType); implLinkage = SILLinkage::Public; @@ -135,7 +135,7 @@ SILGenModule::emitVTableMethod(ClassDecl *theClass, auto thunk = builder.createFunction( SILLinkage::Private, name, overrideInfo.SILFnType, cast(derivedDecl)->getGenericEnvironment(), loc, - IsBare, IsNotTransparent, IsNotSerialized); + IsBare, IsNotTransparent, IsNotSerialized, IsNotDynamic); thunk->setDebugScope(new (M) SILDebugScope(loc, thunk)); SILGenFunction(*this, *thunk, theClass) @@ -684,7 +684,8 @@ SILFunction *SILGenModule::emitProtocolWitness( auto *f = builder.createFunction( linkage, nameBuffer, witnessSILFnType, genericEnv, SILLocation(witnessRef.getDecl()), IsNotBare, IsTransparent, isSerialized, - ProfileCounter(), IsThunk, SubclassScope::NotApplicable, InlineStrategy); + IsNotDynamic, ProfileCounter(), IsThunk, SubclassScope::NotApplicable, + InlineStrategy); f->setDebugScope(new (M) SILDebugScope(RegularLocation(witnessRef.getDecl()), f)); diff --git a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp index d1a34de831f41..a0f8db02a75db 100644 --- a/lib/SILOptimizer/Analysis/ARCAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/ARCAnalysis.cpp @@ -136,6 +136,8 @@ bool swift::canNeverUseValues(SILInstruction *Inst) { switch (Inst->getKind()) { // These instructions do not use other values. case SILInstructionKind::FunctionRefInst: + case SILInstructionKind::DynamicFunctionRefInst: + case SILInstructionKind::PreviousDynamicFunctionRefInst: case SILInstructionKind::IntegerLiteralInst: case SILInstructionKind::FloatLiteralInst: case SILInstructionKind::StringLiteralInst: diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index 52ab994f1a3db..66e43eced53cd 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -227,8 +227,8 @@ void AccessSummaryAnalysis::processPartialApply(FunctionInfo *callerInfo, // Make sure the partial_apply is not calling the result of another // partial_apply. - assert(isa(apply->getCallee()) && - "Noescape partial apply of non-functionref?"); + assert(isa(apply->getCallee()) + && "Noescape partial apply of non-functionref?"); assert(llvm::all_of(apply->getUses(), hasExpectedUsesOfNoEscapePartialApply) && diff --git a/lib/SILOptimizer/Analysis/ArraySemantic.cpp b/lib/SILOptimizer/Analysis/ArraySemantic.cpp index bde7e348415bc..85aaa125bd9fc 100644 --- a/lib/SILOptimizer/Analysis/ArraySemantic.cpp +++ b/lib/SILOptimizer/Analysis/ArraySemantic.cpp @@ -731,12 +731,13 @@ bool swift::ArraySemanticsCall::replaceByAppendingValues( SILValue ArrRef = SemanticsCall->getArgument(1); SILBuilderWithScope Builder(SemanticsCall); auto Loc = SemanticsCall->getLoc(); - auto *FnRef = Builder.createFunctionRef(Loc, AppendFn); + auto *FnRef = Builder.createFunctionRefFor(Loc, AppendFn); if (Vals.size() > 1) { // Create a call to reserveCapacityForAppend() to reserve space for multiple // elements. - FunctionRefInst *ReserveFnRef = Builder.createFunctionRef(Loc, ReserveFn); + FunctionRefBaseInst *ReserveFnRef = + Builder.createFunctionRefFor(Loc, ReserveFn); SILFunctionType *ReserveFnTy = ReserveFnRef->getType().castTo(); assert(ReserveFnTy->getNumParameters() == 2); diff --git a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp index 78402fb32c95b..5d7af6e3f8343 100644 --- a/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp @@ -241,6 +241,10 @@ CalleeList CalleeCache::getCalleeListForCalleeKind(SILValue Callee) const { case ValueKind::FunctionRefInst: return CalleeList(cast(Callee)->getReferencedFunction()); + case ValueKind::DynamicFunctionRefInst: + case ValueKind::PreviousDynamicFunctionRefInst: + return CalleeList(); // Don't know the dynamic target. + case ValueKind::PartialApplyInst: return getCalleeListForCalleeKind( cast(Callee)->getCallee()); diff --git a/lib/SILOptimizer/Analysis/CallerAnalysis.cpp b/lib/SILOptimizer/Analysis/CallerAnalysis.cpp index be7a51ac1082b..950d97d93cf4d 100644 --- a/lib/SILOptimizer/Analysis/CallerAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/CallerAnalysis.cpp @@ -55,7 +55,17 @@ struct CallerAnalysis::ApplySiteFinderVisitor ~ApplySiteFinderVisitor(); bool visitSILInstruction(SILInstruction *) { return false; } - bool visitFunctionRefInst(FunctionRefInst *fri); + bool visitFunctionRefInst(FunctionRefInst *fri) { + return visitFunctionRefBaseInst(fri); + } + bool visitDynamicFunctionRefInst(DynamicFunctionRefInst *fri) { + return visitFunctionRefBaseInst(fri); + } + bool + visitPreviousDynamicFunctionRefInst(PreviousDynamicFunctionRefInst *fri) { + return visitFunctionRefBaseInst(fri); + } + bool visitFunctionRefBaseInst(FunctionRefBaseInst *fri); void process(); void processApplySites(ArrayRef applySites); @@ -100,8 +110,8 @@ CallerAnalysis::ApplySiteFinderVisitor::~ApplySiteFinderVisitor() { #endif } -bool CallerAnalysis::ApplySiteFinderVisitor::visitFunctionRefInst( - FunctionRefInst *fri) { +bool CallerAnalysis::ApplySiteFinderVisitor::visitFunctionRefBaseInst( + FunctionRefBaseInst *fri) { auto optResult = findLocalApplySites(fri); auto *calleeFn = fri->getReferencedFunction(); FunctionInfo &calleeInfo = analysis->unsafeGetFunctionInfo(calleeFn); diff --git a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp index b9fe0d8b2df4a..82b3fd03f9256 100644 --- a/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/EscapeAnalysis.cpp @@ -65,6 +65,8 @@ static SingleValueInstruction *isProjection(SILNode *node) { static bool isNonWritableMemoryAddress(SILNode *V) { switch (V->getKind()) { case SILNodeKind::FunctionRefInst: + case SILNodeKind::DynamicFunctionRefInst: + case SILNodeKind::PreviousDynamicFunctionRefInst: case SILNodeKind::WitnessMethodInst: case SILNodeKind::ClassMethodInst: case SILNodeKind::SuperMethodInst: @@ -105,7 +107,8 @@ void EscapeAnalysis::ConnectionGraph::clear() { EscapeAnalysis::CGNode *EscapeAnalysis::ConnectionGraph:: getNode(ValueBase *V, EscapeAnalysis *EA, bool createIfNeeded) { - if (isa(V)) + if (isa(V) || isa(V) || + isa(V)) return nullptr; if (!EA->isPointer(V)) @@ -1612,7 +1615,8 @@ bool EscapeAnalysis::deinitIsKnownToNotCapture(SILValue V) { if (V->getType().is()) return true; - if (isa(V)) + if (isa(V) || isa(V) || + isa(V)) return true; // Check all operands of a partial_apply diff --git a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp index f823b431e03d9..db30ddf7d5328 100644 --- a/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/SideEffectAnalysis.cpp @@ -353,6 +353,14 @@ bool FunctionSideEffects::setDefinedEffects(SILFunction *F) { // FunctionSideEffects object without visiting its body. bool FunctionSideEffects::summarizeFunction(SILFunction *F) { assert(ParamEffects.empty() && "Expect uninitialized effects."); + + if (F->isDynamicallyReplaceable()) { + LLVM_DEBUG(llvm::dbgs() + << " -- is dynamically_replaceable " << F->getName() << '\n'); + setWorstEffects(); + return true; + } + if (!F->empty()) ParamEffects.resize(F->getArguments().size()); diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp index 6eebe616b68f7..54eb7f193f64c 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp @@ -384,7 +384,7 @@ void ExistentialTransform::populateThunkBody() { auto Loc = ThunkBody->getParent()->getLocation(); /// Create the function_ref instruction to the NewF. - auto *FRI = Builder.createFunctionRef(Loc, NewF); + auto *FRI = Builder.createFunctionRefFor(Loc, NewF); auto GenCalleeType = NewF->getLoweredFunctionType(); auto CalleeGenericSig = GenCalleeType->getGenericSignature(); @@ -518,9 +518,9 @@ void ExistentialTransform::createExistentialSpecializedFunction() { /// Step 1: Create the new protocol constrained generic function. NewF = FunctionBuilder.createFunction( linkage, Name, NewFTy, NewFGenericEnv, F->getLocation(), F->isBare(), - F->isTransparent(), F->isSerialized(), F->getEntryCount(), F->isThunk(), - F->getClassSubclassScope(), F->getInlineStrategy(), F->getEffectsKind(), - nullptr, F->getDebugScope()); + F->isTransparent(), F->isSerialized(), IsNotDynamic, F->getEntryCount(), + F->isThunk(), F->getClassSubclassScope(), F->getInlineStrategy(), + F->getEffectsKind(), nullptr, F->getDebugScope()); /// Set the semantics attributes for the new function. for (auto &Attr : F->getSemanticsAttrs()) NewF->addSemanticsAttr(Attr); diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index 6471a485c9913..a8e27f2ad392c 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -501,7 +501,8 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // classSubclassScope. TransformDescriptor.OptimizedFunction = FunctionBuilder.createFunction( linkage, Name, NewFTy, NewFGenericEnv, F->getLocation(), F->isBare(), - F->isTransparent(), F->isSerialized(), F->getEntryCount(), F->isThunk(), + F->isTransparent(), F->isSerialized(), IsNotDynamic, F->getEntryCount(), + F->isThunk(), /*classSubclassScope=*/SubclassScope::NotApplicable, F->getInlineStrategy(), F->getEffectsKind(), nullptr, F->getDebugScope()); SILFunction *NewF = TransformDescriptor.OptimizedFunction.get(); @@ -742,6 +743,9 @@ class FunctionSignatureOpts : public SILFunctionTransform { if (!F->shouldOptimize()) return; + if (F->isDynamicallyReplaceable()) + return; + // This is the function to optimize. LLVM_DEBUG(llvm::dbgs() << "*** FSO on function: " << F->getName() << " ***\n"); diff --git a/lib/SILOptimizer/IPO/CapturePromotion.cpp b/lib/SILOptimizer/IPO/CapturePromotion.cpp index 010579441f426..3f01e1506f11a 100644 --- a/lib/SILOptimizer/IPO/CapturePromotion.cpp +++ b/lib/SILOptimizer/IPO/CapturePromotion.cpp @@ -435,9 +435,9 @@ ClosureCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, auto *Fn = FunctionBuilder.createFunction( Orig->getLinkage(), ClonedName, ClonedTy, Orig->getGenericEnvironment(), Orig->getLocation(), Orig->isBare(), IsNotTransparent, Serialized, - Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), - Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, - Orig->getDebugScope()); + IsNotDynamic, Orig->getEntryCount(), Orig->isThunk(), + Orig->getClassSubclassScope(), Orig->getInlineStrategy(), + Orig->getEffectsKind(), Orig, Orig->getDebugScope()); for (auto &Attr : Orig->getSemanticsAttrs()) Fn->addSemanticsAttr(Attr); if (!Orig->hasQualifiedOwnership()) { diff --git a/lib/SILOptimizer/IPO/CapturePropagation.cpp b/lib/SILOptimizer/IPO/CapturePropagation.cpp index 6c2d13dde3c78..0536192c0f057 100644 --- a/lib/SILOptimizer/IPO/CapturePropagation.cpp +++ b/lib/SILOptimizer/IPO/CapturePropagation.cpp @@ -261,7 +261,7 @@ SILFunction *CapturePropagation::specializeConstClosure(PartialApplyInst *PAI, SILOptFunctionBuilder FuncBuilder(*this); SILFunction *NewF = FuncBuilder.createFunction( SILLinkage::Shared, Name, NewFTy, GenericEnv, OrigF->getLocation(), - OrigF->isBare(), OrigF->isTransparent(), Serialized, + OrigF->isBare(), OrigF->isTransparent(), Serialized, IsNotDynamic, OrigF->getEntryCount(), OrigF->isThunk(), OrigF->getClassSubclassScope(), OrigF->getInlineStrategy(), OrigF->getEffectsKind(), /*InsertBefore*/ OrigF, OrigF->getDebugScope()); diff --git a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp index f102ee7d6f3f2..87648977c739a 100644 --- a/lib/SILOptimizer/IPO/ClosureSpecializer.cpp +++ b/lib/SILOptimizer/IPO/ClosureSpecializer.cpp @@ -659,7 +659,7 @@ ClosureSpecCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, getSpecializedLinkage(ClosureUser, ClosureUser->getLinkage()), ClonedName, ClonedTy, ClosureUser->getGenericEnvironment(), ClosureUser->getLocation(), IsBare, ClosureUser->isTransparent(), - CallSiteDesc.isSerialized(), ClosureUser->getEntryCount(), + CallSiteDesc.isSerialized(), IsNotDynamic, ClosureUser->getEntryCount(), ClosureUser->isThunk(), /*classSubclassScope=*/SubclassScope::NotApplicable, ClosureUser->getInlineStrategy(), ClosureUser->getEffectsKind(), diff --git a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp index f9ecb5314def0..ad715e71923a6 100644 --- a/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp +++ b/lib/SILOptimizer/IPO/DeadFunctionElimination.cpp @@ -362,6 +362,10 @@ class FunctionLivenessComputation { ensureAliveClassMethod(mi, dyn_cast(funcDecl), MethodCl); } else if (auto *FRI = dyn_cast(&I)) { ensureAlive(FRI->getReferencedFunction()); + } else if (auto *FRI = dyn_cast(&I)) { + ensureAlive(FRI->getReferencedFunction()); + } else if (auto *FRI = dyn_cast(&I)) { + ensureAlive(FRI->getReferencedFunction()); } else if (auto *KPI = dyn_cast(&I)) { for (auto &component : KPI->getPattern()->getComponents()) ensureKeyPathComponentIsAlive(component); diff --git a/lib/SILOptimizer/IPO/EagerSpecializer.cpp b/lib/SILOptimizer/IPO/EagerSpecializer.cpp index 0cdf70dff50d4..49cad1d9bbdb6 100644 --- a/lib/SILOptimizer/IPO/EagerSpecializer.cpp +++ b/lib/SILOptimizer/IPO/EagerSpecializer.cpp @@ -755,6 +755,9 @@ void EagerSpecializerTransform::run() { if (F.isExternalDeclaration() || F.isAvailableExternally()) continue; + if (F.isDynamicallyReplaceable()) + continue; + if (!F.getLoweredFunctionType()->getGenericSignature()) continue; diff --git a/lib/SILOptimizer/IPO/GlobalOpt.cpp b/lib/SILOptimizer/IPO/GlobalOpt.cpp index 9ee51ecc8d4db..6eb97d0e34124 100644 --- a/lib/SILOptimizer/IPO/GlobalOpt.cpp +++ b/lib/SILOptimizer/IPO/GlobalOpt.cpp @@ -274,9 +274,9 @@ static SILFunction *getGlobalGetterFunction(SILOptFunctionBuilder &FunctionBuild /*params*/ {}, /*yields*/ {}, Results, None, M.getASTContext()); auto getterName = M.allocateCopy(getterNameTmp); - return FunctionBuilder.getOrCreateFunction(loc, getterName, Linkage, - LoweredType, IsBare, - IsNotTransparent, Serialized); + return FunctionBuilder.getOrCreateFunction( + loc, getterName, Linkage, LoweredType, IsBare, IsNotTransparent, + Serialized, IsNotDynamic); } /// Generate getter from the initialization code whose result is stored by a diff --git a/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp b/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp index bdbf2ba918515..8a0f4a4e884ac 100644 --- a/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp +++ b/lib/SILOptimizer/LoopTransforms/LoopRotate.cpp @@ -72,6 +72,13 @@ canDuplicateOrMoveToPreheader(SILLoop *L, SILBasicBlock *Preheader, else if (isa(Inst)) { Move.push_back(Inst); Invariant.insert(Inst); + } else if (isa(Inst)) { + Move.push_back(Inst); + Invariant.insert(Inst); + } + else if (isa(Inst)) { + Move.push_back(Inst); + Invariant.insert(Inst); } else if (isa(Inst)) { Move.push_back(Inst); Invariant.insert(Inst); diff --git a/lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp b/lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp index 091925ab45e3a..091a8d496ce65 100644 --- a/lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp +++ b/lib/SILOptimizer/Mandatory/GuaranteedARCOpts.cpp @@ -129,7 +129,8 @@ static bool couldReduceStrongRefcount(SILInstruction *Inst) { bool GuaranteedARCOptsVisitor::visitStrongReleaseInst(StrongReleaseInst *SRI) { SILValue Operand = SRI->getOperand(); // Release on a functionref is a noop. - if (isa(Operand)) { + if (isa(Operand) || isa(Operand) || + isa(Operand)) { SRI->eraseFromParent(); ++NumInstsEliminated; return true; diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index c0496e8a2fe43..a44182f1f6e2c 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -609,9 +609,9 @@ initCloned(SILOptFunctionBuilder &FuncBuilder, SILFunction *Orig, auto *Fn = FuncBuilder.createFunction( SILLinkage::Shared, ClonedName, ClonedTy, Orig->getGenericEnvironment(), Orig->getLocation(), Orig->isBare(), IsNotTransparent, Serialized, - Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), - Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, - Orig->getDebugScope()); + IsNotDynamic, Orig->getEntryCount(), Orig->isThunk(), + Orig->getClassSubclassScope(), Orig->getInlineStrategy(), + Orig->getEffectsKind(), Orig, Orig->getDebugScope()); for (auto &Attr : Orig->getSemanticsAttrs()) { Fn->addSemanticsAttr(Attr); } diff --git a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp index ace4f64d0ec30..9037c6ab78add 100644 --- a/lib/SILOptimizer/Transforms/GenericSpecializer.cpp +++ b/lib/SILOptimizer/Transforms/GenericSpecializer.cpp @@ -97,7 +97,7 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) { SILFunction *Callee = Apply.getReferencedFunction(); assert(Callee && "Expected to have a known callee"); - if (!Callee->shouldOptimize()) + if (!Apply.canOptimize() || !Callee->shouldOptimize()) continue; // We have a call that can potentially be specialized, so diff --git a/lib/SILOptimizer/Transforms/Outliner.cpp b/lib/SILOptimizer/Transforms/Outliner.cpp index 410aae8882f12..3c8c43979a133 100644 --- a/lib/SILOptimizer/Transforms/Outliner.cpp +++ b/lib/SILOptimizer/Transforms/Outliner.cpp @@ -322,7 +322,7 @@ BridgedProperty::outline(SILModule &M) { auto *Fun = FuncBuilder.getOrCreateFunction( ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare, - IsNotTransparent, IsSerializable); + IsNotTransparent, IsSerializable, IsNotDynamic); bool NeedsDefinition = Fun->empty(); if (Release) { @@ -933,7 +933,7 @@ ObjCMethodCall::outline(SILModule &M) { auto *Fun = FuncBuilder.getOrCreateFunction( ObjCMethod->getLoc(), name, SILLinkage::Shared, FunctionType, IsNotBare, - IsNotTransparent, IsSerializable); + IsNotTransparent, IsSerializable, IsNotDynamic); bool NeedsDefinition = Fun->empty(); // Call the outlined function. diff --git a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp index 89f61376e19f5..73a6fd306a9e3 100644 --- a/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp +++ b/lib/SILOptimizer/UtilityPasses/BugReducerTester.cpp @@ -94,7 +94,7 @@ class BugReducerTester : public SILFunctionTransform { SILFunction *F = FunctionBuilder.getOrCreateSharedFunction( RegularLocation::getAutoGeneratedLocation(), RuntimeCrasherFunctionName, FuncType, IsBare, IsNotTransparent, IsSerialized, ProfileCounter(), - IsNotThunk); + IsNotThunk, IsNotDynamic); if (F->isDefinition()) return F; diff --git a/lib/SILOptimizer/Utils/Devirtualize.cpp b/lib/SILOptimizer/Utils/Devirtualize.cpp index e80b935db24d4..87abeeca2165a 100644 --- a/lib/SILOptimizer/Utils/Devirtualize.cpp +++ b/lib/SILOptimizer/Utils/Devirtualize.cpp @@ -712,7 +712,7 @@ FullApplySite swift::devirtualizeClassMethod(FullApplySite AI, SILBuilderWithScope B(AI.getInstruction()); SILLocation Loc = AI.getLoc(); - FunctionRefInst *FRI = B.createFunctionRef(Loc, F); + auto *FRI = B.createFunctionRefFor(Loc, F); // Create the argument list for the new apply, casting when needed // in order to handle covariant indirect return types and @@ -949,7 +949,7 @@ devirtualizeWitnessMethod(ApplySite AI, SILFunction *F, // the witness thunk. SILBuilderWithScope Builder(AI.getInstruction()); SILLocation Loc = AI.getLoc(); - FunctionRefInst *FRI = Builder.createFunctionRef(Loc, F); + auto *FRI = Builder.createFunctionRefFor(Loc, F); ApplySite SAI = replaceApplySite(Builder, Loc, AI, FRI, SubMap, Arguments, substConv); diff --git a/lib/SILOptimizer/Utils/GenericCloner.cpp b/lib/SILOptimizer/Utils/GenericCloner.cpp index a9cc17ad0b7ff..51b67650c7d76 100644 --- a/lib/SILOptimizer/Utils/GenericCloner.cpp +++ b/lib/SILOptimizer/Utils/GenericCloner.cpp @@ -44,9 +44,9 @@ SILFunction *GenericCloner::initCloned(SILOptFunctionBuilder &FunctionBuilder, getSpecializedLinkage(Orig, Orig->getLinkage()), NewName, ReInfo.getSpecializedType(), ReInfo.getSpecializedGenericEnvironment(), Orig->getLocation(), Orig->isBare(), Orig->isTransparent(), Serialized, - Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), - Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, - Orig->getDebugScope()); + IsNotDynamic, Orig->getEntryCount(), Orig->isThunk(), + Orig->getClassSubclassScope(), Orig->getInlineStrategy(), + Orig->getEffectsKind(), Orig, Orig->getDebugScope()); for (auto &Attr : Orig->getSemanticsAttrs()) { NewF->addSemanticsAttr(Attr); } diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 794304f20166e..3c89a754aab99 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -2097,7 +2097,7 @@ class ReabstractionThunkGenerator { SILFunction *ReabstractionThunkGenerator::createThunk() { SILFunction *Thunk = FunctionBuilder.getOrCreateSharedFunction( Loc, ThunkName, ReInfo.getSubstitutedType(), IsBare, IsTransparent, - Serialized, ProfileCounter(), IsThunk); + Serialized, ProfileCounter(), IsThunk, IsNotDynamic); // Re-use an existing thunk. if (!Thunk->empty()) return Thunk; diff --git a/lib/SILOptimizer/Utils/Local.cpp b/lib/SILOptimizer/Utils/Local.cpp index 02e23dd59824a..4f71557b26098 100644 --- a/lib/SILOptimizer/Utils/Local.cpp +++ b/lib/SILOptimizer/Utils/Local.cpp @@ -220,6 +220,14 @@ void swift::recursivelyDeleteTriviallyDeadInstructions( auto *FRI = dyn_cast(I); if (FRI && FRI->getReferencedFunction()) FRI->dropReferencedFunction(); + + auto *DFRI = dyn_cast(I); + if (DFRI && DFRI->getReferencedFunction()) + DFRI->dropReferencedFunction(); + + auto *PFRI = dyn_cast(I); + if (PFRI && PFRI->getReferencedFunction()) + PFRI->dropReferencedFunction(); } for (auto I : DeadInsts) { @@ -1561,8 +1569,9 @@ bool swift::calleesAreStaticallyKnowable(SILModule &M, SILDeclRef Decl) { if (!AFD->isChildContextOf(AssocDC)) return false; - if (AFD->isDynamic()) + if (AFD->isDynamic()) { return false; + } if (!AFD->hasAccess()) return false; @@ -1634,7 +1643,7 @@ StaticInitCloner::clone(SingleValueInstruction *InitVal) { } Optional -swift::findLocalApplySites(FunctionRefInst *FRI) { +swift::findLocalApplySites(FunctionRefBaseInst *FRI) { SmallVector worklist(FRI->use_begin(), FRI->use_end()); Optional f; diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index 7bd6b46c3e657..418188ac4e682 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -106,6 +106,9 @@ static bool canInlineBeginApply(BeginApplyInst *BA) { } bool SILInliner::canInlineApplySite(FullApplySite apply) { + if (!apply.canOptimize()) + return false; + if (auto BA = dyn_cast(apply)) return canInlineBeginApply(BA); @@ -686,6 +689,8 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::EndBorrowInst: case SILInstructionKind::BeginBorrowInst: case SILInstructionKind::MarkDependenceInst: + case SILInstructionKind::PreviousDynamicFunctionRefInst: + case SILInstructionKind::DynamicFunctionRefInst: case SILInstructionKind::FunctionRefInst: case SILInstructionKind::AllocGlobalInst: case SILInstructionKind::GlobalAddrInst: diff --git a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp index 3c8d3df91ef0a..ba98d7bb1c04b 100644 --- a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp +++ b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp @@ -109,6 +109,20 @@ void SILSSAUpdater::RewriteUse(Operand &Op) { auto *NewFR = cast(FR->clone(User)); Op.set(NewFR); return; + } else if (auto *FR = dyn_cast(Op.get())) { + assert(areIdentical(*AV) && + "The function_refs need to have the same value"); + SILInstruction *User = Op.getUser(); + auto *NewFR = cast(FR->clone(User)); + Op.set(NewFR); + return; + } else if (auto *FR = dyn_cast(Op.get())) { + assert(areIdentical(*AV) && + "The function_refs need to have the same value"); + SILInstruction *User = Op.getUser(); + auto *NewFR = cast(FR->clone(User)); + Op.set(NewFR); + return; } else if (auto *IL = dyn_cast(Op.get())) if (areIdentical(*AV)) { // Some llvm intrinsics don't like phi nodes as their constant inputs (e.g diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 5c5dba3494bb6..1cf2507fb93f6 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -312,7 +312,7 @@ static AccessorDecl *createSetterPrototype(TypeChecker &TC, // to 'get' and 'set', rather than the normal dynamically dispatched // opaque accessors that peer dispatch to 'get' and 'set'. static bool needsDynamicCoroutineAccessors(AbstractStorageDecl *storage) { - return storage->isDynamic() || storage->hasClangNode(); + return storage->isObjCDynamic() || storage->hasClangNode(); } /// Mark the accessor as transparent if we can. diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index 5cf0f3d3e8f18..09026d55789af 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -114,7 +114,7 @@ bool TypeChecker::diagnoseInlinableDeclRef(SourceLoc loc, // Dynamic declarations were mistakenly not checked in Swift 4.2. // Do enforce the restriction even in pre-Swift-5 modes if the module we're // building is resilient, though. - if (D->isDynamic() && !Context.isSwiftVersionAtLeast(5) && + if (D->isObjCDynamic() && !Context.isSwiftVersionAtLeast(5) && DC->getParentModule()->getResilienceStrategy() != ResilienceStrategy::Resilient) { return false; diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index ff73e735a08a9..c63bfea517e99 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -117,6 +117,7 @@ class AttributeEarlyChecker : public AttributeVisitor { IGNORED_ATTR(UnsafeNoObjCTaggedPointer) IGNORED_ATTR(UsableFromInline) IGNORED_ATTR(WeakLinked) + IGNORED_ATTR(DynamicReplacement) #undef IGNORED_ATTR // @noreturn has been replaced with a 'Never' return type. @@ -359,6 +360,11 @@ void AttributeEarlyChecker::visitDynamicAttr(DynamicAttr *attr) { // Members cannot be both dynamic and @nonobjc. if (D->getAttrs().hasAttribute()) diagnoseAndRemoveAttr(attr, diag::dynamic_with_nonobjc); + + // Members cannot be both dynamic and @_transparent. + if (D->getASTContext().LangOpts.isSwiftVersionAtLeast(5) && + D->getAttrs().hasAttribute()) + diagnoseAndRemoveAttr(attr, diag::dynamic_with_transparent); } @@ -818,6 +824,7 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(Transparent) IGNORED_ATTR(WarnUnqualifiedAccess) IGNORED_ATTR(WeakLinked) + IGNORED_ATTR(DynamicReplacement) #undef IGNORED_ATTR void visitAvailableAttr(AvailableAttr *attr); @@ -1904,6 +1911,214 @@ void AttributeChecker::visitDiscardableResultAttr(DiscardableResultAttr *attr) { } } +/// Lookup the replaced decl in the replacments scope. +void lookupReplacedDecl(DeclName replacedDeclName, + DynamicReplacementAttr *attr, + AbstractFunctionDecl *replacement, + SmallVectorImpl &results) { + auto *declCtxt = replacement->getDeclContext(); + + // Look at the accessors' storage's context. + if (auto *accessor = dyn_cast(replacement)) { + auto *storage = accessor->getStorage(); + declCtxt = storage->getDeclContext(); + } + + if (isa(declCtxt)) { + UnqualifiedLookup lookup(replacedDeclName, + replacement->getModuleScopeContext(), nullptr, + attr->getLocation()); + if (lookup.isSuccess()) { + for (auto entry : lookup.Results) + results.push_back(entry.getValueDecl()); + } + return; + } + + assert(declCtxt->isTypeContext()); + auto typeCtx = dyn_cast(declCtxt->getAsDecl()); + if (!typeCtx) + typeCtx = cast(declCtxt->getAsDecl())->getExtendedNominal(); + + replacement->getModuleScopeContext()->lookupQualified( + {typeCtx}, replacedDeclName, NL_QualifiedDefault, results); +} + +static FuncDecl *findReplacedAccessor(DeclName replacedVarName, + AccessorDecl *replacement, + DynamicReplacementAttr *attr, + TypeChecker &TC) { + + // Retrieve the replaced abstract storage decl. + SmallVector results; + lookupReplacedDecl(replacedVarName, attr, replacement, results); + + if (results.empty()) { + TC.diagnose(attr->getLocation(), + diag::dynamic_replacement_accessor_not_found, replacedVarName); + attr->setInvalid(); + return nullptr; + } + assert(results.size() == 1 && "Should only have on var or fun"); + + assert(!isa(results[0])); + TC.validateDecl(results[0]); + auto *origStorage = cast(results[0]); + if (!origStorage->isDynamic()) { + TC.diagnose(attr->getLocation(), + diag::dynamic_replacement_accessor_not_dynamic, + replacedVarName); + attr->setInvalid(); + return nullptr; + } + + // Find the accessor in the replaced storage decl. + for (auto *origAccessor : origStorage->getAllAccessors()) { + TC.validateDecl(origAccessor); + if (origAccessor->getAccessorKind() != replacement->getAccessorKind()) + continue; + + if (!replacement->getInterfaceType()->getCanonicalType()->matches( + origAccessor->getInterfaceType()->getCanonicalType(), + TypeMatchFlags::AllowABICompatible)) { + TC.diagnose(attr->getLocation(), + diag::dynamic_replacement_accessor_type_mismatch, + replacedVarName); + attr->setInvalid(); + return nullptr; + } + if (origAccessor->isImplicit() && + !(origStorage->getReadImpl() == ReadImplKind::Stored && + origStorage->getWriteImpl() == WriteImplKind::Stored)) { + TC.diagnose(attr->getLocation(), + diag::dynamic_replacement_accessor_not_explicit, + (unsigned)origAccessor->getAccessorKind(), replacedVarName); + attr->setInvalid(); + return nullptr; + } + return origAccessor; + } + return nullptr; +} + +static AbstractFunctionDecl * +findReplacedFunction(DeclName replacedFunctionName, + AbstractFunctionDecl *replacement, + DynamicReplacementAttr *attr, TypeChecker &TC) { + + SmallVector results; + lookupReplacedDecl(replacedFunctionName, attr, replacement, results); + + for (auto *result : results) { + TC.validateDecl(result); + if (result->getInterfaceType()->getCanonicalType()->matches( + replacement->getInterfaceType()->getCanonicalType(), + TypeMatchFlags::AllowABICompatible)) { + if (!result->isDynamic()) { + TC.diagnose(attr->getLocation(), + diag::dynamic_replacement_function_not_dynamic, + replacedFunctionName); + attr->setInvalid(); + return nullptr; + } + return cast(result); + } + } + if (results.empty()) + TC.diagnose(attr->getLocation(), + diag::dynamic_replacement_function_not_found, + attr->getReplacedFunctionName()); + else { + TC.diagnose(attr->getLocation(), + diag::dynamic_replacement_function_of_type_not_found, + attr->getReplacedFunctionName(), + replacement->getInterfaceType()->getCanonicalType()); + + for (auto *result : results) { + TC.diagnose(SourceLoc(), diag::dynamic_replacement_found_function_of_type, + attr->getReplacedFunctionName(), + result->getInterfaceType()->getCanonicalType()); + } + } + attr->setInvalid(); + return nullptr; +} + +void TypeChecker::checkDynamicReplacementAttribute(ValueDecl *D) { + assert(isa(D) || isa(D)); + + auto *attr = D->getAttrs().getAttribute(); + assert(attr); + + if (!D->getDeclContext()->isExtensionContext() && + !D->getDeclContext()->isModuleScopeContext()) { + diagnose(attr->getLocation(), diag::dynamic_replacement_not_in_extension, + D->getBaseName()); + attr->setInvalid(); + return; + } + + if (D->isDynamic() && !D->isObjC()) { + assert(false && "dynamic replacement must not be dynamic itself"); + 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()) + return; + + SmallVector replacements; + SmallVector origs; + + // Collect the accessor replacement mapping if this is an abstract storage. + if (auto *var = dyn_cast(D)) { + for (auto *accessor : var->getAllAccessors()) { + validateDecl(accessor); + if (accessor->isImplicit()) + continue; + auto *orig = findReplacedAccessor(attr->getReplacedFunctionName(), + accessor, attr, *this); + if (attr->isInvalid()) + return; + if (!orig) + continue; + origs.push_back(orig); + replacements.push_back(accessor); + } + } else { + // Otherwise, find the matching function. + auto *fun = cast(D); + if (auto *orig = findReplacedFunction(attr->getReplacedFunctionName(), fun, + attr, *this)) { + 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()) { + attr->setReplacedFunction(origs[index]); + continue; + } + auto *newAttr = DynamicReplacementAttr::create( + D->getASTContext(), attr->getReplacedFunctionName(), origs[index]); + DeclAttributes &attrs = replacements[index]->getAttrs(); + attrs.add(newAttr); + } + + // Remove the attribute on the abstract storage (we have moved it to the + // accessor decl). + if (!isa(D)) + return; + D->getAttrs().removeAttribute(attr); +} + void AttributeChecker::visitImplementsAttr(ImplementsAttr *attr) { TypeLoc &ProtoTypeLoc = attr->getProtocolType(); TypeResolutionOptions options = None; @@ -2116,3 +2331,19 @@ TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(const Decl *D) { return None; } + +void TypeChecker::addImplicitDynamicAttribute(Decl *D) { + if (!getLangOpts().EnableImplicitDynamic) + return; + + // Add the attribute if the decl kind allows it and it is not an accessor + // decl. Accessor decls should always infer the var/subscript's attribute. + if (!DeclAttribute::canAttributeAppearOnDecl(DAK_Dynamic, D) || + isa(D)) + return; + + if (!D->getAttrs().hasAttribute()) { + auto attr = new (D->getASTContext()) DynamicAttr(/*implicit=*/true); + D->getAttrs().add(attr); + } +} diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index ab2946a54a944..e5e3c7f4c187d 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -1117,7 +1117,7 @@ static void inferFinalAndDiagnoseIfNeeded(TypeChecker &TC, ValueDecl *D, /// constraints before doing so. /// /// \returns true if it can be made dynamic, false otherwise. -static bool makeDynamic(ValueDecl *decl) { +static bool makeObjCDynamic(ValueDecl *decl) { // Only members of classes can be dynamic. auto classDecl = decl->getDeclContext()->getSelfClassDecl(); if (!classDecl) { @@ -1127,7 +1127,7 @@ static bool makeDynamic(ValueDecl *decl) { return false; } - // 'dynamic' is only supported through the Objective-C runtime. + // '@objc dynamic' is only supported through the Objective-C runtime. if (!decl->isObjC()) { decl->diagnose(diag::dynamic_requires_objc, decl->getDescriptiveKind(), decl->getFullName()) @@ -1145,6 +1145,69 @@ static bool makeDynamic(ValueDecl *decl) { return true; } +static llvm::Expected isStorageDynamic(Evaluator &evaluator, + AccessorDecl *accessor) { + auto isDynamicResult = evaluator(IsDynamicRequest{accessor->getStorage()}); + + if (!isDynamicResult) + return isDynamicResult; + + return *isDynamicResult; +} + +/// Runtime-replacable accessors are dynamic when their storage declaration +/// is dynamic and they were explicitly defined or they are implicitly defined +/// getter/setter because no accessor was defined. +static llvm::Expected +doesAccessorNeedDynamicAttribute(AccessorDecl *accessor, Evaluator &evaluator) { + auto kind = accessor->getAccessorKind(); + auto storage = accessor->getStorage(); + bool isObjC = storage->isObjC(); + + switch (kind) { + case AccessorKind::Get: { + auto readImpl = storage->getReadImpl(); + if (!isObjC && + (readImpl == ReadImplKind::Read || readImpl == ReadImplKind::Address)) + return false; + return isStorageDynamic(evaluator, accessor); + } + case AccessorKind::Set: { + auto writeImpl = storage->getWriteImpl(); + if (!isObjC && (writeImpl == WriteImplKind::Modify || + writeImpl == WriteImplKind::MutableAddress || + writeImpl == WriteImplKind::StoredWithObservers)) + return false; + return isStorageDynamic(evaluator, accessor); + } + case AccessorKind::Read: + if (!isObjC && storage->getReadImpl() == ReadImplKind::Read) + return isStorageDynamic(evaluator, accessor); + return false; + case AccessorKind::Modify: { + if (!isObjC && storage->getWriteImpl() == WriteImplKind::Modify) + return isStorageDynamic(evaluator, accessor); + return false; + } + case AccessorKind::MutableAddress: { + if (!isObjC && storage->getWriteImpl() == WriteImplKind::MutableAddress) + return isStorageDynamic(evaluator, accessor); + return false; + } + case AccessorKind::Address: { + if (!isObjC && storage->getReadImpl() == ReadImplKind::Address) + return isStorageDynamic(evaluator, accessor); + return false; + } + case AccessorKind::DidSet: + case AccessorKind::WillSet: + if (!isObjC && + storage->getWriteImpl() == WriteImplKind::StoredWithObservers) + return isStorageDynamic(evaluator, accessor); + return false; + } +} + llvm::Expected IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { // If we can't infer dynamic here, don't. @@ -1153,12 +1216,20 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { // If 'dynamic' was explicitly specified, check it. if (decl->getAttrs().hasAttribute()) { - return makeDynamic(decl); + if (decl->getASTContext().LangOpts.isSwiftVersionAtLeast(5)) + return true; + return makeObjCDynamic(decl); } - // Runtime-replacable accessors are dynamic when their storage declaration - // is dynamic. Other accessors are never dynamic. if (auto accessor = dyn_cast(decl)) { + // Swift 5: Runtime-replacable accessors are dynamic when their storage declaration + // is dynamic and they were explicitly defined or they are implicitly defined + // getter/setter because no accessor was defined. + if (decl->getASTContext().LangOpts.isSwiftVersionAtLeast(5)) + return doesAccessorNeedDynamicAttribute(accessor, evaluator); + + // Pre Swift 5: Runtime-replacable accessors are dynamic when their storage declaration + // is dynamic. Other accessors are never dynamic. switch (accessor->getAccessorKind()) { case AccessorKind::Get: case AccessorKind::Set: { @@ -1169,7 +1240,7 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { return isDynamicResult; if (*isDynamicResult) - return makeDynamic(decl); + return makeObjCDynamic(decl); return false; } @@ -1186,7 +1257,7 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { // FIXME: Use a semantic check for NSManaged rather than looking for the // attribute (which could be ill-formed). if (decl->getAttrs().hasAttribute()) { - return makeDynamic(decl); + return makeObjCDynamic(decl); } // The presence of 'final' blocks the inference of 'dynamic'. @@ -1205,7 +1276,7 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { // This is intended to enable overriding the declarations. auto dc = decl->getDeclContext(); if (isa(dc) && dc->getSelfClassDecl()) { - return makeDynamic(decl); + return makeObjCDynamic(decl); } // If any of the declarations overridden by this declaration are dynamic @@ -1216,11 +1287,13 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { auto overriddenDecls = evaluateOrDefault(evaluator, OverriddenDeclsRequest{decl}, {}); for (auto overridden : overriddenDecls) { - if (overridden->isDynamic()) - return makeDynamic(decl); + if (overridden->isDynamic() && + (!decl->getASTContext().LangOpts.isSwiftVersionAtLeast(5) || + overridden->isObjC())) + return makeObjCDynamic(decl); if (overridden->hasClangNode()) - return makeDynamic(decl); + return makeObjCDynamic(decl); } return false; @@ -2664,6 +2737,8 @@ class DeclChecker : public DeclVisitor { } void visitSubscriptDecl(SubscriptDecl *SD) { + TC.addImplicitDynamicAttribute(SD); + TC.validateDecl(SD); if (!SD->isInvalid()) { @@ -2689,6 +2764,9 @@ class DeclChecker : public DeclVisitor { } triggerAccessorSynthesis(TC, SD); + if (SD->getAttrs().hasAttribute()) { + TC.checkDynamicReplacementAttribute(SD); + } } void visitTypeAliasDecl(TypeAliasDecl *TAD) { @@ -3128,8 +3206,16 @@ class DeclChecker : public DeclVisitor { } void visitVarDecl(VarDecl *VD) { + TC.addImplicitDynamicAttribute(VD); + // Delay type-checking on VarDecls until we see the corresponding // PatternBindingDecl. + + // Except if there is a dynamic replacement attribute. + if (VD->getAttrs().hasAttribute()) { + TC.validateDecl(VD); + TC.checkDynamicReplacementAttribute(VD); + } } /// Determine whether the given declaration requires a definition. @@ -3173,6 +3259,8 @@ class DeclChecker : public DeclVisitor { } void visitFuncDecl(FuncDecl *FD) { + TC.addImplicitDynamicAttribute(FD); + TC.validateDecl(FD); if (!FD->isInvalid()) { @@ -3202,6 +3290,10 @@ class DeclChecker : public DeclVisitor { // Record the body. TC.definedFunctions.push_back(FD); } + + if (FD->getAttrs().hasAttribute()) { + TC.checkDynamicReplacementAttribute(FD); + } } void visitModuleDecl(ModuleDecl *) { } @@ -3285,6 +3377,7 @@ class DeclChecker : public DeclVisitor { } void visitConstructorDecl(ConstructorDecl *CD) { + TC.addImplicitDynamicAttribute(CD); TC.validateDecl(CD); if (!CD->isInvalid()) { @@ -3398,6 +3491,10 @@ class DeclChecker : public DeclVisitor { } else { TC.definedFunctions.push_back(CD); } + + if (CD->getAttrs().hasAttribute()) { + TC.checkDynamicReplacementAttribute(CD); + } } void visitDestructorDecl(DestructorDecl *DD) { diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index d7fba1ff845b3..2ee3db68ab537 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -1142,14 +1142,15 @@ Optional shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) { return ObjCReason(ObjCReason::ExplicitlyDynamic); } - - // Complain that 'dynamic' requires '@objc', but (quietly) infer @objc - // anyway for better recovery. - VD->diagnose(diag::dynamic_requires_objc, - VD->getDescriptiveKind(), VD->getFullName()) - .fixItInsert(VD->getAttributeInsertionLoc(/*forModifier=*/false), - "@objc "); - return ObjCReason(ObjCReason::ImplicitlyObjC); + if (!ctx.isSwiftVersionAtLeast(5)) { + // Complain that 'dynamic' requires '@objc', but (quietly) infer @objc + // anyway for better recovery. + VD->diagnose(diag::dynamic_requires_objc, VD->getDescriptiveKind(), + VD->getFullName()) + .fixItInsert(VD->getAttributeInsertionLoc(/*forModifier=*/false), + "@objc "); + return ObjCReason(ObjCReason::ImplicitlyObjC); + } } // If we aren't provided Swift 3's @objc inference rules, we're done. diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 85622b7ea8be0..3ce070986a610 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1210,6 +1210,7 @@ namespace { UNINTERESTING_ATTR(SwiftNativeObjCRuntimeBase) UNINTERESTING_ATTR(ShowInInterface) UNINTERESTING_ATTR(Specialize) + UNINTERESTING_ATTR(DynamicReplacement) // These can't appear on overridable declarations. UNINTERESTING_ATTR(Prefix) @@ -1557,7 +1558,7 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) { if (auto *baseDecl = dyn_cast(base->getDeclContext())) { if (!isAccessor && baseDecl->hasKnownSwiftImplementation() && - !base->isDynamic() && + !base->isObjCDynamic() && override->getDeclContext()->isExtensionContext()) { // For compatibility, only generate a warning in Swift 3 diags.diagnose(override, diag::override_class_declaration_in_extension); diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index d7cbccd43cfea..8e142d5d1dd3a 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1091,7 +1091,9 @@ class TypeChecker final : public LazyResolver { void typeCheckDecl(Decl *D); void checkDeclAttributesEarly(Decl *D); + void addImplicitDynamicAttribute(Decl *D); void checkDeclAttributes(Decl *D); + void checkDynamicReplacementAttribute(ValueDecl *D); void checkTypeModifyingDeclAttributes(VarDecl *var); void checkReferenceOwnershipAttr(VarDecl *D, ReferenceOwnershipAttr *attr); diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index b201980a5eda7..74b39c9acbc5e 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2541,6 +2541,30 @@ ModuleFile::getDeclCheckedImpl(DeclID DID) { break; } + case decls_block::DynamicReplacement_DECL_ATTR: { + bool isImplicit; + uint64_t numArgs; + ArrayRef rawPieceIDs; + DeclID replacedFunID; + serialization::decls_block::DynamicReplacementDeclAttrLayout:: + readRecord(scratch, isImplicit, replacedFunID, numArgs, rawPieceIDs); + + auto replacedFunDecl = getDeclChecked(replacedFunID); + if (!replacedFunDecl) + return replacedFunDecl.takeError(); + auto baseName = getDeclBaseName(rawPieceIDs[0]); + SmallVector pieces; + for (auto pieceID : rawPieceIDs.slice(1)) + pieces.push_back(getIdentifier(pieceID)); + + assert(numArgs != 0); + assert(!isImplicit && "Need to update for implicit"); + Attr = DynamicReplacementAttr::create( + ctx, DeclName(ctx, baseName, ArrayRef(pieces)), + cast(*replacedFunDecl)); + break; + } + #define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \ case decls_block::CLASS##_DECL_ATTR: { \ bool isImplicit; \ diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 45fcbf8941697..72fde0d1588d8 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -463,17 +463,19 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, DeclID clangNodeOwnerID; TypeID funcTyID; + IdentifierID replacedFunctionID; GenericEnvironmentID genericEnvID; unsigned rawLinkage, isTransparent, isSerialized, isThunk, isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy, optimizationMode, effect, numSpecAttrs, hasQualifiedOwnership, - isWeakLinked; + isWeakLinked, isDynamic; ArrayRef SemanticsIDs; SILFunctionLayout::readRecord( scratch, rawLinkage, isTransparent, isSerialized, isThunk, isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy, optimizationMode, effect, numSpecAttrs, hasQualifiedOwnership, - isWeakLinked, funcTyID, genericEnvID, clangNodeOwnerID, SemanticsIDs); + isWeakLinked, isDynamic, funcTyID, replacedFunctionID, genericEnvID, + clangNodeOwnerID, SemanticsIDs); if (funcTyID == 0) { LLVM_DEBUG(llvm::dbgs() << "SILFunction typeID is 0.\n"); @@ -496,6 +498,17 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, return nullptr; } + SILFunction *replacedFunction = nullptr; + Identifier replacedObjectiveCFunc; + if (replacedFunctionID && + ty.getAs()->getExtInfo().getRepresentation() != + SILFunctionTypeRepresentation::ObjCMethod) { + replacedFunction = + getFuncForReference(MF->getIdentifier(replacedFunctionID).str()); + } else if (replacedFunctionID) { + replacedObjectiveCFunc = MF->getIdentifier(replacedFunctionID); + } + auto linkage = fromStableSILLinkage(rawLinkage); if (!linkage) { LLVM_DEBUG(llvm::dbgs() << "invalid linkage code " << rawLinkage @@ -550,6 +563,11 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, linkage == SILLinkage::PublicNonABI) { fn->setLinkage(SILLinkage::SharedExternal); } + if (fn->isDynamicallyReplaceable() != isDynamic) { + LLVM_DEBUG(llvm::dbgs() << "SILFunction type mismatch.\n"); + MF->error(); + return nullptr; + } } else { // Otherwise, create a new function. SILSerializationFunctionBuilder builder(SILMod); @@ -564,6 +582,11 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, fn->setEffectsKind(EffectsKind(effect)); fn->setOptimizationMode(OptimizationMode(optimizationMode)); fn->setWeakLinked(isWeakLinked); + fn->setIsDynamic(IsDynamicallyReplaceable_t(isDynamic)); + if (replacedFunction) + fn->setDynamicallyReplacedFunction(replacedFunction); + if (!replacedObjectiveCFunc.empty()) + fn->setObjCReplacement(replacedObjectiveCFunc); if (clangNodeOwner) fn->setClangNodeOwner(clangNodeOwner); for (auto ID : SemanticsIDs) { @@ -1480,9 +1503,25 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB, case SILInstructionKind::FunctionRefInst: { auto Ty = MF->getType(TyID); StringRef FuncName = MF->getIdentifierText(ValID); - ResultVal = Builder.createFunctionRef(Loc, - getFuncForReference(FuncName, - getSILType(Ty, (SILValueCategory)TyCategory))); + ResultVal = Builder.createFunctionRef( + Loc, getFuncForReference(FuncName, + getSILType(Ty, (SILValueCategory)TyCategory))); + break; + } + case SILInstructionKind::DynamicFunctionRefInst: { + auto Ty = MF->getType(TyID); + StringRef FuncName = MF->getIdentifierText(ValID); + ResultVal = Builder.createDynamicFunctionRef( + Loc, getFuncForReference(FuncName, + getSILType(Ty, (SILValueCategory)TyCategory))); + break; + } + case SILInstructionKind::PreviousDynamicFunctionRefInst: { + auto Ty = MF->getType(TyID); + StringRef FuncName = MF->getIdentifierText(ValID); + ResultVal = Builder.createPreviousDynamicFunctionRef( + Loc, getFuncForReference(FuncName, + getSILType(Ty, (SILValueCategory)TyCategory))); break; } case SILInstructionKind::MarkDependenceInst: { @@ -2475,17 +2514,19 @@ bool SILDeserializer::hasSILFunction(StringRef Name, // linkage to avoid re-reading it from the bitcode each time? DeclID clangOwnerID; TypeID funcTyID; + IdentifierID replacedFunctionID; GenericEnvironmentID genericEnvID; unsigned rawLinkage, isTransparent, isSerialized, isThunk, isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy, optimizationMode, effect, numSpecAttrs, hasQualifiedOwnership, - isWeakLinked; + isWeakLinked, isDynamic; ArrayRef SemanticsIDs; SILFunctionLayout::readRecord( scratch, rawLinkage, isTransparent, isSerialized, isThunk, isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy, optimizationMode, effect, numSpecAttrs, hasQualifiedOwnership, - isWeakLinked, funcTyID, genericEnvID, clangOwnerID, SemanticsIDs); + isWeakLinked, isDynamic, funcTyID, replacedFunctionID, genericEnvID, + clangOwnerID, SemanticsIDs); auto linkage = fromStableSILLinkage(rawLinkage); if (!linkage) { LLVM_DEBUG(llvm::dbgs() << "invalid linkage code " << rawLinkage diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index a07aeeb06a269..8340a47118a5a 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -290,7 +290,9 @@ namespace sil_block { BCVBR<8>, // number of specialize attributes BCFixed<1>, // has qualified ownership BCFixed<1>, // must be weakly referenced + BCFixed<1>, // is dynamically replacable TypeIDField, // SILFunctionType + DeclIDField, // SILFunction name or 0 (replaced function) GenericEnvironmentIDField, DeclIDField, // ClangNode owner BCArray // Semantics Attribute diff --git a/lib/Serialization/SILSerializationFunctionBuilder.h b/lib/Serialization/SILSerializationFunctionBuilder.h index ae937eda724f7..0caa0587371a2 100644 --- a/lib/Serialization/SILSerializationFunctionBuilder.h +++ b/lib/Serialization/SILSerializationFunctionBuilder.h @@ -30,7 +30,7 @@ class LLVM_LIBRARY_VISIBILITY SILSerializationFunctionBuilder { return builder.createFunction( SILLinkage::Private, name, type.getAs(), nullptr, loc, IsNotBare, IsNotTransparent, - IsNotSerialized, ProfileCounter(), IsNotThunk, + IsNotSerialized, IsNotDynamic, ProfileCounter(), IsNotThunk, SubclassScope::NotApplicable); } }; diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 410c984390a05..1391bcee88c64 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2342,6 +2342,21 @@ void Serializer::writeDeclAttribute(const DeclAttribute *DA) { writeGenericRequirements(SA->getRequirements(), DeclTypeAbbrCodes); return; } + + case DAK_DynamicReplacement: { + auto abbrCode = DeclTypeAbbrCodes[DynamicReplacementDeclAttrLayout::Code]; + auto theAttr = cast(DA); + auto replacedFun = theAttr->getReplacedFunctionName(); + SmallVector pieces; + pieces.push_back(addDeclBaseNameRef(replacedFun.getBaseName())); + for (auto argName : replacedFun.getArgumentNames()) + pieces.push_back(addDeclBaseNameRef(argName)); + assert(theAttr->getReplacedFunction()); + DynamicReplacementDeclAttrLayout::emitRecord( + Out, ScratchRecord, abbrCode, false, /*implicit flag*/ + addDeclRef(theAttr->getReplacedFunction()), pieces.size(), pieces); + return; + } } } diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 65f1bf3ba9199..16e363e3472b9 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -385,6 +385,15 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { if (F.hasClangNode()) clangNodeOwnerID = S.addDeclRef(F.getClangNodeOwner()); + IdentifierID replacedFunctionID = 0; + if (auto *fun = F.getDynamicallyReplacedFunction()) { + addReferencedSILFunction(fun, true); + replacedFunctionID = S.addUniquedStringRef(fun->getName()); + } + else if (F.hasObjCReplacement()) { + replacedFunctionID = + S.addUniquedStringRef(F.getObjCReplacement().str()); + } unsigned numSpecAttrs = NoBody ? 0 : F.getSpecializeAttrs().size(); SILFunctionLayout::emitRecord( Out, ScratchRecord, abbrCode, toStableSILLinkage(Linkage), @@ -393,7 +402,8 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { (unsigned)F.isGlobalInit(), (unsigned)F.getInlineStrategy(), (unsigned)F.getOptimizationMode(), (unsigned)F.getEffectsKind(), (unsigned)numSpecAttrs, (unsigned)F.hasQualifiedOwnership(), - F.isWeakLinked(), FnID, genericEnvID, clangNodeOwnerID, SemanticsIDs); + F.isWeakLinked(), (unsigned)F.isDynamicallyReplaceable(), FnID, + replacedFunctionID, genericEnvID, clangNodeOwnerID, SemanticsIDs); if (NoBody) return; @@ -1291,6 +1301,34 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { break; } + case SILInstructionKind::DynamicFunctionRefInst: { + // Use SILOneOperandLayout to specify the function type and the function + // name (IdentifierID). + const auto *FRI = cast(&SI); + SILFunction *ReferencedFunction = FRI->getReferencedFunction(); + unsigned abbrCode = SILAbbrCodes[SILOneOperandLayout::Code]; + SILOneOperandLayout::emitRecord(Out, ScratchRecord, abbrCode, + (unsigned)SI.getKind(), 0, + S.addTypeRef(FRI->getType().getASTType()), + (unsigned)FRI->getType().getCategory(), + addSILFunctionRef(ReferencedFunction)); + + break; + } + case SILInstructionKind::PreviousDynamicFunctionRefInst: { + // Use SILOneOperandLayout to specify the function type and the function + // name (IdentifierID). + const auto *FRI = cast(&SI); + SILFunction *ReferencedFunction = FRI->getReferencedFunction(); + unsigned abbrCode = SILAbbrCodes[SILOneOperandLayout::Code]; + SILOneOperandLayout::emitRecord(Out, ScratchRecord, abbrCode, + (unsigned)SI.getKind(), 0, + S.addTypeRef(FRI->getType().getASTType()), + (unsigned)FRI->getType().getCategory(), + addSILFunctionRef(ReferencedFunction)); + + break; + } case SILInstructionKind::CopyBlockWithoutEscapingInst: case SILInstructionKind::DeallocPartialRefInst: case SILInstructionKind::MarkDependenceInst: diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 455fb432e68f3..2e9be82d54aca 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -164,6 +164,17 @@ void TBDGenVisitor::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) { addSymbol(SILDeclRef(AFD)); + // Add the global function pointer for a dynamically replaceable function. + if (AFD->isDynamic() && ! AFD->isObjC()) { + addSymbol(LinkEntity::forDynamicallyReplaceableFunctionVariable(AFD)); + addSymbol(LinkEntity::forDynamicallyReplaceableFunctionImpl(AFD)); + addSymbol(LinkEntity::forDynamicallyReplaceableFunctionKey(AFD)); + } + if (AFD->getAttrs().hasAttribute()) { + addSymbol(LinkEntity::forDynamicallyReplaceableFunctionVariable(AFD)); + addSymbol(LinkEntity::forDynamicallyReplaceableFunctionImpl(AFD)); + } + if (AFD->getAttrs().hasAttribute()) { // A @_cdecl("...") function has an extra symbol, with the name from the // attribute. diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index 8d35cfc21233d..d718f5939b5ca 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -419,3 +419,17 @@ void swift::swift_abortRetainUnowned(const void *object) { "the object was already deallocated"); } } + +/// Halt due to enabling an already enabled dynamic replacement(). +void swift::swift_abortDynamicReplacementEnabling() { + swift::fatalError(FatalErrorFlags::ReportBacktrace, + "Fatal error: trying to enable a dynamic replacement " + "that is already enabled"); +} + +/// Halt due to disabling an already disabled dynamic replacement(). +void swift::swift_abortDynamicReplacementDisabling() { + swift::fatalError(FatalErrorFlags::ReportBacktrace, + "Fatal error: trying to disable a dynamic replacement " + "that is already disabled"); +} diff --git a/stdlib/public/runtime/ImageInspection.h b/stdlib/public/runtime/ImageInspection.h index 71f22b010a53d..fc3b075efe7ea 100644 --- a/stdlib/public/runtime/ImageInspection.h +++ b/stdlib/public/runtime/ImageInspection.h @@ -45,12 +45,16 @@ void initializeProtocolConformanceLookup(); /// Load the metadata from the image necessary to find a type by name. void initializeTypeMetadataRecordLookup(); +/// Load the metadata from the image necessary to perform dynamic replacements. +void initializeDynamicReplacementLookup(); + // Callbacks to register metadata from an image to the runtime. void addImageProtocolsBlockCallback(const void *start, uintptr_t size); void addImageProtocolConformanceBlockCallback(const void *start, uintptr_t size); void addImageTypeMetadataRecordBlockCallback(const void *start, uintptr_t size); +void addImageDynamicReplacementBlockCallback(const void *start, uintptr_t size); int lookupSymbol(const void *address, SymbolInfo *info); void *lookupSection(const char *segment, const char *section, size_t *outSize); diff --git a/stdlib/public/runtime/ImageInspectionCOFF.cpp b/stdlib/public/runtime/ImageInspectionCOFF.cpp index eeb682f9fed50..2ca78ea154f91 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.cpp +++ b/stdlib/public/runtime/ImageInspectionCOFF.cpp @@ -82,6 +82,9 @@ void swift::initializeTypeMetadataRecordLookup() { } } +void swift::initializeDynamicReplacementLookup() { +} + SWIFT_RUNTIME_EXPORT void swift_addNewDSOImage(const void *addr) { const swift::MetadataSections *sections = @@ -106,6 +109,13 @@ void swift_addNewDSOImage(const void *addr) { const void *metadata = reinterpret_cast(type_metadata.start); if (type_metadata.length) addImageTypeMetadataRecordBlockCallback(metadata, type_metadata.length); + + const auto &dynamic_replacements = sections->swift5_repl; + const auto *replacements = + reinterpret_cast(dynamic_replacements.start); + if (dynamic_replacements.length) + addImageDynamicReplacementBlockCallback(replacements, dynamic_replacements.length); + } int swift::lookupSymbol(const void *address, SymbolInfo *info) { diff --git a/stdlib/public/runtime/ImageInspectionCOFF.h b/stdlib/public/runtime/ImageInspectionCOFF.h index 75e0266cf73bf..02161b6dfe0a3 100644 --- a/stdlib/public/runtime/ImageInspectionCOFF.h +++ b/stdlib/public/runtime/ImageInspectionCOFF.h @@ -52,6 +52,7 @@ struct MetadataSections { Range swift5_reflstr; Range swift5_fieldmd; Range swift5_assocty; + Range swift5_repl; }; } // namespace swift diff --git a/stdlib/public/runtime/ImageInspectionELF.cpp b/stdlib/public/runtime/ImageInspectionELF.cpp index c494171ac20a2..a675ea0fc93f3 100644 --- a/stdlib/public/runtime/ImageInspectionELF.cpp +++ b/stdlib/public/runtime/ImageInspectionELF.cpp @@ -86,6 +86,9 @@ void swift::initializeTypeMetadataRecordLookup() { } } +void swift::initializeDynamicReplacementLookup() { +} + // As ELF images are loaded, ImageInspectionInit:sectionDataInit() will call // addNewDSOImage() with an address in the image that can later be used via // dladdr() to dlopen() the image after the appropriate initialize*Lookup() @@ -114,6 +117,13 @@ void swift_addNewDSOImage(const void *addr) { const void *metadata = reinterpret_cast(type_metadata.start); if (type_metadata.length) addImageTypeMetadataRecordBlockCallback(metadata, type_metadata.length); + + const auto &dynamic_replacements = sections->swift5_replace; + const auto *replacements = + reinterpret_cast(dynamic_replacements.start); + if (dynamic_replacements.length) + addImageDynamicReplacementBlockCallback(replacements, + dynamic_replacements.length); } int swift::lookupSymbol(const void *address, SymbolInfo *info) { diff --git a/stdlib/public/runtime/ImageInspectionELF.h b/stdlib/public/runtime/ImageInspectionELF.h index 950536000412f..e5f0cafd7fd4a 100644 --- a/stdlib/public/runtime/ImageInspectionELF.h +++ b/stdlib/public/runtime/ImageInspectionELF.h @@ -52,6 +52,7 @@ struct MetadataSections { Range swift5_reflstr; Range swift5_fieldmd; Range swift5_assocty; + Range swift5_replace; }; } // namespace swift diff --git a/stdlib/public/runtime/ImageInspectionMachO.cpp b/stdlib/public/runtime/ImageInspectionMachO.cpp index 2a0e8e17cb9c3..ea91f9e39939b 100644 --- a/stdlib/public/runtime/ImageInspectionMachO.cpp +++ b/stdlib/public/runtime/ImageInspectionMachO.cpp @@ -38,6 +38,11 @@ constexpr const char ProtocolConformancesSection[] = "__swift5_proto"; /// The Mach-O section name for the section containing type references. /// This lives within SEG_TEXT. constexpr const char TypeMetadataRecordSection[] = "__swift5_types"; +/// The Mach-O section name for the section containing dynamic replacements. +/// This lives within SEG_TEXT. +constexpr const char DynamicReplacementSection[] = "__swift5_replace"; + +constexpr const char TextSegment[] = SEG_TEXT; #if __POINTER_WIDTH__ == 64 using mach_header_platform = mach_header_64; @@ -47,7 +52,7 @@ using mach_header_platform = mach_header; extern "C" void *_NSGetMachExecuteHeader(); -template void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) { #if __POINTER_WIDTH__ == 64 @@ -58,7 +63,7 @@ void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) { unsigned long size; const uint8_t *section = getsectiondata(reinterpret_cast(mh), - SEG_TEXT, SECTION_NAME, + SEGMENT_NAME, SECTION_NAME, &size); if (!section) @@ -71,20 +76,25 @@ void addImageCallback(const mach_header *mh, intptr_t vmaddr_slide) { void swift::initializeProtocolLookup() { _dyld_register_func_for_add_image( - addImageCallback); } void swift::initializeProtocolConformanceLookup() { _dyld_register_func_for_add_image( - addImageCallback); } void swift::initializeTypeMetadataRecordLookup() { _dyld_register_func_for_add_image( - addImageCallback); - +} + +void swift::initializeDynamicReplacementLookup() { + _dyld_register_func_for_add_image( + addImageCallback); } int swift::lookupSymbol(const void *address, SymbolInfo *info) { diff --git a/stdlib/public/runtime/MetadataLookup.cpp b/stdlib/public/runtime/MetadataLookup.cpp index 52b4c6f56d9cd..cad4420cddb80 100644 --- a/stdlib/public/runtime/MetadataLookup.cpp +++ b/stdlib/public/runtime/MetadataLookup.cpp @@ -22,8 +22,10 @@ #include "swift/ABI/TypeIdentity.h" #include "swift/Runtime/Casting.h" #include "swift/Runtime/Concurrent.h" +#include "swift/Runtime/Debug.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" +#include "swift/Runtime/Mutex.h" #include "swift/Strings.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" @@ -1508,5 +1510,113 @@ void swift::gatherWrittenGenericArgs( } } +struct InitializeDynamicReplacementLookup { + InitializeDynamicReplacementLookup() { + initializeDynamicReplacementLookup(); + } +}; + +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN +static InitializeDynamicReplacementLookup initDynamicReplacements; +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END + +void DynamicReplacementDescriptor::enableReplacement() const { + auto *chainRoot = const_cast( + replacedFunctionKey->root.get()); + + // Make sure this entry is not already enabled. + for (auto *curr = chainRoot; curr != nullptr; curr = curr->next) { + if (curr == chainEntry.get()) { + swift::swift_abortDynamicReplacementEnabling(); + } + } + + // First populate the current replacement's chain entry. + auto *currentEntry = + const_cast(chainEntry.get()); + currentEntry->implementationFunction = chainRoot->implementationFunction; + currentEntry->next = chainRoot->next; + + // Link the replacement entry. + chainRoot->next = chainEntry.get(); + chainRoot->implementationFunction = replacementFunction.get(); +} + +void DynamicReplacementDescriptor::disableReplacement() const { + const auto *chainRoot = replacedFunctionKey->root.get(); + auto *thisEntry = + const_cast(chainEntry.get()); + + // Find the entry previous to this one. + auto *prev = chainRoot; + while (prev && prev->next != thisEntry) + prev = prev->next; + if (!prev) { + swift::swift_abortDynamicReplacementDisabling(); + return; + } + + // Unlink this entry. + auto *previous = const_cast(prev); + previous->next = thisEntry->next; + previous->implementationFunction = thisEntry->implementationFunction; +} + +/// An automatic dymamic replacement entry. +class AutomaticDynamicReplacementEntry { + RelativeDirectPointer replacementScope; + uint32_t flags; + +public: + void enable() const { replacementScope->enable(); } + + uint32_t getFlags() { return flags; } +}; + +/// A list of automatic dynamic replacement scopes. +class AutomaticDynamicReplacements + : private swift::ABI::TrailingObjects { + uint32_t flags; + uint32_t numScopes; + + using TrailingObjects = + swift::ABI::TrailingObjects; + friend TrailingObjects; + + + ArrayRef getReplacementEntries() const { + return { + this->template getTrailingObjects(), + numScopes}; + } + +public: + void enableReplacements() const { + for (auto &replacementEntry : getReplacementEntries()) + replacementEntry.enable(); + } +}; + +namespace { + static Lazy DynamicReplacementLock; +} + +void swift::addImageDynamicReplacementBlockCallback( + const void *replacements, uintptr_t replacementsSize) { + auto *automaticReplacements = + reinterpret_cast(replacements); + DynamicReplacementLock.get().withLock( + [&] { automaticReplacements->enableReplacements(); }); +} + +void swift::swift_enableDynamicReplacementScope(const DynamicReplacementScope *scope) { + scope->enable(); +} + +void swift::swift_disableDynamicReplacementScope(const DynamicReplacementScope *scope) { + scope->disable(); +} #define OVERRIDE_METADATALOOKUP COMPATIBILITY_OVERRIDE #include "CompatibilityOverride.def" diff --git a/stdlib/public/runtime/SwiftRT-COFF.cpp b/stdlib/public/runtime/SwiftRT-COFF.cpp index af3451f76f7c1..0b7fe91e50e78 100644 --- a/stdlib/public/runtime/SwiftRT-COFF.cpp +++ b/stdlib/public/runtime/SwiftRT-COFF.cpp @@ -42,6 +42,7 @@ DECLARE_SWIFT_SECTION(sw5tyrf) DECLARE_SWIFT_SECTION(sw5rfst) DECLARE_SWIFT_SECTION(sw5flmd) DECLARE_SWIFT_SECTION(sw5asty) +DECLARE_SWIFT_SECTION(sw5repl) } namespace { @@ -68,6 +69,7 @@ static void swift_image_constructor() { SWIFT_SECTION_RANGE(sw5rfst), SWIFT_SECTION_RANGE(sw5flmd), SWIFT_SECTION_RANGE(sw5asty), + SWIFT_SECTION_RANGE(sw5repl), }; #undef SWIFT_SECTION_RANGE diff --git a/stdlib/public/runtime/SwiftRT-ELF.cpp b/stdlib/public/runtime/SwiftRT-ELF.cpp index 9848a9078ea96..1ae4f4dd36607 100644 --- a/stdlib/public/runtime/SwiftRT-ELF.cpp +++ b/stdlib/public/runtime/SwiftRT-ELF.cpp @@ -32,6 +32,7 @@ DECLARE_SWIFT_SECTION(swift5_typeref) DECLARE_SWIFT_SECTION(swift5_reflstr) DECLARE_SWIFT_SECTION(swift5_fieldmd) DECLARE_SWIFT_SECTION(swift5_assocty) +DECLARE_SWIFT_SECTION(swift5_replace) } #undef DECLARE_SWIFT_SECTION @@ -61,6 +62,7 @@ static void swift_image_constructor() { SWIFT_SECTION_RANGE(swift5_reflstr), SWIFT_SECTION_RANGE(swift5_fieldmd), SWIFT_SECTION_RANGE(swift5_assocty), + SWIFT_SECTION_RANGE(swift5_replace), }; #undef SWIFT_SECTION_RANGE diff --git a/test/IRGen/dynamic_replaceable.sil b/test/IRGen/dynamic_replaceable.sil new file mode 100644 index 0000000000000..a36a50df68423 --- /dev/null +++ b/test/IRGen/dynamic_replaceable.sil @@ -0,0 +1,76 @@ +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -emit-ir -disable-objc-interop | %FileCheck %s + +// REQUIRES: objc_interop + +// CHECK: @test_dynamically_replaceableTX = global %swift.dyn_repl_link_entry { i8* bitcast (void ()* @test_dynamically_replaceableTI to i8*), %swift.dyn_repl_link_entry* null } +// CHECK: @test_dynamically_replaceableTx = constant %swift.dyn_repl_key { i32{{.*}}%swift.dyn_repl_link_entry* @test_dynamically_replaceableTX{{.*}}, i32 0 }, section "__TEXT,__const" +// CHECK: @test_replacementTX = global %swift.dyn_repl_link_entry zeroinitializer + +// CHECK: @"\01l_unnamed_dynamic_replacements" = private constant { i32, i32, [1 x { i32, i32, i32, i32 }] } +// CHECK: { i32 0, +// CHECK: i32 1, +// CHECK: [1 x { i32, i32, i32, i32 }] +// CHECK: [{ i32, i32, i32, i32 } +// CHECK: %swift.dyn_repl_key* @test_dynamically_replaceableTx +// CHECK: @test_replacement +// CHECK: %swift.dyn_repl_link_entry* @test_replacementTX +// CHECK: i32 0 }] }, section "__TEXT,__const" + +// CHECK: @"\01l_auto_dynamic_replacements" = private constant { i32, i32, [1 x i32] } +// CHECK: { i32 0, i32 1, +// CHECK: [1 x i32] [{{.*}}@"\01l_unnamed_dynamic_replacements"{{.*}}] +// CHECK: }, section "__TEXT, __swift5_replace, regular, no_dead_strip" + +// CHECK-LABEL: define swiftcc void @test_dynamically_replaceable() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FUN_PTR:%.*]] = load i8*, i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_dynamically_replaceableTX, i32 0, i32 0) +// CHECK-NEXT: [[TYPED_PTR:%.*]] = bitcast i8* [[FUN_PTR]] to void ()* +// CHECK-NEXT: call swiftcc void [[TYPED_PTR]]() +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK-LABEL: define swiftcc void @test_dynamically_replaceableTI() +// CHECK: entry: +// CHECK: ret void +// CHECK: } + +sil [dynamically_replacable] @test_dynamically_replaceable : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + +// CHECK-LABEL: define swiftcc void @test_replacement() +// CHECK: entry: +// CHECK: call swiftcc void @test_replacementTI() +// CHECK: ret void +// CHECK: } + +// The thunk that implement the prev_dynamic_function_ref lookup. +// CHECK-LABEL: define swiftcc void @test_replacementTI() +// CHECK: entry: +// CHECK: [[FUN_PTR:%.*]] = load i8*, i8** getelementptr inbounds (%swift.dyn_repl_link_entry, %swift.dyn_repl_link_entry* @test_replacementTX, i32 0, i32 0) +// CHECK: [[TYPED_PTR:%.*]] = bitcast i8* [[FUN_PTR]] to void ()* +// CHECK: call swiftcc void [[TYPED_PTR]]() +// CHECK: ret void +// CHECK: } +sil [dynamic_replacement_for "test_dynamically_replaceable"] @test_replacement : $@convention(thin) () -> () { +bb0: + %0 = prev_dynamic_function_ref @test_replacement : $@convention(thin) () -> () + %1 = apply %0() : $@convention(thin) () -> () + %2 = tuple () + return %2 : $() +} + +// CHECK-LABEL: define swiftcc void @test_dynamic_call() +// CHECK: entry: +// CHECK: call swiftcc void @test_dynamically_replaceable() +// CHECK: ret void +// CHECK: } +sil @test_dynamic_call : $@convention(thin) () -> () { +bb0: + %0 = dynamic_function_ref @test_dynamically_replaceable : $@convention(thin) () -> () + %1 = apply %0() : $@convention(thin) () -> () + %2 = tuple () + return %2 : $() +} diff --git a/test/Interpreter/Inputs/dynamic_replacement_module.swift b/test/Interpreter/Inputs/dynamic_replacement_module.swift new file mode 100644 index 0000000000000..dd495813cb503 --- /dev/null +++ b/test/Interpreter/Inputs/dynamic_replacement_module.swift @@ -0,0 +1,151 @@ +#if MODULE +public dynamic func public_global_func() -> String { + return "public_global_func" +} + +public dynamic func public_global_generic_func(_ t: T.Type) -> String { + return "public_global_generic_func" +} + +public class PublicClass { + public var str : String = "" + public init() {} + public dynamic init(x: Int) { str = "public_class_init" } + + public dynamic func function() -> String { + return "public_class_func" + } + public dynamic func genericFunction(_ t: T.Type) -> String { + return "public_class_generic_func" + } +} + +public struct PublicStruct { + public var str = "" + public init() {} + + public dynamic init(x: Int) { str = "public_struct_init" } + + public dynamic func function() -> String { + return "public_struct_func" + } + public dynamic func genericFunction(_ t: T.Type) -> String { + return "public_struct_generic_func" + } + public dynamic var public_stored_property : String = "public_stored_property" + + public dynamic subscript(_ x: Int) -> String { + get { + return "public_subscript_get" + } + set { + str = newValue + } + } + public dynamic subscript(y x: Int) -> String { + _read { + yield "public_subscript_get_modify_read" + } + _modify { + yield &str + } + } +} + +public enum PublicEnumeration { + case A + case B + + public dynamic func function() -> String { + return "public_enum_func" + } + public dynamic func genericFunction(_ t: T.Type) -> String { + return "public_enum_generic_func" + } +} + +#elseif MODULE2 + +import Module1 + +/// Public global functions, struct, class, and enum. + +@_dynamicReplacement(for: public_global_func()) +public func replacement_for_public_global_func() -> String { + return "replacement of " + public_global_func() +} + +@_dynamicReplacement(for: public_global_generic_func(_:)) +public func replacement_for_public_global_generic_func(_ t: T.Type) -> String { + return "replacement of " + public_global_generic_func(t) +} + +extension PublicClass { + @_dynamicReplacement(for: init(x:)) + convenience public init(y: Int) { + self.init(x: y) + str = "replacement of public_class_init" + } + + @_dynamicReplacement(for: function()) + public func replacement_function() -> String { + return "replacement of " + function() + } + @_dynamicReplacement(for: genericFunction(_:)) + public func replacement_genericFunction(_ t: T.Type) -> String { + return "replacement of " + genericFunction(t) + } +} + +extension PublicStruct { + @_dynamicReplacement(for: init(x:)) + public init(y: Int) { + self.init(x: y) + str = "replacement of public_struct_init" + } + + @_dynamicReplacement(for: function()) + public func replacement_function() -> String { + return "replacement of " + function() + } + @_dynamicReplacement(for: genericFunction(_:)) + public func replacement_genericFunction(_ t: T.Type) -> String { + return "replacement of " + genericFunction(t) + } + @_dynamicReplacement(for: public_stored_property) + var replacement_public_stored_property : String { + return "replacement of " + public_stored_property + } + @_dynamicReplacement(for: subscript(_:)) + subscript(x x: Int) -> String { + get { + return "replacement of " + self[x] + } + set { + str = "replacement of " + newValue + } + } + + @_dynamicReplacement(for: subscript(y:)) + public subscript(z x: Int) -> String { + _read { + yield "replacement of " + self[y: x] + } + _modify { + yield &str + str = "replacement of " + str + } + } +} + +extension PublicEnumeration { + @_dynamicReplacement(for: function()) + public func replacement_function() -> String { + return "replacement of " + function() + } + @_dynamicReplacement(for: genericFunction(_:)) + public func replacement_genericFunction(_ t: T.Type) -> String { + return "replacement of " + genericFunction(t) + } +} +#endif diff --git a/test/Interpreter/dynamic_replacement.swift b/test/Interpreter/dynamic_replacement.swift new file mode 100644 index 0000000000000..47d7d37d8e5b8 --- /dev/null +++ b/test/Interpreter/dynamic_replacement.swift @@ -0,0 +1,101 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/libModule1.%target-dylib-extension) -DMODULE -module-name Module1 -emit-module -emit-module-path %t/Module1.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_module.swift +// RUN: %target-build-swift-dylib(%t/libModule2.%target-dylib-extension) -I%t -L%t -lModule1 -Xlinker -rpath -Xlinker %t -DMODULE2 -module-name Module2 -emit-module -emit-module-path %t/Module2.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_module.swift +// RUN: %target-build-swift -I%t -L%t -lModule1 -DMAIN -o %t/main -Xlinker -rpath -Xlinker %t %s -swift-version 5 +// RUN: %target-codesign %t/main %t/libModule1.%target-dylib-extension %t/libModule2.%target-dylib-extension +// RUN: %target-run %t/main %t/libModule1.%target-dylib-extension %t/libModule2.%target-dylib-extension + +// Now the same in optimized mode. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/libModule1.%target-dylib-extension) -O -DMODULE -module-name Module1 -emit-module -emit-module-path %t/Module1.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_module.swift +// RUN: %target-build-swift-dylib(%t/libModule2.%target-dylib-extension) -O -I%t -L%t -lModule1 -Xlinker -rpath -Xlinker %t -DMODULE2 -module-name Module2 -emit-module -emit-module-path %t/Module2.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_module.swift +// RUN: %target-build-swift -O -I%t -L%t -lModule1 -DMAIN -o %t/main -Xlinker -rpath -Xlinker %t %s -swift-version 5 +// RUN: %target-codesign %t/main %t/libModule1.%target-dylib-extension %t/libModule2.%target-dylib-extension +// RUN: %target-run %t/main %t/libModule1.%target-dylib-extension %t/libModule2.%target-dylib-extension + +// Now the same in size mode. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/libModule1.%target-dylib-extension) -Osize -DMODULE -module-name Module1 -emit-module -emit-module-path %t/Module1.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_module.swift +// RUN: %target-build-swift-dylib(%t/libModule2.%target-dylib-extension) -Osize -I%t -L%t -lModule1 -Xlinker -rpath -Xlinker %t -DMODULE2 -module-name Module2 -emit-module -emit-module-path %t/Module2.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_module.swift +// RUN: %target-build-swift -Osize -I%t -L%t -lModule1 -DMAIN -o %t/main -Xlinker -rpath -Xlinker %t %s -swift-version 5 +// RUN: %target-codesign %t/main %t/libModule1.%target-dylib-extension %t/libModule2.%target-dylib-extension +// RUN: %target-run %t/main %t/libModule1.%target-dylib-extension %t/libModule2.%target-dylib-extension + +// Now the same in optimized wholemodule mode. +// RUN: %empty-directory(%t) +// RUN: %target-build-swift-dylib(%t/libModule1.%target-dylib-extension) -O -wmo -DMODULE -module-name Module1 -emit-module -emit-module-path %t/Module1.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_module.swift +// RUN: %target-build-swift-dylib(%t/libModule2.%target-dylib-extension) -O -wmo -I%t -L%t -lModule1 -Xlinker -rpath -Xlinker %t -DMODULE2 -module-name Module2 -emit-module -emit-module-path %t/Module2.swiftmodule -swift-version 5 %S/Inputs/dynamic_replacement_module.swift +// RUN: %target-build-swift -O -wmo -I%t -L%t -lModule1 -DMAIN -o %t/main -Xlinker -rpath -Xlinker %t %s -swift-version 5 +// RUN: %target-codesign %t/main %t/libModule1.%target-dylib-extension %t/libModule2.%target-dylib-extension +// RUN: %target-run %t/main %t/libModule1.%target-dylib-extension %t/libModule2.%target-dylib-extension + + +// REQUIRES: executable_test + +import Module1 + +import StdlibUnittest + +#if os(Linux) + import Glibc + let dylibSuffix = "so" +#else + import Darwin + let dylibSuffix = "dylib" +#endif + +var DynamicallyReplaceable = TestSuite("DynamicallyReplaceable") + +func expectedResult(_ forOriginalLibrary: Bool, _ expectedOriginalString: String) -> String { + if forOriginalLibrary { + return expectedOriginalString + } else { + return "replacement of \(expectedOriginalString)" + } +} + +func checkExpectedResults(forOriginalLibrary useOrig: Bool) { + expectTrue(public_global_func() == + expectedResult(useOrig, "public_global_func")) + expectTrue(public_global_generic_func(Int.self) == + expectedResult(useOrig, "public_global_generic_func")) + + expectTrue(PublicClass().function() == + expectedResult(useOrig, "public_class_func")) + expectTrue(PublicClass().genericFunction(Int.self) == + expectedResult(useOrig, "public_class_generic_func")) + + expectTrue(PublicStruct().function() == + expectedResult(useOrig, "public_struct_func")) + expectTrue(PublicStruct().genericFunction(Int.self) == + expectedResult(useOrig, "public_struct_generic_func")) + expectTrue(PublicStruct().public_stored_property == + expectedResult(useOrig, "public_stored_property")) + expectTrue(PublicStruct()[0] == + expectedResult(useOrig, "public_subscript_get")) + expectTrue(PublicStruct()[y:0] == + expectedResult(useOrig, "public_subscript_get_modify_read")) + var testSetter = PublicStruct() + testSetter[0] = "public_subscript_set" + expectTrue(testSetter.str == + expectedResult(useOrig, "public_subscript_set")) + testSetter[y:0] = "public_subscript_set_modify_read" + expectTrue(testSetter.str == + expectedResult(useOrig, "public_subscript_set_modify_read")) + + expectTrue(PublicEnumeration.A.function() == + expectedResult(useOrig, "public_enum_func")) + expectTrue(PublicEnumeration.B.genericFunction(Int.self) == + expectedResult(useOrig, "public_enum_generic_func")) +} + +DynamicallyReplaceable.test("DynamicallyReplaceable") { + // First, test with only the original module. + checkExpectedResults(forOriginalLibrary: true) + + // Now, test with the module containing the replacements. + _ = dlopen("libModule2."+dylibSuffix, RTLD_NOW) + checkExpectedResults(forOriginalLibrary: false) +} + +runAllTests() diff --git a/test/Parse/diagnose_dynamicReplacement.swift b/test/Parse/diagnose_dynamicReplacement.swift new file mode 100644 index 0000000000000..07ab5d1582a52 --- /dev/null +++ b/test/Parse/diagnose_dynamicReplacement.swift @@ -0,0 +1,20 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 + +dynamic func dynamic_replaceable() { + +} + +@_dynamicReplacement +// expected-error@-1 {{expected '(' in '_dynamicReplacement' attribute}} +func test_dynamic_replacement_for() { +} + +@_dynamicReplacement( +// expected-error@-1 {{expected 'for' in '_dynamicReplacement' attribute}} +func test_dynamic_replacement_for2() { +} + +@_dynamicReplacement(for: dynamically_replaceable() // expected-note {{to match this opening '('}} +func test_dynamic_replacement_for3() { +// expected-error@-1 {{expected ')' after function name for @_dynamicReplacement}} +} diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index d4ba7d8a531a9..9dd1ec1dd1c55 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -1679,6 +1679,20 @@ bb0(%0 : $A): return %20 : $() } +// CHECK-LABEL: sil [dynamically_replacable] @test_dynamically_replaceable +sil [dynamically_replacable] @test_dynamically_replaceable : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + +// CHECK-LABEL: sil [dynamic_replacement_for "test_dynamically_replaceable"] @test_dynamic_replacement_for +sil [dynamic_replacement_for "test_dynamically_replaceable"] @test_dynamic_replacement_for : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + struct EmptyStruct {} sil @test_empty_destructure : $@convention(thin) () -> () { diff --git a/test/SIL/Serialization/dynamically_replaceable.sil b/test/SIL/Serialization/dynamically_replaceable.sil new file mode 100644 index 0000000000000..4ddde1fa787a5 --- /dev/null +++ b/test/SIL/Serialization/dynamically_replaceable.sil @@ -0,0 +1,31 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -emit-module -o %t/tmp.swiftmodule +// RUN: %target-sil-opt %t/tmp.swiftmodule -disable-sil-linking | %FileCheck %s + + +// CHECK-DAG-LABEL: sil [serialized] [dynamically_replacable] [canonical] @test_dynamically_replaceable +sil [serialized] [dynamically_replacable] @test_dynamically_replaceable : $@convention(thin) () -> () { +bb0: + %0 = tuple () + return %0 : $() +} + +// CHECK-DAG-LABEL: sil [serialized] [dynamic_replacement_for "test_dynamically_replaceable"] [canonical] @test_dynamic_replacement_for +// CHECK: prev_dynamic_function_ref @test_dynamic_replacement_for +sil [serialized] [dynamic_replacement_for "test_dynamically_replaceable"] @test_dynamic_replacement_for : $@convention(thin) () -> () { +bb0: + %0 = prev_dynamic_function_ref @test_dynamic_replacement_for : $@convention(thin) () -> () + %1 = apply %0() : $@convention(thin) () -> () + %2 = tuple () + return %2 : $() +} + +// CHECK-DAG-LABEL: sil [serialized] [canonical] @test_dynamically_replaceable_impl +// CHECK: dynamic_function_ref @test_dynamically_replaceable +sil [serialized] @test_dynamically_replaceable_impl : $@convention(thin) () -> () { +bb0: + %0 = dynamic_function_ref @test_dynamically_replaceable : $@convention(thin) () -> () + %1 = apply %0() : $@convention(thin) () -> () + %2 = tuple () + return %2 : $() +} diff --git a/test/SILGen/dynamically_replaceable.swift b/test/SILGen/dynamically_replaceable.swift new file mode 100644 index 0000000000000..4db2b0745a6d4 --- /dev/null +++ b/test/SILGen/dynamically_replaceable.swift @@ -0,0 +1,268 @@ +// RUN: %target-swift-emit-silgen -enable-sil-ownership -swift-version 5 %s | %FileCheck %s + + +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable08dynamic_B0yyF : $@convention(thin) () -> () { +dynamic func dynamic_replaceable() { +} + +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable6StruktV1xACSi_tcfC : $@convention(method) (Int, @thin Strukt.Type) -> Strukt +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable6StruktV08dynamic_B0yyF : $@convention(method) (Strukt) -> () { +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable6StruktV08dynamic_B4_varSivg +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable6StruktV08dynamic_B4_varSivs +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable6StruktVyS2icig : $@convention(method) (Int, Strukt) -> Int +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable6StruktVyS2icis : $@convention(method) (Int, Int, @inout Strukt) -> () +struct Strukt { + dynamic init(x: Int) { + } + dynamic func dynamic_replaceable() { + } + + dynamic var dynamic_replaceable_var : Int { + get { + return 10 + } + set { + } + } + + dynamic subscript(x : Int) -> Int { + get { + return 10 + } + set { + } + } +} +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable5KlassC1xACSi_tcfC : $@convention(method) (Int, @thick Klass.Type) -> @owned Klass +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable5KlassC08dynamic_B0yyF : $@convention(method) (@guaranteed Klass) -> () { +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable5KlassC08dynamic_B4_varSivg +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable5KlassC08dynamic_B4_varSivs +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable5KlassCyS2icig : $@convention(method) (Int, @guaranteed Klass) -> Int +// CHECK_LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable5KlassCyS2icis : $@convention(method) (Int, Int, @guaranteed Klass) -> () +class Klass { + dynamic init(x: Int) { + } + dynamic func dynamic_replaceable() { + } + dynamic func dynamic_replaceable2() { + } + dynamic var dynamic_replaceable_var : Int { + get { + return 10 + } + set { + } + } + dynamic subscript(x : Int) -> Int { + get { + return 10 + } + set { + } + } +} + +// CHECK-LABEL: sil hidden [dynamically_replacable] @$s23dynamically_replaceable6globalSivg : $@convention(thin) () -> Int { +dynamic var global : Int { + return 1 +} + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable08dynamic_B0yyF"] @$s23dynamically_replaceable11replacementyyF : $@convention(thin) () -> () { +@_dynamicReplacement(for: dynamic_replaceable()) +func replacement() { +} + +extension Klass { + // Calls to the replaced function inside the replacing function should be + // statically dispatched. + + // CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC08dynamic_B0yyF"] @$s23dynamically_replaceable5KlassC11replacementyyF : $@convention(method) (@guaranteed Klass) -> () { + // CHECK: [[FN:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC11replacementyyF + // CHECK: apply [[FN]](%0) : $@convention(method) (@guaranteed Klass) -> () + // CHECK: [[METHOD:%.*]] = class_method %0 : $Klass, #Klass.dynamic_replaceable2!1 + // CHECK: = apply [[METHOD]](%0) : $@convention(method) (@guaranteed Klass) -> () + // CHECK: return + @_dynamicReplacement(for: dynamic_replaceable()) + func replacement() { + dynamic_replaceable() + dynamic_replaceable2() + } + + // CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC1xACSi_tcfC"] @$s23dynamically_replaceable5KlassC1yACSi_tcfC : $@convention(method) (Int, @thick Klass.Type) -> @owned Klass { + // CHECK: [[FUN:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC1yACSi_tcfC + // CHECK: apply [[FUN]]({{.*}}, %1) + @_dynamicReplacement(for: init(x:)) + convenience init(y: Int) { + self.init(x: y + 1) + } + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC08dynamic_B4_varSivg"] @$s23dynamically_replaceable5KlassC1rSivg : $@convention(method) (@guaranteed Klass) -> Int { +// CHECK: bb0([[ARG:%.*]] : @guaranteed $Klass): +// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC1rSivg +// CHECK: apply [[ORIG]]([[ARG]]) : $@convention(method) (@guaranteed Klass) -> Int + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassC08dynamic_B4_varSivs"] @$s23dynamically_replaceable5KlassC1rSivs : $@convention(method) (Int, @guaranteed Klass) -> () { +// CHECK: bb0({{.*}} : @trivial $Int, [[SELF:%.*]] : @guaranteed $Klass): +// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC1rSivs +// CHECK: apply [[ORIG]]({{.*}}, [[SELF]]) : $@convention(method) + @_dynamicReplacement(for: dynamic_replaceable_var) + var r : Int { + get { + return dynamic_replaceable_var + 1 + } + set { + dynamic_replaceable_var = newValue + 1 + } + } + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassCyS2icig"] @$s23dynamically_replaceable5KlassC1xS2i_tcig +// CHECK: bb0({{.*}} : @trivial $Int, [[SELF:%.*]] : @guaranteed $Klass): +// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC1xS2i_tcig +// CHECK: apply [[ORIG]]({{.*}}, [[SELF]]) : $@convention(method) (Int, @guaranteed Klass) -> Int + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable5KlassCyS2icis"] @$s23dynamically_replaceable5KlassC1xS2i_tcis +// CHECK: bb0({{.*}} : @trivial $Int, {{.*}} : @trivial $Int, [[SELF:%.*]] : @guaranteed $Klass): +// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable5KlassC1xS2i_tcis +// CHECK: apply [[ORIG]]({{.*}}, {{.*}}, [[SELF]]) : $@convention(method) (Int, Int, @guaranteed Klass) -> () + + @_dynamicReplacement(for: subscript(_:)) + subscript(x y: Int) -> Int { + get { + return self[y] + } + set { + self[y] = newValue + } + } +} + +extension Strukt { + + // CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable6StruktV08dynamic_B0yyF"] @$s23dynamically_replaceable6StruktV11replacementyyF : $@convention(method) (Strukt) -> () { + // CHECK: [[FUN:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable6StruktV11replacementyyF + // CHECK: apply [[FUN]](%0) : $@convention(method) (Strukt) -> () + @_dynamicReplacement(for: dynamic_replaceable()) + func replacement() { + dynamic_replaceable() + } + // CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable6StruktV1xACSi_tcfC"] @$s23dynamically_replaceable6StruktV1yACSi_tcfC : $@convention(method) (Int, @thin Strukt.Type) -> Strukt { + // CHECK: [[FUN:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable6StruktV1yACSi_tcfC + // CHECK: apply [[FUN]]({{.*}}, %1) + @_dynamicReplacement(for: init(x:)) + init(y: Int) { + self.init(x: y + 1) + } + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable6StruktV08dynamic_B4_varSivg"] @$s23dynamically_replaceable6StruktV1rSivg +// CHECK: bb0([[ARG:%.*]] : @trivial $Strukt): +// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable6StruktV1rSivg +// CHECK: apply [[ORIG]]([[ARG]]) : $@convention(method) (Strukt) -> Int + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable6StruktV08dynamic_B4_varSivs"] @$s23dynamically_replaceable6StruktV1rSivs +// CHECK: bb0({{.*}} : @trivial $Int, [[ARG:%.*]] : @trivial $*Strukt): +// CHECK: [[BA:%.*]] = begin_access [modify] [unknown] [[ARG]] : $*Strukt +// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable6StruktV1rSivs +// CHECK: apply [[ORIG]]({{.*}}, [[BA]]) : $@convention(method) (Int, @inout Strukt) -> () +// CHECK: end_access [[BA]] : $*Strukt + @_dynamicReplacement(for: dynamic_replaceable_var) + var r : Int { + get { + return dynamic_replaceable_var + 1 + } + set { + dynamic_replaceable_var = newValue + 1 + } + } + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable6StruktVyS2icig"] @$s23dynamically_replaceable6StruktV1xS2i_tcig +// CHECK: bb0({{.*}} : @trivial $Int, [[SELF:%.*]] : @trivial $Strukt): +// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable6StruktV1xS2i_tcig +// CHECK: apply [[ORIG]]({{.*}}, [[SELF]]) : $@convention(method) (Int, Strukt) -> Int + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable6StruktVyS2icis"] @$s23dynamically_replaceable6StruktV1xS2i_tcis +// CHECK: bb0({{.*}} : @trivial $Int, {{.*}} : @trivial $Int, [[SELF:%.*]] : @trivial $*Strukt): +// CHECK: [[BA:%.*]] = begin_access [modify] [unknown] [[SELF]] : $*Strukt +// CHECK: [[ORIG:%.*]] = prev_dynamic_function_ref @$s23dynamically_replaceable6StruktV1xS2i_tcis +// CHECK: apply [[ORIG]]({{.*}}, {{.*}}, [[BA]]) : $@convention(method) (Int, Int, @inout Strukt) -> () +// CHECK: end_access [[BA]] : $*Strukt + + @_dynamicReplacement(for: subscript(_:)) + subscript(x y: Int) -> Int { + get { + return self[y] + } + set { + self[y] = newValue + } + } +} + + +struct GenericS { + dynamic init(x: Int) { + } + dynamic func dynamic_replaceable() { + } + + dynamic var dynamic_replaceable_var : Int { + get { + return 10 + } + set { + } + } + + dynamic subscript(x : Int) -> Int { + get { + return 10 + } + set { + } + } +} + +extension GenericS { + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable8GenericSV08dynamic_B0yyF"] @$s23dynamically_replaceable8GenericSV11replacementyyF +// CHECK: prev_dynamic_function_ref @$s23dynamically_replaceable8GenericSV11replacementyyF + @_dynamicReplacement(for: dynamic_replaceable()) + func replacement() { + dynamic_replaceable() + } +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable8GenericSV1xACyxGSi_tcfC"] @$s23dynamically_replaceable8GenericSV1yACyxGSi_tcfC +// CHECK: prev_dynamic_function_ref @$s23dynamically_replaceable8GenericSV1yACyxGSi_tcfC + @_dynamicReplacement(for: init(x:)) + init(y: Int) { + self.init(x: y + 1) + } + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable8GenericSV08dynamic_B4_varSivg"] @$s23dynamically_replaceable8GenericSV1rSivg +// CHECK: prev_dynamic_function_ref @$s23dynamically_replaceable8GenericSV1rSivg + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable8GenericSV08dynamic_B4_varSivs"] @$s23dynamically_replaceable8GenericSV1rSivs +// CHECK: prev_dynamic_function_ref @$s23dynamically_replaceable8GenericSV1rSivs + @_dynamicReplacement(for: dynamic_replaceable_var) + var r : Int { + get { + return dynamic_replaceable_var + 1 + } + set { + dynamic_replaceable_var = newValue + 1 + } + } + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable8GenericSVyS2icig"] @$s23dynamically_replaceable8GenericSV1xS2i_tcig +// CHECK: prev_dynamic_function_ref @$s23dynamically_replaceable8GenericSV1xS2i_tcig + +// CHECK-LABEL: sil hidden [dynamic_replacement_for "$s23dynamically_replaceable8GenericSVyS2icis"] @$s23dynamically_replaceable8GenericSV1xS2i_tcis +// CHECK: prev_dynamic_function_ref @$s23dynamically_replaceable8GenericSV1xS2i_tcis + @_dynamicReplacement(for: subscript(_:)) + subscript(x y: Int) -> Int { + get { + return self[y] + } + set { + self[y] = newValue + } + } +} diff --git a/test/attr/attr_native_dynamic.swift b/test/attr/attr_native_dynamic.swift new file mode 100644 index 0000000000000..9c37d8facf48b --- /dev/null +++ b/test/attr/attr_native_dynamic.swift @@ -0,0 +1,297 @@ +// RUN: %target-swift-frontend -swift-version 5 -typecheck -dump-ast %s 2>&1 | %FileCheck %s + +struct Strukt { + // CHECK: (struct_decl {{.*}} "Strukt" + // CHECK: (var_decl {{.*}} "dynamicStorageOnlyVar" type='Int' interface type='Int' access=internal dynamic readImpl=stored writeImpl=stored readWriteImpl=stored + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=dynamicStorageOnlyVar + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=dynamicStorageOnlyVar + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=dynamicStorageOnlyVar + dynamic var dynamicStorageOnlyVar : Int = 0 + + // CHECK: (var_decl {{.*}} "computedVar" type='Int' interface type='Int' access=internal dynamic readImpl=getter immutable + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=computedVar + dynamic var computedVar : Int { + return 0 + } + + // CHECK: (var_decl {{.*}} "computedVar2" type='Int' interface type='Int' access=internal dynamic readImpl=getter immutable + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=computedVar2 + dynamic var computedVar2 : Int { + get { + return 0 + } + } + + // CHECK: (var_decl {{.*}} "computedVarGetterSetter" type='Int' interface type='Int' access=internal dynamic readImpl=getter writeImpl=setter readWriteImpl=materialize_to_temporary + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=computedVarGetterSetter + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=computedVarGetterSetter + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=computedVarGetterSetter + dynamic var computedVarGetterSetter : Int { + get { + return 0 + } + set { + } + } + + // CHECK: (var_decl {{.*}} "computedVarGetterModify" type='Int' interface type='Int' access=internal dynamic readImpl=getter writeImpl=modify_coroutine readWriteImpl=modify_coroutine + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=computedVarGetterModify + // CHECK: (accessor_decl {{.*}} access=internal dynamic _modify_for=computedVarGetterModify + // CHECK: (accessor_decl {{.*}} access=internal set_for=computedVarGetterModify + dynamic var computedVarGetterModify : Int { + get { + return 0 + } + _modify { + } + } + + // CHECK: (var_decl {{.*}} "computedVarReadSet" type='Int' interface type='Int' access=internal dynamic readImpl=read_coroutine writeImpl=setter readWriteImpl=materialize_to_temporary + // CHECK: (accessor_decl {{.*}} access=internal dynamic _read_for=computedVarReadSet + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=computedVarReadSet + // CHECK: (accessor_decl {{.*}} access=internal get_for=computedVarReadSet + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=computedVarReadSet + dynamic var computedVarReadSet : Int { + _read { + } + set { + } + } + + // CHECK: (var_decl {{.*}} "computedVarReadModify" type='Int' interface type='Int' access=internal dynamic readImpl=read_coroutine writeImpl=modify_coroutine readWriteImpl=modify_coroutine + // CHECK: (accessor_decl {{.*}} access=internal dynamic _read_for=computedVarReadModify + // CHECK: (accessor_decl {{.*}} access=internal dynamic _modify_for=computedVarReadModify + // CHECK: (accessor_decl {{.*}} access=internal get_for=computedVarReadModify + // CHECK: (accessor_decl {{.*}} access=internal set_for=computedVarReadModify + dynamic var computedVarReadModify : Int { + _read { + } + _modify { + } + } + + // CHECK: (var_decl {{.*}} "storedWithObserver" type='Int' interface type='Int' access=internal dynamic readImpl=stored writeImpl=stored_with_observers readWriteImpl=materialize_to_temporary + // CHECK: (accessor_decl {{.*}}access=private dynamic didSet_for=storedWithObserver + // CHECK: (accessor_decl {{.*}}access=internal dynamic get_for=storedWithObserver + // CHECK: (accessor_decl {{.*}}access=internal set_for=storedWithObserver + // CHECK: (accessor_decl {{.*}}access=internal _modify_for=storedWithObserver + dynamic var storedWithObserver : Int { + didSet { + } + } + + // CHECK: (func_decl {{.*}} access=internal dynamic + dynamic func aMethod(arg: Int) -> Int { + return arg + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=getter writeImpl=setter readWriteImpl=materialize_to_temporary + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=subscript(_:) + dynamic subscript(_ index: Int) -> Int { + get { + return 1 + } + set { + } + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=getter writeImpl=modify_coroutine readWriteImpl=modify_coroutine + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic _modify_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal set_for=subscript(_:) + dynamic subscript(_ index: Float) -> Int { + get { + return 1 + } + _modify { + } + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=read_coroutine writeImpl=modify_coroutine readWriteImpl=modify_coroutine + // CHECK: (accessor_decl {{.*}} access=internal dynamic _read_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic _modify_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal set_for=subscript(_:) + dynamic subscript(_ index: Double) -> Int { + _read { + } + _modify { + } + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=read_coroutine writeImpl=setter readWriteImpl=materialize_to_temporary + // CHECK: (accessor_decl {{.*}} access=internal dynamic _read_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=subscript(_:) + dynamic subscript(_ index: Strukt) -> Int { + _read { + } + set { + } + } +} + +class Klass { + // CHECK: (class_decl {{.*}} "Klass" + // CHECK: (var_decl {{.*}} "dynamicStorageOnlyVar" type='Int' interface type='Int' access=internal dynamic readImpl=stored writeImpl=stored readWriteImpl=stored + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=dynamicStorageOnlyVar + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=dynamicStorageOnlyVar + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=dynamicStorageOnlyVar + dynamic var dynamicStorageOnlyVar : Int = 0 + + // CHECK: (var_decl {{.*}} "computedVar" type='Int' interface type='Int' access=internal dynamic readImpl=getter immutable + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=computedVar + dynamic var computedVar : Int { + return 0 + } + + // CHECK: (var_decl {{.*}} "computedVar2" type='Int' interface type='Int' access=internal dynamic readImpl=getter immutable + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=computedVar2 + dynamic var computedVar2 : Int { + get { + return 0 + } + } + + // CHECK: (var_decl {{.*}} "computedVarGetterSetter" type='Int' interface type='Int' access=internal dynamic readImpl=getter writeImpl=setter readWriteImpl=materialize_to_temporary + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=computedVarGetterSetter + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=computedVarGetterSetter + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=computedVarGetterSetter + dynamic var computedVarGetterSetter : Int { + get { + return 0 + } + set { + } + } + + // CHECK: (var_decl {{.*}} "computedVarGetterModify" type='Int' interface type='Int' access=internal dynamic readImpl=getter writeImpl=modify_coroutine readWriteImpl=modify_coroutine + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=computedVarGetterModify + // CHECK: (accessor_decl {{.*}} access=internal dynamic _modify_for=computedVarGetterModify + // CHECK: (accessor_decl {{.*}} access=internal set_for=computedVarGetterModify + dynamic var computedVarGetterModify : Int { + get { + return 0 + } + _modify { + } + } + + // CHECK: (var_decl {{.*}} "computedVarReadSet" type='Int' interface type='Int' access=internal dynamic readImpl=read_coroutine writeImpl=setter readWriteImpl=materialize_to_temporary + // CHECK: (accessor_decl {{.*}} access=internal dynamic _read_for=computedVarReadSet + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=computedVarReadSet + // CHECK: (accessor_decl {{.*}} access=internal get_for=computedVarReadSet + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=computedVarReadSet + dynamic var computedVarReadSet : Int { + _read { + } + set { + } + } + + // CHECK: (var_decl {{.*}} "computedVarReadModify" type='Int' interface type='Int' access=internal dynamic readImpl=read_coroutine writeImpl=modify_coroutine readWriteImpl=modify_coroutine + // CHECK: (accessor_decl {{.*}} access=internal dynamic _read_for=computedVarReadModify + // CHECK: (accessor_decl {{.*}} access=internal dynamic _modify_for=computedVarReadModify + // CHECK: (accessor_decl {{.*}} access=internal get_for=computedVarReadModify + // CHECK: (accessor_decl {{.*}} access=internal set_for=computedVarReadModify + dynamic var computedVarReadModify : Int { + _read { + } + _modify { + } + } + // CHECK: (func_decl {{.*}} "aMethod(arg:)" {{.*}} access=internal dynamic + dynamic func aMethod(arg: Int) -> Int { + return arg + } + + // CHECK-NOT: (func_decl {{.*}} "anotherMethod()" {{.*}} access=internal{{.*}} dynamic + func anotherMethod() -> Int { + return 3 + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=addressor writeImpl=mutable_addressor readWriteImpl=mutable_addressor + // CHECK: (accessor_decl {{.*}} access=internal dynamic address_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic mutableAddress_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal set_for=subscript(_:) + dynamic subscript(_ index: Int) -> Int { + unsafeAddress { + fatalError() + } + unsafeMutableAddress { + fatalError() + } + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=getter writeImpl=mutable_addressor readWriteImpl=mutable_addressor + // CHECK: (accessor_decl {{.*}} access=internal dynamic get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic mutableAddress_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal set_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=subscript(_:) + dynamic subscript(_ index: Float) -> Int { + get { + return 1 + } + unsafeMutableAddress { + fatalError() + } + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=read_coroutine writeImpl=mutable_addressor readWriteImpl=mutable_addressor + // CHECK: (accessor_decl {{.*}} access=internal dynamic _read_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic mutableAddress_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal set_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=subscript(_:) + dynamic subscript(_ index: Double) -> Int { + _read { + } + unsafeMutableAddress { + fatalError() + } + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=addressor writeImpl=setter readWriteImpl=materialize_to_temporary + // CHECK: (accessor_decl {{.*}} access=internal dynamic address_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic set_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal _modify_for=subscript(_:) + dynamic subscript(_ index: Int8) -> Int { + unsafeAddress { + fatalError() + } + set { + } + } + + // CHECK: (subscript_decl {{.*}} "subscript(_:)" {{.*}} access=internal dynamic readImpl=addressor writeImpl=modify_coroutine readWriteImpl=modify_coroutine + // CHECK: (accessor_decl {{.*}} access=internal dynamic address_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal dynamic _modify_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal get_for=subscript(_:) + // CHECK: (accessor_decl {{.*}} access=internal set_for=subscript(_:) + dynamic subscript(_ index: Int16) -> Int { + unsafeAddress { + fatalError() + } + _modify { + } + } +} + +class SubKlass : Klass { + + // CHECK: (class_decl {{.*}} "SubKlass" + // CHECK: (func_decl {{.*}} "aMethod(arg:)" interface type='(SubKlass) -> (Int) -> Int' access=internal {{.*}} dynamic + override dynamic func aMethod(arg: Int) -> Int { + return 23 + } + + // CHECK: (func_decl {{.*}} "anotherMethod()" interface type='(SubKlass) -> () -> Int' access=internal {{.*}} dynamic + override dynamic func anotherMethod() -> Int { + return 23 + } +} diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index c5aa3709f34d9..5097e1a6fab2b 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -17,6 +17,7 @@ # | availability-spec-list # | specialize-attr-spec-list # | implements-attr-arguments + # | named-attribute-string-argument # )? ')'? Node('Attribute', kind='Syntax', description=''' @@ -42,6 +43,8 @@ Child('ObjCName', kind='ObjCSelector'), Child('ImplementsArguments', kind='ImplementsAttributeArguments'), + Child('NamedAttributeString', + kind='NamedAttributeStringArgument'), ], description=''' The arguments of the attribute. In case the attribute \ takes multiple arguments, they are gather in the \ @@ -95,7 +98,38 @@ A trailing comma if this argument is followed by another one '''), ]), - + # The argument of '@_dynamic_replacement(for:)' or '@_private(sourceFile:)' + # named-attribute-string-arg -> 'name': string-literal + Node('NamedAttributeStringArgument', kind='Syntax', + description=''' + The argument for the `@_dynamic_replacement` or `@_private` \ + attribute of the form `for: "function()"` or `sourceFile: \ + "Src.swift"` + ''', + children=[ + Child('NameTok', kind='Token', + description='The label of the argument'), + Child('Colon', kind='ColonToken', + description='The colon separating the label and the value'), + Child('StringOrDeclname', kind='Syntax', node_choices=[ + Child('String', kind='StringLiteralToken'), + Child('Declname', kind='DeclName'), + ]), + ]), + Node('DeclName', kind='Syntax', children=[ + Child('DeclBaseName', kind='Syntax', description=''' + The base name of the protocol\'s requirement. + ''', + node_choices=[ + Child('Identifier', kind='IdentifierToken'), + Child('Operator', kind='PrefixOperatorToken'), + ]), + Child('DeclNameArguments', kind='DeclNameArguments', + is_optional=True, description=''' + The argument labels of the protocol\'s requirement if it \ + is a function requirement. + '''), + ]), # The argument of '@_implements(...)' # implements-attr-arguments -> simple-type-identifier ',' # (identifier | operator) decl-name-arguments diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 6c545570551bd..6b6968863cc2b 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -229,6 +229,8 @@ 'YieldStmt': 224, 'YieldList': 225, 'IdentifierList': 226, + 'NamedAttributeStringArgument': 227, + 'DeclName': 228, }