diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 972b22ff99c0e..4785653b6b689 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -581,6 +581,11 @@ BridgedDeclAttribute BridgedDeclAttribute_createSimple( BridgedASTContext cContext, BridgedDeclAttrKind cKind, BridgedSourceLoc cAtLoc, BridgedSourceLoc cNameLoc); +SWIFT_NAME("BridgedABIAttr.createParsed(_:atLoc:range:abiDecl:)") +BridgedABIAttr BridgedABIAttr_createParsed( + BridgedASTContext cContext, BridgedSourceLoc atLoc, + BridgedSourceRange range, BridgedNullableDecl abiDecl); + enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedAccessLevel { BridgedAccessLevelPrivate, BridgedAccessLevelFilePrivate, @@ -918,8 +923,8 @@ BridgedUnavailableFromAsyncAttr BridgedUnavailableFromAsyncAttr_createParsed( struct BridgedFingerprint; -SWIFT_NAME("BridgedDecl.setAttrs(self:_:)") -void BridgedDecl_setAttrs(BridgedDecl decl, BridgedDeclAttributes attrs); +SWIFT_NAME("BridgedDecl.attachParsedAttrs(self:_:)") +void BridgedDecl_attachParsedAttrs(BridgedDecl decl, BridgedDeclAttributes attrs); enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedStaticSpelling { BridgedStaticSpellingNone, diff --git a/include/swift/AST/ASTBridgingWrappers.def b/include/swift/AST/ASTBridgingWrappers.def index 320fcea135a81..7057a75d47eb2 100644 --- a/include/swift/AST/ASTBridgingWrappers.def +++ b/include/swift/AST/ASTBridgingWrappers.def @@ -68,7 +68,7 @@ // Some of the base classes need to be nullable to allow them to be used as // optional parameters. -AST_BRIDGING_WRAPPER_NONNULL(Decl) +AST_BRIDGING_WRAPPER_NULLABLE(Decl) AST_BRIDGING_WRAPPER_NONNULL(DeclContext) AST_BRIDGING_WRAPPER_NONNULL(SourceFile) AST_BRIDGING_WRAPPER_NULLABLE(Stmt) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index d3638bb3219a6..d0904f6f310f3 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -67,6 +67,7 @@ namespace llvm { } namespace swift { + class ABIAttr; class AbstractFunctionDecl; class ASTContext; enum class Associativity : unsigned char; diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 650bdf98fe945..36f4d54409b47 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -209,6 +209,14 @@ class ASTMangler : public Mangler { return tryMangleSubstitution(type.getPointer()); } +protected: + using Mangler::addSubstitution; + void addSubstitution(const Decl *decl); + + using Mangler::tryMangleSubstitution; + bool tryMangleSubstitution(const Decl *decl); + +public: std::string mangleClosureEntity(const AbstractClosureExpr *closure, SymbolKind SKind); diff --git a/include/swift/AST/ASTScope.h b/include/swift/AST/ASTScope.h index 73cf141218ea3..6620970c9e881 100644 --- a/include/swift/AST/ASTScope.h +++ b/include/swift/AST/ASTScope.h @@ -142,6 +142,7 @@ class ASTScopeImpl : public ASTAllocated { friend class IterableTypeBodyPortion; friend class ScopeCreator; friend class ASTSourceFileScope; + friend class ABIAttributeScope; friend class Lowering::SILGenFunction; #pragma mark - tree state @@ -250,12 +251,15 @@ class ASTScopeImpl : public ASTAllocated { void printRange(llvm::raw_ostream &out) const; + void printParents(llvm::raw_ostream &out) const; + protected: virtual void printSpecifics(llvm::raw_ostream &out) const {} virtual NullablePtr addressForPrinting() const; public: SWIFT_DEBUG_DUMP; + SWIFT_DEBUG_DUMPER(dumpParents()); void dumpOneScopeMapLocation(std::pair lineColumn); @@ -302,6 +306,9 @@ class ASTScopeImpl : public ASTAllocated { SourceFile *sourceFile, SourceLoc loc, llvm::function_ref consume); + static ABIAttr *lookupEnclosingABIAttributeScope( + SourceFile *sourceFile, SourceLoc loc); + static CatchNode lookupCatchNode(ModuleDecl *module, SourceLoc loc); /// Scopes that cannot bind variables may set this to true to create more @@ -945,6 +952,40 @@ class CustomAttributeScope final : public ASTScopeImpl { } }; +/// The scope for ABI attributes and their arguments. +/// +/// Source locations for the attribute name and its arguments are in the +/// custom attribute, so lookup is invoked from within the attribute +/// itself. +class ABIAttributeScope final : public ASTScopeImpl { +public: + ABIAttr *attr; + Decl *decl; + + ABIAttributeScope(ABIAttr *attr, Decl *decl) + : ASTScopeImpl(ScopeKind::ABIAttribute), attr(attr), decl(decl) {} + virtual ~ABIAttributeScope() {} + +protected: + ASTScopeImpl *expandSpecifically(ScopeCreator &) override; + +public: + SourceRange + getSourceRangeOfThisASTNode(bool omitAssertions = false) const override; + NullablePtr addressForPrinting() const override { return decl; } + + bool ignoreInDebugInfo() const override { return true; } + +private: + AnnotatedInsertionPoint + expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &); + +public: + static bool classof(const ASTScopeImpl *scope) { + return scope->getKind() == ScopeKind::ABIAttribute; + } +}; + /// PatternBindingDecl's (PBDs) are tricky (See the comment for \c /// PatternBindingDecl): /// diff --git a/include/swift/AST/ASTScopeNodes.def b/include/swift/AST/ASTScopeNodes.def index 79f2ed0d8b13c..0c8137021c32a 100644 --- a/include/swift/AST/ASTScopeNodes.def +++ b/include/swift/AST/ASTScopeNodes.def @@ -89,6 +89,7 @@ SCOPE_NODE(CaseLabelItem) SCOPE_NODE(CaseStmtBody) STMT_SCOPE_NODE(BraceStmt) EXPR_SCOPE_NODE(Try) +DECL_ATTRIBUTE_SCOPE_NODE(ABIAttribute) #undef DECL_ATTRIBUTE_SCOPE_NODE #undef EXPR_SCOPE_NODE diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 1ee7c96db3d49..aeed6619fb640 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -32,6 +32,7 @@ #include "swift/AST/StorageImpl.h" #include "swift/Basic/Debug.h" #include "swift/Basic/EnumTraits.h" +#include "swift/Basic/Feature.h" #include "swift/Basic/InlineBitfield.h" #include "swift/Basic/Located.h" #include "swift/Basic/OptimizationMode.h" @@ -491,6 +492,8 @@ class DeclAttribute : public AttributeBase { LLVM_READNONE static bool canAttributeAppearOnDeclKind(DeclAttrKind DAK, DeclKind DK); + static std::optional getRequiredFeature(DeclAttrKind DK); + /// Returns the source name of the attribute, without the @ or any arguments. StringRef getAttrName() const; @@ -2875,6 +2878,12 @@ template struct ToAttributeKind { return cast(Attr); return std::nullopt; } + + std::optional operator()(DeclAttribute *Attr) { + if (isa(Attr) && (Attr->isValid() || AllowInvalid)) + return cast(Attr); + return std::nullopt; + } }; /// The @_allowFeatureSuppression(Foo, Bar) attribute. The feature @@ -2908,6 +2917,31 @@ class AllowFeatureSuppressionAttr final UNIMPLEMENTED_CLONE(AllowFeatureSuppressionAttr) }; +/// Defines the @abi attribute. +class ABIAttr : public DeclAttribute { +public: + ABIAttr(Decl *abiDecl, SourceLoc AtLoc, SourceRange Range, bool Implicit) + : DeclAttribute(DeclAttrKind::ABI, AtLoc, Range, Implicit), + abiDecl(abiDecl) + { } + + ABIAttr(Decl *abiDecl, bool Implicit) + : ABIAttr(abiDecl, SourceLoc(), SourceRange(), Implicit) {} + + /// The declaration which will be used to compute a mangled name. + /// + /// \note For a \c VarDecl with a parent \c PatternBindingDecl , this should + /// point to the parent \c PatternBindingDecl . (That accommodates the way + /// sibling \c VarDecl s share attributes.) + Decl *abiDecl; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DeclAttrKind::ABI; + } + + UNIMPLEMENTED_CLONE(ABIAttr) +}; + /// Attributes that may be applied to declarations. class DeclAttributes { /// Linked list of declaration attributes. @@ -3047,17 +3081,25 @@ class DeclAttributes { } public: - template + template using AttributeKindRange = - OptionalTransformRange, + OptionalTransformRange, ToAttributeKind, - const_iterator>; + Iterator>; + + /// Return a range with all attributes in DeclAttributes with AttrKind + /// ATTR. + template + AttributeKindRange getAttributes() const { + return AttributeKindRange( + make_range(begin(), end()), ToAttributeKind()); + } /// Return a range with all attributes in DeclAttributes with AttrKind /// ATTR. template - AttributeKindRange getAttributes() const { - return AttributeKindRange( + AttributeKindRange getAttributes() { + return AttributeKindRange( make_range(begin(), end()), ToAttributeKind()); } diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 6655ef88123cb..eb4425f66cd00 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -95,6 +95,7 @@ namespace swift { class MacroDefinition; class ModuleDecl; class NamedPattern; + enum NLOptions : unsigned; class EnumCaseDecl; class EnumElementDecl; class ParameterList; @@ -1022,6 +1023,17 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi /// attribute macro expansion. DeclAttributes getSemanticAttrs() const; + /// Register the relationship between \c this and \p attr->abiDecl , assuming + /// that \p attr is attached to \c this . This is necessary for + /// \c ABIRoleInfo::ABIRoleInfo() to determine that \c attr->abiDecl + /// is ABI-only and locate its API counterpart. + void recordABIAttr(ABIAttr *attr); + + /// Set this declaration's attributes to the specified attribute list, + /// applying any post-processing logic appropriate for attributes parsed + /// from source code. + void attachParsedAttrs(DeclAttributes attrs); + /// True if this declaration provides an implementation for an imported /// Objective-C declaration. This implies various restrictions and special /// behaviors for it and, if it's an extension, its members. @@ -2657,6 +2669,11 @@ class PatternBindingDecl final : public Decl, /// Returns \c true if this pattern binding was created by the debugger. bool isDebuggerBinding() const { return Bits.PatternBindingDecl.IsDebugger; } + /// Returns the \c VarDecl in this PBD at the same offset in the same + /// pattern entry as \p otherVar is in its PBD, or \c nullptr if this PBD is + /// too different from \p otherVar 's to find an equivalent variable. + VarDecl *getVarAtSimilarStructuralPosition(VarDecl *otherVar); + static bool classof(const Decl *D) { return D->getKind() == DeclKind::PatternBinding; } @@ -4344,6 +4361,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { IncludeAttrImplements = 1 << 0, /// Whether to exclude members of macro expansions. ExcludeMacroExpansions = 1 << 1, + /// If @abi attributes are present, return the decls representing the ABI, + /// not the API. + ABIProviding = 1 << 2, }; /// Find all of the declarations with the given name within this nominal type @@ -9778,6 +9798,126 @@ const ParamDecl *getParameterAt(const ValueDecl *source, unsigned index); /// nullptr if the source does not have a parameter list. const ParamDecl *getParameterAt(const DeclContext *source, unsigned index); +class ABIRole { +public: + enum Value : uint8_t { + Neither = 0, + ProvidesABI = 1 << 0, + ProvidesAPI = 1 << 1, + Either = ProvidesABI | ProvidesAPI, + }; + + Value value; + + ABIRole(Value value) + : value(value) + { } + + ABIRole() + : ABIRole(Neither) + { } + + explicit ABIRole(NLOptions opts); + + template + explicit ABIRole(OptionSet flags) + : value(flags.contains(FlagType::ABIProviding) ? ProvidesABI : ProvidesAPI) + { } + + inline ABIRole operator|(ABIRole rhs) const { + return ABIRole(ABIRole::Value(value | rhs.value)); + } + inline ABIRole &operator|=(ABIRole rhs) { + value = ABIRole::Value(value | rhs.value); + return *this; + } + inline ABIRole operator&(ABIRole rhs) const { + return ABIRole(ABIRole::Value(value & rhs.value)); + } + inline ABIRole &operator&=(ABIRole rhs) { + value = ABIRole::Value(value & rhs.value); + return *this; + } + inline ABIRole operator~() const { + return ABIRole(ABIRole::Value(~value)); + } + + operator bool() const { + return value != Neither; + } +}; + +namespace abi_role_detail { + +using Storage = llvm::PointerIntPair; +Storage computeStorage(Decl *decl); + +} + +/// Specifies the \c ABIAttr -related behavior of this declaration +/// and provides access to its counterpart. +/// +/// A given declaration may provide the API, the ABI, or both. If it provides +/// API, the counterpart is the matching ABI-providing decl; if it provides +/// ABI, the countepart is the matching API-providing decl. A declaration +/// which provides both API and ABI is its own counterpart. +/// +/// If the counterpart is \c nullptr , this indicates a fundamental mismatch +/// between decl and counterpart. Sometimes this mismatch is a difference in +/// decl kind; in these cases, \c getCounterpartUnchecked() will return the +/// incorrect counterpart. +template +class ABIRoleInfo { + friend abi_role_detail::Storage abi_role_detail::computeStorage(Decl *); + + abi_role_detail::Storage counterpartAndFlags; + + ABIRoleInfo(abi_role_detail::Storage storage) + : counterpartAndFlags(storage) + { } + +public: + explicit ABIRoleInfo(const SpecificDecl *decl) + : ABIRoleInfo(abi_role_detail::computeStorage(const_cast(decl))) + { } + + Decl *getCounterpartUnchecked() const { + return counterpartAndFlags.getPointer(); + } + + SpecificDecl *getCounterpart() const { + return dyn_cast_or_null(getCounterpartUnchecked()); + } + + ABIRole getRole() const { + return ABIRole(ABIRole::Value(counterpartAndFlags.getInt())); + } + + bool matches(ABIRole desiredRole) const { + return getRole() & desiredRole; + } + + template + bool matchesOptions(Options opts) const { + return matches(ABIRole(opts)); + } + + bool providesABI() const { + return matches(ABIRole::ProvidesABI); + } + + bool providesAPI() const { + return matches(ABIRole::ProvidesAPI); + } + + bool hasABIAttr() const { + return !providesABI(); + } +}; + +template +ABIRoleInfo(const SpecificDecl *decl) -> ABIRoleInfo; + StringRef getAccessorNameForDiagnostic(AccessorDecl *accessor, bool article, std::optional underscored = std::nullopt); diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index 63f22ec57be1a..12d7048e5c093 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -42,6 +42,16 @@ DECL_ATTR_ALIAS(SPELLING, CLASS) #endif +// Diagnose any use of the attribute CLASS without FEATURE_NAME enabled, +// and also enable other special behavior. If you use this for an experimental +// feature, please add test cases to: +// +// * test/attr/feature_requirement.swift +// * test/IDE/complete_decl_attribute_feature_requirement.swift +#ifndef DECL_ATTR_FEATURE_REQUIREMENT +#define DECL_ATTR_FEATURE_REQUIREMENT(CLASS, FEATURE_NAME) +#endif + #ifndef LAST_DECL_ATTR #define LAST_DECL_ATTR(CLASS) #endif @@ -523,7 +533,12 @@ DECL_ATTR(safe, Safe, ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, 163) -LAST_DECL_ATTR(Safe) +DECL_ATTR(abi, ABI, + OnAbstractFunction | OnVar /* will eventually add types */ | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + 164) +DECL_ATTR_FEATURE_REQUIREMENT(ABI, ABIAttribute) + +LAST_DECL_ATTR(ABI) #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS @@ -531,4 +546,5 @@ LAST_DECL_ATTR(Safe) #undef CONTEXTUAL_SIMPLE_DECL_ATTR #undef DECL_ATTR #undef CONTEXTUAL_DECL_ATTR +#undef DECL_ATTR_FEATURE_REQUIREMENT #undef LAST_DECL_ATTR diff --git a/include/swift/AST/DiagnosticsClangImporter.def b/include/swift/AST/DiagnosticsClangImporter.def index 76ceaef0f0f0e..53f5f53033f6e 100644 --- a/include/swift/AST/DiagnosticsClangImporter.def +++ b/include/swift/AST/DiagnosticsClangImporter.def @@ -322,5 +322,15 @@ ERROR(unknown_template_parameter,none, ERROR(type_template_parameter_expected,none, "template parameter '%0' expected to be a type parameter", (StringRef)) +NOTE(bridged_type_not_found_in_module,none, + "could not find type '%0' for bridging; module '%1' may be broken", + (StringRef, StringRef)) + +NOTE(bridged_pointer_type_not_found,none, + "could not find type '%select{UnsafeMutableRawPointer|UnsafeRawPointer|" + "UnsafeMutablePointer|UnsafePointer|AutoreleasingUnsafeMutablePointer}0' " + "for bridging; module 'Swift' may be broken", + (unsigned)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index f6f09a00ee4e3..1a74f4fab3830 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -228,6 +228,9 @@ ERROR(let_cannot_be_addressed_property,none, ERROR(disallowed_var_multiple_getset,none, "'var' declarations with multiple variables cannot have explicit" " getters/setters", ()) +ERROR(stub_decl_cannot_have_body,none, + "stub %0 cannot have body", + (DescriptiveDeclKind)) ERROR(disallowed_init,none, "initial value is not allowed here", ()) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index acc4944aff20d..2b488f5f129bb 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8135,5 +8135,40 @@ ERROR(availability_value_generic_type_only_version_newer, none, ERROR(invalid_value_for_type_same_type,none, "cannot constrain type parameter %0 to be integer %1", (Type, Type)) +//===----------------------------------------------------------------------===// +// MARK: @abi Attribute +//===----------------------------------------------------------------------===// + +ERROR(attr_abi_mismatched_kind,none, + "cannot give %kind0 the ABI of a %1", + (Decl *, DescriptiveDeclKind)) + +ERROR(attr_abi_mismatched_arity,none, + "cannot give %kind0 the ABI of a %kindonly0 with a different number of " + "low-level parameters", + (ValueDecl *)) + +ERROR(attr_abi_mismatched_throws,none, + "cannot give %0 the ABI of a %kindonly0 which %select{cannot|can}1 throw", + (ValueDecl *, /*abiCanThrow=*/bool)) + +ERROR(attr_abi_mismatched_async,none, + "cannot give %0 the ABI of %select{a non-async|an async}1 %kindonly0", + (ValueDecl *, /*abiIsAsync=*/bool)) + +ERROR(attr_abi_mismatched_pbd_size,none, + "cannot give pattern binding the ABI of a binding with " + "%select{more|fewer}0 patterns", + (/*abiHasExtra=*/bool)) + +ERROR(attr_abi_mismatched_var,none, + "no match for %select{%kind0 in the ABI|ABI %kind0}1", + (ValueDecl *, /*isABI=*/bool)) + +ERROR(attr_abi_incompatible_with_silgen_name,none, + "cannot use '@_silgen_name' and '@abi' on the same %0 because they serve " + "the same purpose", + (DescriptiveDeclKind)) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/include/swift/AST/LookupKinds.h b/include/swift/AST/LookupKinds.h index e697cc95299b6..03356ec51fda8 100644 --- a/include/swift/AST/LookupKinds.h +++ b/include/swift/AST/LookupKinds.h @@ -64,6 +64,10 @@ enum NLOptions : unsigned { /// from a module that has not been imported. NL_IgnoreMissingImports = 1 << 9, + /// If @abi attributes are present, return the decls representing the ABI, + /// not the API. + NL_ABIProviding = 1 << 10, + /// The default set of options used for qualified name lookup. /// /// FIXME: Eventually, add NL_ProtocolMembers to this, once all of the @@ -97,6 +101,9 @@ void simple_display(llvm::raw_ostream &out, NLOptions options); enum class ModuleLookupFlags : unsigned { /// Exclude names introduced by macro expansions in the top-level module. ExcludeMacroExpansions = 1 << 0, + /// If @abi attributes are present, return the decls representing the ABI, + /// not the API. + ABIProviding = 1 << 1, }; } // end namespace swift diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index 66469ef1a558c..6c85c51827ddf 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -253,6 +253,11 @@ enum class UnqualifiedLookupFlags { /// This lookup should include members that would otherwise be filtered out /// because they come from a module that has not been imported. IgnoreMissingImports = 1 << 10, + /// If @abi attributes are present, return the decls representing the ABI, + /// not the API. + ABIProviding = 1 << 11, + + // Reminder: If you add a flag, make sure you update simple_display() below }; using UnqualifiedLookupOptions = OptionSet; @@ -562,6 +567,9 @@ template void filterForDiscriminator(SmallVectorImpl &results, DebuggerClient *debugClient); +/// \returns Whether the given source location is inside an \@abi attribute. +bool isInABIAttr(SourceFile *sourceFile, SourceLoc loc); + /// \returns The set of macro declarations with the given name that /// fulfill any of the given macro roles. SmallVector lookupMacros(DeclContext *dc, @@ -756,8 +764,16 @@ class ASTScope : public ASTAllocated { /// local declarations inside the innermost syntactic scope only. static void lookupLocalDecls(SourceFile *, DeclName, SourceLoc, bool stopAfterInnermostBraceStmt, + ABIRole roleFilter, SmallVectorImpl &); + static void lookupLocalDecls(SourceFile *sf, DeclName name, SourceLoc loc, + bool stopAfterInnermostBraceStmt, + SmallVectorImpl &results) { + lookupLocalDecls(sf, name, loc, stopAfterInnermostBraceStmt, + ABIRole::ProvidesAPI, results); + } + /// Returns the result if there is exactly one, nullptr otherwise. static ValueDecl *lookupSingleLocalDecl(SourceFile *, DeclName, SourceLoc); @@ -802,6 +818,18 @@ class ASTScope : public ASTAllocated { SourceFile *sourceFile, SourceLoc loc, llvm::function_ref consume); + /// Look up the scope tree for the nearest enclosing ABI attribute at + /// the given source location. + /// + /// \param sourceFile The source file containing the given location. + /// \param loc The source location to start lookup from. + /// \param consume A function that is called when an ABI attribute + /// scope is found. If \c consume returns \c true, lookup + /// will stop. If \c consume returns \c false, lookup will + /// continue up the scope tree. + static ABIAttr *lookupEnclosingABIAttributeScope( + SourceFile *sourceFile, SourceLoc loc); + /// Look up the scope tree for the nearest point at which an error thrown from /// this location can be caught or rethrown. /// diff --git a/include/swift/AST/NameLookupRequests.h b/include/swift/AST/NameLookupRequests.h index 4572a79475471..36fe7b6974dbe 100644 --- a/include/swift/AST/NameLookupRequests.h +++ b/include/swift/AST/NameLookupRequests.h @@ -25,6 +25,7 @@ #include "swift/AST/TypeOrExtensionDecl.h" #include "swift/Basic/Statistic.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/TinyPtrVector.h" namespace swift { @@ -1002,6 +1003,32 @@ class ObjCCategoryNameMapRequest bool isCached() const { return true; } }; +struct DeclABIRoleInfoResult { + llvm::PointerIntPair storage; + DeclABIRoleInfoResult(Decl *counterpart, uint8_t roleValue) + : storage(counterpart, roleValue) {} +}; + +/// Return the ABI role info (role and counterpart) of a given declaration. +class DeclABIRoleInfoRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + + void recordABIOnly(Evaluator &evaluator, Decl *counterpart); + +private: + friend SimpleRequest; + + // Evaluation. + DeclABIRoleInfoResult evaluate(Evaluator &evaluator, Decl *decl) const; + +public: + bool isCached() const { return true; } +}; + #define SWIFT_TYPEID_ZONE NameLookup #define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def" #include "swift/Basic/DefineTypeIDZone.h" diff --git a/include/swift/AST/NameLookupTypeIDZone.def b/include/swift/AST/NameLookupTypeIDZone.def index f358767f5c16c..a025901ece813 100644 --- a/include/swift/AST/NameLookupTypeIDZone.def +++ b/include/swift/AST/NameLookupTypeIDZone.def @@ -118,3 +118,5 @@ SWIFT_REQUEST(NameLookup, LookupIntrinsicRequest, FuncDecl *(ModuleDecl *, Identifier), Cached, NoLocationInfo) SWIFT_REQUEST(NameLookup, ObjCCategoryNameMapRequest, ObjCCategoryNameMap(ClassDecl *, ExtensionDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(NameLookup, DeclABIRoleInfoRequest, + DeclABIRoleInfoResult(Decl *), Cached, NoLocationInfo) diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index 1dd5af359e2d6..2b630e2272284 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -237,6 +237,10 @@ struct PrintOptions { /// Use the original module name to qualify a symbol. bool UseOriginallyDefinedInModuleNames = false; + /// Add a `@_silgen_name` attribute to each function that + /// is compatible with one that specifies its mangled name. + bool PrintSyntheticSILGenName = false; + /// Print Swift.Array and Swift.Optional with sugared syntax /// ([] and ?), even if there are no sugar type nodes. bool SynthesizeSugarOnTypes = false; diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 7c343f44b2bd2..65c70df097738 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -436,6 +436,9 @@ EXPERIMENTAL_FEATURE(GenerateForceToMainActorThunks, false) EXPERIMENTAL_FEATURE(AddressableParameters, true) +/// Allow the @abi attribute. +SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ABIAttribute, true) + #undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE #undef EXPERIMENTAL_FEATURE #undef UPCOMING_FEATURE diff --git a/include/swift/IDE/CompletionLookup.h b/include/swift/IDE/CompletionLookup.h index a61b4515cadfe..4d42cbcb21925 100644 --- a/include/swift/IDE/CompletionLookup.h +++ b/include/swift/IDE/CompletionLookup.h @@ -600,7 +600,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { SourceLoc CodeCompletionLoc); static bool canUseAttributeOnDecl(DeclAttrKind DAK, bool IsInSil, - bool IsConcurrencyEnabled, + const LangOptions &langOpts, std::optional DK, StringRef Name); void getAttributeDeclCompletions(bool IsInSil, std::optional DK); diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index e561826431f8b..28e9e8764024f 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -933,7 +933,7 @@ class Parser { bool isStartOfGetSetAccessor(); /// Flags that control the parsing of declarations. - enum ParseDeclFlags { + enum ParseDeclFlags : uint16_t { PD_Default = 0, PD_AllowTopLevel = 1 << 1, PD_HasContainerType = 1 << 2, @@ -944,6 +944,7 @@ class Parser { PD_InExtension = 1 << 7, PD_InStruct = 1 << 8, PD_InEnum = 1 << 9, + PD_StubOnly = 1 << 10, }; /// Options that control the parsing of declarations. @@ -953,20 +954,24 @@ class Parser { ParserStatus parseDecl(bool IsAtStartOfLineOrPreviousHadSemi, bool IfConfigsAreDeclAttrs, - llvm::function_ref Handler); + llvm::function_ref Handler, + bool stubOnly = false); std::pair, std::optional> parseDeclListDelayed(IterableDeclContext *IDC); bool parseMemberDeclList(SourceLoc &LBLoc, SourceLoc &RBLoc, Diag<> LBraceDiag, Diag<> RBraceDiag, - IterableDeclContext *IDC); + IterableDeclContext *IDC, + ParseDeclOptions Flags); bool canDelayMemberDeclParsing(bool &HasOperatorDeclarations, bool &HasNestedClassDeclarations, - bool &HasDerivativeDeclarations); + bool &HasDerivativeDeclarations, + ParseDeclOptions Flags); - bool canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations); + bool canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations, + ParseDeclOptions Flags); bool delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, IterableDeclContext *IDC); @@ -1318,7 +1323,8 @@ class Parser { bool HasFuncKeyword = true); BodyAndFingerprint parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD); - void parseAbstractFunctionBody(AbstractFunctionDecl *AFD); + void parseAbstractFunctionBody(AbstractFunctionDecl *AFD, + ParseDeclOptions Flags); BodyAndFingerprint parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 38da374da8897..4fccbee25b386 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -967,9 +967,10 @@ namespace { } /// Print an unnamed field containing a node's name, read from a declaration. - void printDeclName(const ValueDecl *D, bool leadingSpace = true) { - if (D->getName()) { - printName(D->getName(), leadingSpace); + void printDeclName(const Decl *D, bool leadingSpace = true) { + auto VD = dyn_cast(D); + if (VD && VD->getName()) { + printName(VD->getName(), leadingSpace); } else { if (leadingSpace) OS << ' '; @@ -979,7 +980,7 @@ namespace { } /// Print a field containing a node's name, read from a declaration. - void printDeclNameField(const ValueDecl *D, StringRef name) { + void printDeclNameField(const Decl *D, StringRef name) { printFieldRaw([&](raw_ostream &os) { printDeclName(D, /*leadingSpace=*/false); }, name); @@ -990,6 +991,7 @@ namespace { TerminalColor Color = DeclColor) { printFieldQuotedRaw([&](raw_ostream &OS) { declRef.dump(OS); }, label, Color); + printFlag(!ABIRoleInfo(declRef.getDecl()).providesAPI(), "abi_only_decl"); } void printThrowDest(ThrownErrorDestination throws, bool wantNothrow) { @@ -1185,6 +1187,8 @@ namespace { printFieldQuoted(implAttr->CategoryName.str(), label); } + printFlag(!ABIRoleInfo(D).providesAPI(), "abi_only"); + printSourceRange(D->getSourceRange(), &D->getASTContext()); printFlag(D->TrailingSemiLoc.isValid(), "trailing_semi", DeclModifierColor); @@ -1383,9 +1387,13 @@ namespace { OS << "\")"; }); } - auto lifetimeString = getDumpString(VD->getLifetimeAnnotation()); - if (!lifetimeString.empty()) - printFlag(lifetimeString); + // In some cases, getLifetimeAnnotation() can fail before extension + // binding. hasResolvedImports() approximates an extension binding check. + if (VD->getModuleContext()->hasResolvedImports()) { + auto lifetimeString = getDumpString(VD->getLifetimeAnnotation()); + if (!lifetimeString.empty()) + printFlag(lifetimeString); + } } void printCommon(NominalTypeDecl *NTD, const char *Name, StringRef Label, @@ -1905,8 +1913,13 @@ void swift::printContext(raw_ostream &os, DeclContext *dc) { break; case DeclContextKind::ExtensionDecl: - if (auto extendedNominal = cast(dc)->getExtendedNominal()) { + if (auto repr = cast(dc)->getExtendedTypeRepr()) { + repr->print(os); + } else if (cast(dc)->hasBeenBound()) { + auto extendedNominal = cast(dc)->getExtendedNominal(); printName(os, extendedNominal->getName()); + } else { + os << ""; } os << " extension"; break; @@ -3853,6 +3866,11 @@ class PrintAttribute : public AttributeVisitor, #undef TRIVIAL_ATTR_PRINTER + void visitABIAttr(ABIAttr *Attr, StringRef label) { + printCommon(Attr, "abi_attr", label); + printRec(Attr->abiDecl, "decl"); + printFoot(); + } void visitAccessControlAttr(AccessControlAttr *Attr, StringRef label) { printCommon(Attr, "access_control_attr", label); printField(Attr->getAccess(), "access_level"); diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 1b5c03833ec19..1c04e4868039c 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -66,6 +66,54 @@ using namespace swift; using namespace swift::Mangle; +template +static DeclType *getABIDecl(DeclType *D) { + if (!D) + return nullptr; + + auto abiRole = ABIRoleInfo(D); + if (!abiRole.providesABI()) + return abiRole.getCounterpart(); + return nullptr; +} + +static std::optional +getABIDecl(ASTMangler::SymbolicReferent ref) { + switch (ref.getKind()) { + case ASTMangler::SymbolicReferent::NominalType: + if (auto abiTypeDecl = getABIDecl(ref.getNominalType())) { + return ASTMangler::SymbolicReferent(abiTypeDecl); + } + break; + + case ASTMangler::SymbolicReferent::OpaqueType: + if (auto abiTypeDecl = getABIDecl(ref.getOpaqueType())) { + return ASTMangler::SymbolicReferent(abiTypeDecl); + } + break; + + case ASTMangler::SymbolicReferent::ExtendedExistentialTypeShape: + // Do nothing; mangling will use the underlying ABI decls in the end. + break; + } + + return std::nullopt; +} + +void ASTMangler::addSubstitution(const Decl *decl) { + if (auto abiDecl = getABIDecl(decl)) { + return addSubstitution(abiDecl); + } + return Mangler::addSubstitution(decl); +} + +bool ASTMangler::tryMangleSubstitution(const Decl *decl) { + if (auto abiDecl = getABIDecl(decl)) { + return tryMangleSubstitution(abiDecl); + } + return Mangler::tryMangleSubstitution(decl); +} + bool ASTMangler::inversesAllowed(const Decl *decl) { if (!decl) return true; @@ -302,6 +350,10 @@ std::string ASTMangler::mangleClosureWitnessThunk( } std::string ASTMangler::mangleGlobalVariableFull(const VarDecl *decl) { + if (auto abiDecl = getABIDecl(decl)) { + return mangleGlobalVariableFull(abiDecl); + } + // Clang globals get mangled using Clang's mangler. if (auto clangDecl = dyn_cast_or_null(decl->getClangDecl())) { @@ -431,6 +483,10 @@ std::string ASTMangler::mangleGlobalInit(const PatternBindingDecl *pd, Pattern *pattern = pd->getPattern(pbdEntry); bool first = true; pattern->forEachVariable([&](VarDecl *D) { + if (auto abiD = getABIDecl(D)) { + D = abiD; + } + if (first) { BaseEntitySignature base(D); appendContextOf(D, base); @@ -519,6 +575,10 @@ std::string ASTMangler::mangleAutoDiffLinearMap( void ASTMangler::beginManglingWithAutoDiffOriginalFunction( const AbstractFunctionDecl *afd) { + if (auto abiAFD = getABIDecl(afd)) { + return beginManglingWithAutoDiffOriginalFunction(abiAFD); + } + if (auto *attr = afd->getAttrs().getAttribute()) { beginManglingWithoutPrefix(); appendOperator(attr->Name); @@ -852,6 +912,10 @@ void ASTMangler::appendAnyDecl(const ValueDecl *Decl) { } else if (auto GTD = dyn_cast(Decl)) { appendAnyGenericType(GTD); } else if (isa(Decl)) { + if (auto abiDecl = getABIDecl(Decl)) { + return appendAnyDecl(abiDecl); + } + BaseEntitySignature base(Decl); appendContextOf(Decl, base); appendDeclName(Decl); @@ -904,6 +968,10 @@ std::string ASTMangler::mangleAccessorEntityAsUSR(AccessorKind kind, } std::string ASTMangler::mangleLocalTypeDecl(const TypeDecl *type) { + if (auto abiType = getABIDecl(type)) { + return mangleLocalTypeDecl(abiType); + } + beginManglingWithoutPrefix(); AllowNamelessEntities = true; OptimizeProtocolNames = false; @@ -954,6 +1022,10 @@ std::string ASTMangler::mangleHasSymbolQuery(const ValueDecl *Decl) { } else if (auto GTD = dyn_cast(Decl)) { appendAnyGenericType(GTD); } else if (isa(Decl)) { + if (auto abiDecl = getABIDecl(Decl)) { + Decl = abiDecl; + } + BaseEntitySignature nullBase(nullptr); appendContextOf(Decl, nullBase); appendDeclName(Decl); @@ -1061,6 +1133,7 @@ getOverriddenSwiftProtocolObjCName(const ValueDecl *decl, } void ASTMangler::appendDeclName(const ValueDecl *decl, DeclBaseName name) { + ASSERT(!getABIDecl(decl) && "caller should make sure we get ABI decls"); if (name.empty()) name = decl->getBaseName(); assert(!name.isSpecial() && "Cannot print special names"); @@ -1196,6 +1269,10 @@ void ASTMangler::appendExistentialLayout( bool DroppedRequiresClass = false; bool SawRequiresClass = false; for (auto proto : layout.getProtocols()) { + if (auto abiProto = getABIDecl(proto)) { + proto = abiProto; + } + // Skip requirements to conform to an invertible protocols. // We only mangle inverse requirements, but as a constrained existential. if (proto->getInvertibleProtocolKind()) @@ -1553,6 +1630,9 @@ void ASTMangler::appendType(Type type, GenericSignature sig, Decl = typeAlias->getDecl(); else Decl = type->getAnyGeneric(); + if (auto abiDecl = getABIDecl(Decl)) { + Decl = abiDecl; + } if (shouldMangleAsGeneric(type)) { // Try to mangle the entire name as a substitution. if (tryMangleTypeSubstitution(tybase, sig)) @@ -2050,6 +2130,11 @@ void ASTMangler::appendSymbolicExtendedExistentialType( Type type, GenericSignature sig, const ValueDecl *forDecl) { + if (auto abiShapeReferent = getABIDecl(shapeReferent)) { + return appendSymbolicExtendedExistentialType(abiShapeReferent.value(), type, + sig, forDecl); + } + assert(shapeReferent.getKind() == SymbolicReferent::ExtendedExistentialTypeShape); assert(canSymbolicReference(shapeReferent)); @@ -2581,6 +2666,7 @@ void ASTMangler::appendContext(const DeclContext *ctx, void ASTMangler::appendModule(const ModuleDecl *module, StringRef useModuleName) { assert(!module->getParent() && "cannot mangle nested modules!"); + ASSERT(!getABIDecl(module)); // Use the module real name in mangling; this is the physical name // of the module on-disk, which can be different if -module-alias is @@ -2639,6 +2725,10 @@ void ASTMangler::appendProtocolName(const ProtocolDecl *protocol, bool allowStandardSubstitution) { assert(AllowMarkerProtocols || !protocol->isMarkerProtocol()); + if (auto abiProtocol = getABIDecl(protocol)) { + return appendProtocolName(abiProtocol, allowStandardSubstitution); + } + if (allowStandardSubstitution && tryAppendStandardSubstitution(protocol)) return; @@ -2700,6 +2790,10 @@ ASTMangler::getClangDeclForMangling(const ValueDecl *vd) { } void ASTMangler::appendSymbolicReference(SymbolicReferent referent) { + if (auto abiReferent = getABIDecl(referent)) { + return appendSymbolicReference(abiReferent.value()); + } + // Drop in a placeholder. The real reference value has to be filled in during // lowering to IR. auto offset = Buffer.str().size(); @@ -2833,6 +2927,10 @@ void ASTMangler::appendContextualInverses(const GenericTypeDecl *contextDecl, void ASTMangler::appendExtension(const ExtensionDecl* ext, BaseEntitySignature &base, StringRef useModuleName) { + if (auto abiExt = getABIDecl(ext)) { + return appendExtension(abiExt, base, useModuleName); + } + auto decl = ext->getExtendedNominal(); // Recover from erroneous extension. if (!decl) @@ -2883,6 +2981,10 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) { void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl, BaseEntitySignature &base) { + if (auto abiDecl = getABIDecl(decl)) { + return appendAnyGenericType(abiDecl); + } + auto *nominal = dyn_cast(decl); if (nominal && isa(nominal)) @@ -3795,6 +3897,10 @@ ASTMangler::dropProtocolsFromAssociatedTypes(Type type, void ASTMangler::appendAssociatedTypeName(DependentMemberType *dmt, GenericSignature sig) { if (auto assocTy = dmt->getAssocType()) { + if (auto abiAssocTy = getABIDecl(assocTy)) { + assocTy = abiAssocTy; + } + appendIdentifier(assocTy->getName().str()); // If the base type is known to have a single protocol conformance @@ -3901,6 +4007,10 @@ CanType ASTMangler::getDeclTypeForMangling( const ValueDecl *decl, GenericSignature &genericSig, GenericSignature &parentGenericSig) { + if (auto abiDecl = getABIDecl(decl)) { + return getDeclTypeForMangling(abiDecl, genericSig, parentGenericSig); + } + genericSig = GenericSignature(); parentGenericSig = GenericSignature(); @@ -4000,6 +4110,10 @@ bool ASTMangler::tryAppendStandardSubstitution(const GenericTypeDecl *decl) { void ASTMangler::appendConstructorEntity(const ConstructorDecl *ctor, bool isAllocating) { + if (auto abiCtor = getABIDecl(ctor)) { + return appendConstructorEntity(abiCtor, isAllocating); + } + BaseEntitySignature base(ctor); appendContextOf(ctor, base); appendDeclType(ctor, base); @@ -4013,6 +4127,10 @@ void ASTMangler::appendConstructorEntity(const ConstructorDecl *ctor, void ASTMangler::appendDestructorEntity(const DestructorDecl *dtor, DestructorKind kind) { + if (auto abiDtor = getABIDecl(dtor)) { + return appendDestructorEntity(abiDtor, kind); + } + BaseEntitySignature base(dtor); appendContextOf(dtor, base); switch (kind) { @@ -4031,6 +4149,10 @@ void ASTMangler::appendDestructorEntity(const DestructorDecl *dtor, void ASTMangler::appendAccessorEntity(StringRef accessorKindCode, const AbstractStorageDecl *decl, bool isStatic) { + if (auto abiDecl = getABIDecl(decl)) { + return appendAccessorEntity(accessorKindCode, abiDecl, isStatic); + } + BaseEntitySignature base(decl); appendContextOf(decl, base); if (auto *varDecl = dyn_cast(decl)) { @@ -4058,6 +4180,10 @@ void ASTMangler::appendEntity(const ValueDecl *decl, BaseEntitySignature &base, StringRef EntityOp, bool isStatic) { + if (auto abiDecl = getABIDecl(decl)) { + return appendEntity(abiDecl, base, EntityOp, isStatic); + } + appendContextOf(decl, base); appendDeclName(decl); appendDeclType(decl, base); @@ -4069,7 +4195,11 @@ void ASTMangler::appendEntity(const ValueDecl *decl, void ASTMangler::appendEntity(const ValueDecl *decl) { assert(!isa(decl)); assert(!isa(decl)); - + + if (auto abiDecl = getABIDecl(decl)) { + return appendEntity(abiDecl); + } + // Handle accessors specially, they are mangled as modifiers on the accessed // declaration. if (auto accessor = dyn_cast(decl)) { @@ -4424,6 +4554,10 @@ ASTMangler::mangleOpaqueTypeDescriptorRecord(const OpaqueTypeDecl *decl) { void ASTMangler::appendDistributedThunk( const AbstractFunctionDecl *thunk, bool asReference) { + if (auto abiThunk = getABIDecl(thunk)) { + return appendDistributedThunk(abiThunk, asReference); + } + // Marker protocols cannot be checked at runtime, so there is no point // in recording them for distributed thunks. llvm::SaveAndRestore savedAllowMarkerProtocols(AllowMarkerProtocols, @@ -4486,6 +4620,9 @@ void ASTMangler::appendDistributedThunk( if (stubClassLookupResults.size() > 0) { stubActorDecl = dyn_cast_or_null(stubClassLookupResults.front()); + if (auto abiStub = getABIDecl(stubActorDecl)) { + stubActorDecl = abiStub; + } } } @@ -4505,7 +4642,11 @@ void ASTMangler::appendDistributedThunk( "mangled as thunks"); // A distributed getter is mangled as the name of its storage (i.e. "the // var") - appendIdentifier(accessor->getStorage()->getBaseIdentifier().str()); + auto storage = accessor->getStorage(); + if (auto abiStorage = getABIDecl(storage)) { + storage = abiStorage; + } + appendIdentifier(storage->getBaseIdentifier().str()); } else { appendIdentifier(thunk->getBaseIdentifier().str()); } @@ -4812,6 +4953,10 @@ getPrecheckedLocalContextDiscriminator(const Decl *decl, Identifier name) { std::string ASTMangler::mangleAttachedMacroExpansion( const Decl *decl, CustomAttr *attr, MacroRole role) { + if (auto abiDecl = getABIDecl(decl)) { + return mangleAttachedMacroExpansion(decl, attr, role); + } + // FIXME(kavon): using the decl causes a cycle. Is a null base fine? BaseEntitySignature nullBase(nullptr); @@ -4926,6 +5071,7 @@ std::string ASTMangler::mangleAttachedMacroExpansion( static void gatherExistentialRequirements(SmallVectorImpl &reqs, ParameterizedProtocolType *PPT) { auto protoTy = PPT->getBaseType(); + ASSERT(!getABIDecl(protoTy->getDecl()) && "need to figure out behavior"); PPT->getRequirements(protoTy->getDecl()->getSelfInterfaceType(), reqs); } @@ -4946,6 +5092,7 @@ static void extractExistentialInverseRequirements( for (auto ip : PCT->getInverses()) { auto *proto = ctx.getProtocol(getKnownProtocolKind(ip)); assert(proto); + ASSERT(!getABIDecl(proto) && "can't use @abi on inverse protocols"); inverses.push_back({existentialSelf, proto, SourceLoc()}); } } diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 90bfe802a248f..b9f3d692c5a19 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -1230,6 +1230,18 @@ static bool hasLessAccessibleSetter(const AbstractStorageDecl *ASD) { return ASD->getSetterFormalAccess() < ASD->getFormalAccess(); } +bool canPrintSyntheticSILGenName(const Decl *D) { + ASSERT(!D->getAttrs().hasAttribute()); + + // Only print on functions, and only those functions with no barriers to use. + if (auto FD = dyn_cast(D); !FD + || FD->getAttrs().hasAttribute() + || FD->getOpaqueResultTypeDecl()) + return false; + + return true; +} + void PrintAST::printAttributes(const Decl *D) { if (Options.SkipAttributes) return; @@ -1247,6 +1259,27 @@ void PrintAST::printAttributes(const Decl *D) { // for each decl They cannot be shared across different decls. assert(Options.ExcludeCustomAttrList.empty()); + if (Options.PrintSyntheticSILGenName + && !D->getAttrs().hasAttribute()) { + if (canPrintSyntheticSILGenName(D)) { + auto mangledName = Mangle::ASTMangler(D->getASTContext()) + .mangleAnyDecl(cast(D), /*prefix=*/true, + /*respectOriginallyDefinedIn=*/true); + Printer.printAttrName("@_silgen_name"); + Printer << "("; + Printer.printEscapedStringLiteral(mangledName); + Printer << ")\n"; + } + else { + Printer.printAttrName("@available"); + Printer << "(*, unavailable, message: "; + Printer.printEscapedStringLiteral( + "this compiler cannot match the ABI specified by the @abi attribute"); + Printer << ")\n"; + Options.ExcludeAttrList.push_back(DeclAttrKind::Available); + } + } + if (Options.PrintImplicitAttrs) { // Don't print a redundant 'final' if we are printing a 'static' decl. @@ -3139,6 +3172,16 @@ suppressingFeatureCoroutineAccessors(PrintOptions &options, action(); } +static void +suppressingFeatureABIAttribute(PrintOptions &options, + llvm::function_ref action) { + llvm::SaveAndRestore scope(options.PrintSyntheticSILGenName, true); + unsigned originalExcludeAttrCount = options.ExcludeAttrList.size(); + options.ExcludeAttrList.push_back(DeclAttrKind::ABI); + action(); + options.ExcludeAttrList.resize(originalExcludeAttrCount); +} + /// Suppress the printing of a particular feature. static void suppressingFeature(PrintOptions &options, Feature feature, llvm::function_ref action) { @@ -3910,6 +3953,8 @@ void PrintAST::printOneParameter(const ParamDecl *param, void PrintAST::printParameterList(ParameterList *PL, ArrayRef params, bool isAPINameByDefault) { + llvm::SaveAndRestore scope(Options.PrintSyntheticSILGenName, false); + Printer.printStructurePre(PrintStructureKind::FunctionParameterList); SWIFT_DEFER { Printer.printStructurePost(PrintStructureKind::FunctionParameterList); diff --git a/lib/AST/ASTScope.cpp b/lib/AST/ASTScope.cpp index 9a44b6fdaf86b..20d1abde72285 100644 --- a/lib/AST/ASTScope.cpp +++ b/lib/AST/ASTScope.cpp @@ -67,6 +67,11 @@ void ASTScope::lookupEnclosingMacroScope( return ASTScopeImpl::lookupEnclosingMacroScope(sourceFile, loc, body); } +ABIAttr *ASTScope::lookupEnclosingABIAttributeScope( + SourceFile *sourceFile, SourceLoc loc) { + return ASTScopeImpl::lookupEnclosingABIAttributeScope(sourceFile, loc); +} + CatchNode ASTScope::lookupCatchNode(ModuleDecl *module, SourceLoc loc) { return ASTScopeImpl::lookupCatchNode(module, loc); } diff --git a/lib/AST/ASTScopeCreation.cpp b/lib/AST/ASTScopeCreation.cpp index 2adb187178263..2ddca3e9fb2be 100644 --- a/lib/AST/ASTScopeCreation.cpp +++ b/lib/AST/ASTScopeCreation.cpp @@ -484,17 +484,24 @@ class NodeAdder SmallVector localFuncsAndTypes; SmallVector localVars; + auto addDecl = [&](ValueDecl *vd) { + if (isa(vd) || isa(vd)) { + localFuncsAndTypes.push_back(vd); + } else if (auto *var = dyn_cast(vd)) { + localVars.push_back(var); + } + }; + // All types and functions are visible anywhere within a brace statement // scope. When ordering matters (i.e. var decl) we will have split the brace // statement into nested scopes. for (auto braceElement : bs->getElements()) { if (auto localBinding = braceElement.dyn_cast()) { if (auto *vd = dyn_cast(localBinding)) { - if (isa(vd) || isa(vd)) { - localFuncsAndTypes.push_back(vd); - } else if (auto *var = dyn_cast(localBinding)) { - localVars.push_back(var); - } + addDecl(vd); + auto abiRole = ABIRoleInfo(vd); + if (!abiRole.providesABI()) + addDecl(abiRole.getCounterpart()); } } } @@ -616,6 +623,9 @@ void ScopeCreator::addChildrenForKnownAttributes(Decl *decl, if (isa(attr)) relevantAttrs.push_back(attr); + + if (isa(attr)) + relevantAttrs.push_back(attr); } // Decl::getAttrs() is a linked list with head insertion, so the @@ -634,6 +644,9 @@ void ScopeCreator::addChildrenForKnownAttributes(Decl *decl, } else if (auto *customAttr = dyn_cast(attr)) { constructExpandAndInsert( parent, customAttr, decl); + } else if (auto *abiAttr = dyn_cast(attr)) { + constructExpandAndInsert( + parent, abiAttr, decl); } } } @@ -742,6 +755,7 @@ CREATES_NEW_INSERTION_POINT(GenericTypeOrExtensionScope) CREATES_NEW_INSERTION_POINT(BraceStmtScope) CREATES_NEW_INSERTION_POINT(TopLevelCodeScope) CREATES_NEW_INSERTION_POINT(ConditionalClausePatternUseScope) +CREATES_NEW_INSERTION_POINT(ABIAttributeScope) NO_NEW_INSERTION_POINT(FunctionBodyScope) NO_NEW_INSERTION_POINT(AbstractFunctionDeclScope) @@ -972,6 +986,26 @@ TopLevelCodeScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator & "code scope"}; } +AnnotatedInsertionPoint +ABIAttributeScope::expandAScopeThatCreatesANewInsertionPoint( + ScopeCreator &scopeCreator) { + SourceLoc endLoc; + // Get the decl we're attached to. + if (auto parent = getParent().getPtrOrNull()) + // Get the enclosing scope for that decl. + if (auto grandparent = parent->getParent().getPtrOrNull()) + // If we're lexically scoped, that's what defines the end of the child's + // scope. + endLoc = grandparent->getSourceRangeOfThisASTNode().End; + + auto child = scopeCreator.addToScopeTreeAndReturnInsertionPoint(attr->abiDecl, + this, endLoc); + return {child, "@abi attribute can contain scopes which create new insertion " + "points; any such scopes should have the same scope they " + "would have if they were siblings of the decl the attribute " + "is attached to"}; +} + #pragma mark expandAScopeThatDoesNotCreateANewInsertionPoint // Create child scopes for every declaration in a body. diff --git a/lib/AST/ASTScopeLookup.cpp b/lib/AST/ASTScopeLookup.cpp index 10b182a04e25e..ed80e1440e30e 100644 --- a/lib/AST/ASTScopeLookup.cpp +++ b/lib/AST/ASTScopeLookup.cpp @@ -489,8 +489,13 @@ bool ASTScopeImpl::lookupLocalBindingsInPattern(const Pattern *p, return false; bool isDone = false; p->forEachVariable([&](VarDecl *var) { - if (!isDone) - isDone = consumer.consume({var}); + if (!isDone) { + SmallVector vars = { var }; + auto abiRole = ABIRoleInfo(var); + if (!abiRole.providesABI()) + vars.push_back(abiRole.getCounterpart()); + isDone = consumer.consume(vars); + } }); return isDone; } @@ -681,6 +686,23 @@ void ASTScopeImpl::lookupEnclosingMacroScope( } while ((scope = scope->getParent().getPtrOrNull())); } +ABIAttr *ASTScopeImpl:: +lookupEnclosingABIAttributeScope(SourceFile *sourceFile, SourceLoc loc) { + if (!sourceFile || loc.isInvalid()) + return nullptr; + + auto *fileScope = sourceFile->getScope().impl; + auto *scope = fileScope->findInnermostEnclosingScope( + sourceFile->getParentModule(), loc, nullptr); + do { + if (auto abiAttrScope = dyn_cast(scope)) { + return abiAttrScope->attr; + } + } while ((scope = scope->getParent().getPtrOrNull())); + + return nullptr; +} + static std::pair getCatchNodeBody(const ASTScopeImpl *scope, CatchNode node) { const auto &children = scope->getChildren(); diff --git a/lib/AST/ASTScopePrinting.cpp b/lib/AST/ASTScopePrinting.cpp index 14cc533850234..049ed0200c738 100644 --- a/lib/AST/ASTScopePrinting.cpp +++ b/lib/AST/ASTScopePrinting.cpp @@ -40,6 +40,8 @@ using namespace ast_scope; void ASTScopeImpl::dump() const { print(llvm::errs(), 0, false); } +void ASTScopeImpl::dumpParents() const { printParents(llvm::errs()); } + void ASTScopeImpl::dumpOneScopeMapLocation( std::pair lineColumn) { auto bufferID = getSourceFile()->getBufferID(); @@ -124,6 +126,21 @@ void ASTScopeImpl::printRange(llvm::raw_ostream &out) const { printSourceRange(out, range, getSourceManager()); } +void ASTScopeImpl::printParents(llvm::raw_ostream &out) const { + SmallVector nodes; + const ASTScopeImpl *cursor = this; + do { + nodes.push_back(cursor); + cursor = cursor->getParent().getPtrOrNull(); + } while (cursor); + + std::reverse(nodes.begin(), nodes.end()); + + for (auto i : indices(nodes)) { + nodes[i]->print(out, i, /*lastChild=*/true, /*printChildren=*/false); + } +} + #pragma mark printSpecifics diff --git a/lib/AST/ASTScopeSourceRange.cpp b/lib/AST/ASTScopeSourceRange.cpp index 117a6b7bec576..6fad78c64c43e 100644 --- a/lib/AST/ASTScopeSourceRange.cpp +++ b/lib/AST/ASTScopeSourceRange.cpp @@ -353,6 +353,11 @@ SourceRange CustomAttributeScope::getSourceRangeOfThisASTNode( return attr->getRange(); } +SourceRange ABIAttributeScope::getSourceRangeOfThisASTNode( + const bool omitAssertions) const { + return attr->getRange(); +} + SourceRange GuardStmtScope::getSourceRangeOfThisASTNode( const bool omitAssertions) const { return SourceRange(getStmt()->getStartLoc(), endLoc); diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index ced7f38bae67a..0e9530f37fd2b 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1219,9 +1219,34 @@ class Verifier : public ASTWalker { Out << "\n"; abort(); } + if (E->getDecl() && !ABIRoleInfo(E->getDecl()).providesAPI()) { + PrettyStackTraceExpr debugStack(Ctx, "verifying decl reference", E); + Out << "reference to ABI-only decl in user code\n"; + E->dump(Out); + Out << "\n"; + E->getDecl()->dump(Out); + Out << "\n"; + abort(); + } verifyCheckedBase(E); } + void verifyParsed(OverloadedDeclRefExpr *E) { + for (auto D : E->getDecls()) { + if (!ABIRoleInfo(D).providesAPI()) { + PrettyStackTraceExpr debugStack(Ctx, + "verifying overloaded decl reference", E); + Out << "reference to ABI-only decl in user code\n"; + E->dump(Out); + Out << "\n"; + D->dump(Out); + Out << "\n"; + abort(); + } + } + verifyParsedBase(E); + } + void verifyChecked(AssignExpr *S) { Type lhsTy = checkAssignDest(S->getDest()); checkSameType(lhsTy, S->getSrc()->getType(), "assignment operands"); diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 8a0ac68e90068..8cab833be1d82 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -785,7 +785,7 @@ void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options, AttributeVector attributes; AttributeVector modifiers; bool libraryLevelAPI = - D->getASTContext().LangOpts.LibraryLevel == LibraryLevel::API; + D && D->getASTContext().LangOpts.LibraryLevel == LibraryLevel::API; for (auto DA : llvm::reverse(FlattenedAttrs)) { // Don't skip implicit custom attributes. Custom attributes like global @@ -1645,6 +1645,22 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, break; } + case DeclAttrKind::ABI: { + auto *attr = cast(this); + Printer << "@abi("; + Decl *abiDecl = attr->abiDecl; + if (abiDecl && Options.ExplodePatternBindingDecls + && isa(abiDecl) && D && isa(D)) + abiDecl = cast(abiDecl) + ->getVarAtSimilarStructuralPosition( + const_cast(cast(D))); + if (abiDecl) + abiDecl->print(Printer, Options); + Printer << ")"; + + break; + } + #define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS: #include "swift/AST/DeclAttr.def" llvm_unreachable("handled above"); @@ -1684,12 +1700,26 @@ uint64_t DeclAttribute::getOptions(DeclAttrKind DK) { llvm_unreachable("bad DeclAttrKind"); } +std::optional DeclAttribute::getRequiredFeature(DeclAttrKind DK) { + switch (DK) { +#define DECL_ATTR_FEATURE_REQUIREMENT(CLASS, FEATURE_NAME) \ + case DeclAttrKind::CLASS: \ + return Feature::FEATURE_NAME; +#include "swift/AST/DeclAttr.def" + default: + return std::nullopt; + } + llvm_unreachable("bad DeclAttrKind"); +} + StringRef DeclAttribute::getAttrName() const { switch (getKind()) { #define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \ case DeclAttrKind::CLASS: \ return #NAME; #include "swift/AST/DeclAttr.def" + case DeclAttrKind::ABI: + return "abi"; case DeclAttrKind::SILGenName: return "_silgen_name"; case DeclAttrKind::Alignment: @@ -2892,6 +2922,10 @@ static bool hasDeclAttribute(const LangOptions &langOpts, if (DeclAttribute::isConcurrencyOnly(*kind)) return false; + if (auto feature = DeclAttribute::getRequiredFeature(*kind)) + if (!langOpts.hasFeature(*feature)) + return false; + return true; } diff --git a/lib/AST/Bridging/DeclAttributeBridging.cpp b/lib/AST/Bridging/DeclAttributeBridging.cpp index dc80369bc877a..382f5d2835721 100644 --- a/lib/AST/Bridging/DeclAttributeBridging.cpp +++ b/lib/AST/Bridging/DeclAttributeBridging.cpp @@ -93,6 +93,16 @@ static std::optional unbridge(BridgedAccessLevel level) { llvm_unreachable("unhandled BridgedAccessLevel"); } +BridgedABIAttr BridgedABIAttr_createParsed(BridgedASTContext cContext, + BridgedSourceLoc atLoc, + BridgedSourceRange range, + BridgedNullableDecl abiDecl) { + return new (cContext.unbridged()) ABIAttr(abiDecl.unbridged(), + atLoc.unbridged(), + range.unbridged(), + /*isImplicit=*/false); +} + BridgedAccessControlAttr BridgedAccessControlAttr_createParsed(BridgedASTContext cContext, BridgedSourceRange cRange, diff --git a/lib/AST/Bridging/DeclBridging.cpp b/lib/AST/Bridging/DeclBridging.cpp index f1ed286f34b17..3716c01eeddf3 100644 --- a/lib/AST/Bridging/DeclBridging.cpp +++ b/lib/AST/Bridging/DeclBridging.cpp @@ -115,8 +115,9 @@ static AccessorKind unbridged(BridgedAccessorKind kind) { return static_cast(kind); } -void BridgedDecl_setAttrs(BridgedDecl decl, BridgedDeclAttributes attrs) { - decl.unbridged()->getAttrs() = attrs.unbridged(); +void BridgedDecl_attachParsedAttrs(BridgedDecl decl, + BridgedDeclAttributes attrs) { + decl.unbridged()->attachParsedAttrs(attrs.unbridged()); } BridgedAccessorDecl BridgedAccessorDecl_createParsed( @@ -146,7 +147,7 @@ BridgedPatternBindingDecl BridgedPatternBindingDecl_createParsed( // Configure all vars. pattern->forEachVariable([&](VarDecl *VD) { - VD->getAttrs() = cAttrs.unbridged(); + VD->attachParsedAttrs(cAttrs.unbridged()); VD->setStatic(isStatic); VD->setIntroducer(introducer); }); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 336cfd745cc5a..2c07a93177885 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -35,6 +35,7 @@ #include "swift/AST/ImportCache.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" +#include "swift/AST/LookupKinds.h" #include "swift/AST/MacroDefinition.h" #include "swift/AST/MacroDiscriminatorContext.h" #include "swift/AST/Module.h" @@ -407,6 +408,19 @@ DeclAttributes Decl::getSemanticAttrs() const { return getAttrs(); } +void Decl::attachParsedAttrs(DeclAttributes attrs) { + ASSERT(getAttrs().isEmpty() && "attaching when there are already attrs?"); + + for (auto *attr : attrs.getAttributes()) + attr->setOriginalDeclaration(this); + for (auto *attr : attrs.getAttributes()) + attr->setOriginalDeclaration(this); + for (auto attr : attrs.getAttributes()) + recordABIAttr(attr); + + getAttrs() = attrs; +} + void Decl::visitAuxiliaryDecls( AuxiliaryDeclCallback callback, bool visitFreestandingExpanded @@ -4257,6 +4271,120 @@ void ValueDecl::setRenamedDecl(const AvailableAttr *attr, std::move(renameDecl)); } +void Decl::recordABIAttr(ABIAttr *attr) { + Decl *owner = this; + + // The ABIAttr on a VarDecl ought to point to its PBD. + if (auto VD = dyn_cast(owner)) { + if (auto PBD = VD->getParentPatternBinding()) { + owner = PBD; + } + } + + auto record = [&](Decl *decl) { + auto &evaluator = owner->getASTContext().evaluator; + DeclABIRoleInfoRequest(decl).recordABIOnly(evaluator, owner); + }; + + if (auto abiPBD = dyn_cast(attr->abiDecl)) { + // Add to *every* VarDecl in the ABI PBD, even ones that don't properly + // match anything in the API PBD. + for (auto i : range(abiPBD->getNumPatternEntries())) { + abiPBD->getPattern(i)->forEachVariable(record); + } + return; + } + + record(attr->abiDecl); +} + +void DeclABIRoleInfoRequest::recordABIOnly(Evaluator &evaluator, + Decl *counterpart) { + if (evaluator.hasCachedResult(*this)) + return; + DeclABIRoleInfoResult result{counterpart, ABIRole::ProvidesABI}; + evaluator.cacheOutput(*this, std::move(result)); +} + +DeclABIRoleInfoResult +DeclABIRoleInfoRequest::evaluate(Evaluator &evaluator, Decl *decl) const { + // NOTE: ABI decl -> API decl is manually cached through `recordABIOnly()`, + // so this code does not have to handle that case. + + ASSERT(decl); + + Decl *counterpartDecl = decl; + ABIRole::Value flags = ABIRole::Either; + + if (auto attr = decl->getAttrs().getAttribute()) { + flags = ABIRole::ProvidesAPI; + counterpartDecl = attr->abiDecl; + } + + return DeclABIRoleInfoResult(counterpartDecl, (uint8_t)flags); +} + +abi_role_detail::Storage abi_role_detail::computeStorage(Decl *decl) { + ASSERT(decl); + + auto &ctx = decl->getASTContext(); + + auto result = evaluateOrDefault(ctx.evaluator, DeclABIRoleInfoRequest{decl}, + { decl, ABIRole::Either }); + Decl *counterpartDecl = result.storage.getPointer(); + auto flags = (ABIRole::Value)result.storage.getInt(); + + // If we did find an `@abi` attribute, resolve PBD pointers to their VarDecl. + if (flags != ABIRole::Either) { + if (auto VD = dyn_cast(decl)) + if (auto PBD = dyn_cast_or_null(counterpartDecl)) + counterpartDecl = PBD->getVarAtSimilarStructuralPosition(VD); + } + + return Storage(counterpartDecl, flags); +} + +ABIRole::ABIRole(NLOptions opts) + : value(opts & NL_ABIProviding ? ProvidesABI : ProvidesAPI) +{ } + +VarDecl *PatternBindingDecl:: +getVarAtSimilarStructuralPosition(VarDecl *otherVar) { + auto otherPBD = otherVar->getParentPatternBinding(); + + if (!otherPBD) + return getSingleVar(); + if (otherPBD == this) + return otherVar; + + // Find the entry index and sibling index for `otherVar` within its PBD. + auto entryIndex = otherPBD->getPatternEntryIndexForVarDecl(otherVar); + + SmallVector otherSiblings; + otherPBD->getPattern(entryIndex)->collectVariables(otherSiblings); + size_t siblingIndex = + llvm::find(otherSiblings, otherVar) - otherSiblings.begin(); + + ASSERT(siblingIndex != otherSiblings.size() + && "otherVar not in its own pattern?"); + + // Look up the same entry index and sibling index in this PBD, returning null + // if that index doesn't exist. + + // Corresponding PBD has more patterns in it + if (entryIndex >= getNumPatternEntries()) + return nullptr; + + SmallVector siblings; + getPattern(entryIndex)->collectVariables(siblings); + + // Corresponding pattern has more vars in it + if (siblingIndex >= siblings.size()) + return nullptr; + + return siblings[siblingIndex]; +} + SourceLoc Decl::getAttributeInsertionLoc(bool forModifier) const { // Some decls have a parent/child split where the introducer keyword is on the // parent, but the attributes are on the children. If this is a child in such diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index cd9150a10cca1..98a2044ec7a41 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -323,6 +323,20 @@ static bool usesFeatureAllowUnsafeAttribute(Decl *decl) { return decl->getAttrs().hasAttribute(); } +static ABIAttr *getABIAttr(Decl *decl) { + if (auto pbd = dyn_cast(decl)) + for (auto i : range(pbd->getNumPatternEntries())) + if (auto anchorVar = pbd->getAnchoringVarDecl(i)) + return getABIAttr(anchorVar); + // FIXME: EnumCaseDecl/EnumElementDecl + + return decl->getAttrs().getAttribute(); +} + +static bool usesFeatureABIAttribute(Decl *decl) { + return getABIAttr(decl) != nullptr; +} + UNINTERESTING_FEATURE(WarnUnsafe) UNINTERESTING_FEATURE(SafeInterop) UNINTERESTING_FEATURE(SafeInteropWrappers) @@ -425,26 +439,38 @@ static bool allowFeatureSuppression(StringRef featureName, Decl *decl) { /// Go through all the features used by the given declaration and /// either add or remove them to this set. void FeatureSet::collectFeaturesUsed(Decl *decl, InsertOrRemove operation) { + // Count feature usage in an ABI decl as feature usage by the API, not itself, + // since we can't use `#if` inside an @abi attribute. + Decl *abiDecl = nullptr; + if (auto abiAttr = getABIAttr(decl)) { + abiDecl = abiAttr->abiDecl; + } + +#define CHECK(Function) (Function(decl) || (abiDecl && Function(abiDecl))) +#define CHECK_ARG(Function, Arg) (Function(Arg, decl) || (abiDecl && Function(Arg, abiDecl))) + // Go through each of the features, checking whether the // declaration uses that feature. #define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \ - if (usesFeature##FeatureName(decl)) \ + if (CHECK(usesFeature##FeatureName)) \ collectRequiredFeature(Feature::FeatureName, operation); #define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \ - if (usesFeature##FeatureName(decl)) { \ - if (disallowFeatureSuppression(#FeatureName, decl)) \ + if (CHECK(usesFeature##FeatureName)) { \ + if (CHECK_ARG(disallowFeatureSuppression, #FeatureName)) \ collectRequiredFeature(Feature::FeatureName, operation); \ else \ collectSuppressibleFeature(Feature::FeatureName, operation); \ } #define CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \ - if (usesFeature##FeatureName(decl)) { \ - if (allowFeatureSuppression(#FeatureName, decl)) \ + if (CHECK(usesFeature##FeatureName)) { \ + if (CHECK_ARG(allowFeatureSuppression, #FeatureName)) \ collectSuppressibleFeature(Feature::FeatureName, operation); \ else \ collectRequiredFeature(Feature::FeatureName, operation); \ } #include "swift/Basic/Features.def" +#undef CHECK +#undef CHECK_ARG } FeatureSet swift::getUniqueFeaturesUsed(Decl *decl) { diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index e0a49b57916d4..ce05eb6511026 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -150,6 +150,11 @@ class swift::SourceLookupCache { void add(ValueDecl *VD) { if (!VD->hasName()) return; VD->getName().addToLookupTable(Members, VD); + + auto abiRole = ABIRoleInfo(VD); + if (!abiRole.providesABI() && abiRole.getCounterpart()) + abiRole.getCounterpart()->getName() + .addToLookupTable(Members, abiRole.getCounterpart()); } void clear() { @@ -546,7 +551,8 @@ void SourceLookupCache::lookupValue(DeclName Name, NLKind LookupKind, if (I != TopLevelValues.end()) { Result.reserve(I->second.size()); for (ValueDecl *Elt : I->second) - Result.push_back(Elt); + if (ABIRoleInfo(Elt).matchesOptions(Flags)) + Result.push_back(Elt); } // If we aren't supposed to find names introduced by macros, we're done. @@ -639,7 +645,8 @@ void SourceLookupCache::lookupVisibleDecls(ImportPath::Access AccessPath, if (I == TopLevelValues.end()) return; for (auto vd : I->second) - Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel); + if (ABIRoleInfo(vd).matchesOptions(OptionSet())) // FIXME: figure this out + Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel); return; } @@ -649,7 +656,8 @@ void SourceLookupCache::lookupVisibleDecls(ImportPath::Access AccessPath, // entry for the simple name so that we report each declaration once. if (tlv.first.isSimpleName() && !vd->getName().isSimpleName()) continue; - Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel); + if (ABIRoleInfo(vd).matchesOptions(OptionSet())) // FIXME: figure this out + Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel); } } @@ -674,7 +682,8 @@ void SourceLookupCache::lookupVisibleDecls(ImportPath::Access AccessPath, }); } for (auto *vd : macroExpandedDecls) { - Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel); + if (ABIRoleInfo(vd).matchesOptions(OptionSet())) // FIXME: figure this out + Consumer.foundDecl(vd, DeclVisibilityKind::VisibleAtTopLevel); } } @@ -692,8 +701,9 @@ void SourceLookupCache::lookupClassMembers(ImportPath::Access accessPath, for (ValueDecl *vd : member.second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); if (nominal && nominal->getName() == accessPath.front().Item) - consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, - DynamicLookupInfo::AnyObject); + if (ABIRoleInfo(vd).matchesOptions(OptionSet())) // FIXME: figure this out + consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, + DynamicLookupInfo::AnyObject); } } return; @@ -706,8 +716,9 @@ void SourceLookupCache::lookupClassMembers(ImportPath::Access accessPath, continue; for (ValueDecl *vd : member.second) - consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, - DynamicLookupInfo::AnyObject); + if (ABIRoleInfo(vd).matchesOptions(OptionSet())) // FIXME: figure this out + consumer.foundDecl(vd, DeclVisibilityKind::DynamicLookup, + DynamicLookupInfo::AnyObject); } } @@ -724,7 +735,8 @@ void SourceLookupCache::lookupClassMember(ImportPath::Access accessPath, for (ValueDecl *vd : iter->second) { auto *nominal = vd->getDeclContext()->getSelfNominalTypeDecl(); if (nominal && nominal->getName() == accessPath.front().Item) - results.push_back(vd); + if (ABIRoleInfo(vd).matchesOptions(OptionSet())) // FIXME: figure this out + results.push_back(vd); } return; } @@ -3848,7 +3860,7 @@ void SynthesizedFileUnit::lookupValue( SmallVectorImpl &result) const { for (auto *decl : TopLevelDecls) { if (auto VD = dyn_cast(decl)) { - if (VD->getName().matchesRef(name)) { + if (VD->getName().matchesRef(name) && ABIRoleInfo(VD).matchesOptions(Flags)) { result.push_back(VD); } } diff --git a/lib/AST/ModuleNameLookup.cpp b/lib/AST/ModuleNameLookup.cpp index 3379f1188e6ca..288625bb5ab20 100644 --- a/lib/AST/ModuleNameLookup.cpp +++ b/lib/AST/ModuleNameLookup.cpp @@ -27,7 +27,9 @@ namespace { /// Encapsulates the work done for a recursive qualified lookup into a module. /// /// The \p LookupStrategy handles the non-recursive part of the lookup. It -/// must be a subclass of ModuleNameLookup. +/// must be a subclass of ModuleNameLookup. It should provide a +/// \c doLocalLookup() method; this method will be passed appropriate +/// \c NLOptions but does not necessarily need to honor them. template class ModuleNameLookup { ASTContext &ctx; @@ -183,6 +185,8 @@ void ModuleNameLookup::lookupInModule( if (respectAccessControl && !declIsVisibleToNameLookup(VD, moduleScopeContext, options)) return true; + if (!ABIRoleInfo(VD).matchesOptions(options)) + return true; return false; }); decls.erase(newEnd, decls.end()); @@ -193,6 +197,8 @@ void ModuleNameLookup::lookupInModule( OptionSet currentModuleLookupFlags = {}; if (options & NL_ExcludeMacroExpansions) currentModuleLookupFlags |= ModuleLookupFlags::ExcludeMacroExpansions; + if (options & NL_ABIProviding) + currentModuleLookupFlags |= ModuleLookupFlags::ABIProviding; // Do the lookup into the current module. auto *module = moduleOrFile->getParentModule(); @@ -217,6 +223,10 @@ void ModuleNameLookup::lookupInModule( if (!canReturnEarly) { auto &imports = ctx.getImportCache().getImportSet(moduleOrFile); + OptionSet importedModuleLookupFlags = {}; + if (options & NL_ABIProviding) + currentModuleLookupFlags |= ModuleLookupFlags::ABIProviding; + auto visitImport = [&](ImportedModule import, const DeclContext *moduleScopeContext) { if (import.accessPath.empty()) @@ -226,7 +236,7 @@ void ModuleNameLookup::lookupInModule( return; getDerived()->doLocalLookup(import.importedModule, import.accessPath, - { }, decls); + importedModuleLookupFlags, decls); updateNewDecls(moduleScopeContext); }; diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 3bfcf8f7f6e83..051187d8e8e4e 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -118,6 +118,9 @@ void swift::simple_display(llvm::raw_ostream &out, {UnqualifiedLookupFlags::TypeLookup, "TypeLookup"}, {UnqualifiedLookupFlags::MacroLookup, "MacroLookup"}, {UnqualifiedLookupFlags::ModuleLookup, "ModuleLookup"}, + {UnqualifiedLookupFlags::DisregardSelfBounds, "DisregardSelfBounds"}, + {UnqualifiedLookupFlags::IgnoreMissingImports, "IgnoreMissingImports"}, + {UnqualifiedLookupFlags::ABIProviding, "ABIProviding"}, }; auto flagsToPrint = llvm::make_filter_range( @@ -296,6 +299,10 @@ void LookupResultEntry::print(llvm::raw_ostream& out) const { out << "\n"; } else out << "\n(no-base)\n"; + + auto abiRole = ABIRoleInfo(getValueDecl()); + out << "provides API=" << abiRole.providesAPI() + << ", provides ABI=" << abiRole.providesABI() << "\n"; } @@ -1510,6 +1517,10 @@ void MemberLookupTable::addMember(Decl *member) { // And if given a synonym, under that name too. if (A) A->getMemberName().addToLookupTable(Lookup, vd); + + auto abiRole = ABIRoleInfo(vd); + if (!abiRole.providesABI()) + addMember(abiRole.getCounterpart()); } void MemberLookupTable::addMembers(DeclRange members) { @@ -2062,29 +2073,34 @@ void NominalTypeDecl::prepareLookupTable() { LookupTable.setInt(true); } -static TinyPtrVector -maybeFilterOutUnwantedDecls(TinyPtrVector decls, - DeclName name, - bool includeAttrImplements, - bool excludeMacroExpansions) { - if (includeAttrImplements && !excludeMacroExpansions) - return decls; +static TinyPtrVector maybeFilterOutUnwantedDecls( + TinyPtrVector decls, + DeclName name, + OptionSet flags) { + using Flags = NominalTypeDecl::LookupDirectFlags; + TinyPtrVector result; for (auto V : decls) { // If we're supposed to exclude anything that comes from a macro expansion, // check whether the source location of the declaration is in a macro // expansion, and skip this declaration if it does. - if (excludeMacroExpansions) { + if (flags.contains(Flags::ExcludeMacroExpansions)) { auto sourceFile = V->getModuleContext()->getSourceFileContainingLocation(V->getLoc()); if (sourceFile && sourceFile->Kind == SourceFileKind::MacroExpansion) continue; } + // If this decl is on either side of an @abi attribute, make sure it's on + // the side we want. + if (!ABIRoleInfo(V).matchesOptions(flags)) + continue; + // Filter-out any decl that doesn't have the name we're looking for // (asserting as a consistency-check that such entries all have // @_implements attrs for the name!) - if (V->getName().matchesRef(name)) { + if (flags.contains(Flags::IncludeAttrImplements) + || V->getName().matchesRef(name)) { result.push_back(V); } else { auto A = V->getAttrs().getAttribute(); @@ -2110,8 +2126,6 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, auto *decl = desc.DC; ASTContext &ctx = decl->getASTContext(); - const bool includeAttrImplements = - flags.contains(NominalTypeDecl::LookupDirectFlags::IncludeAttrImplements); const bool excludeMacroExpansions = flags.contains(NominalTypeDecl::LookupDirectFlags::ExcludeMacroExpansions); @@ -2147,9 +2161,8 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, if (!allFound.empty()) { auto known = Table.find(name); if (known != Table.end()) { - auto swiftLookupResult = maybeFilterOutUnwantedDecls( - known->second, name, includeAttrImplements, - excludeMacroExpansions); + auto swiftLookupResult = + maybeFilterOutUnwantedDecls(known->second, name, flags); for (auto foundSwiftDecl : swiftLookupResult) { allFound.push_back(foundSwiftDecl); } @@ -2195,9 +2208,7 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, } // We found something; return it. - return maybeFilterOutUnwantedDecls(known->second, name, - includeAttrImplements, - excludeMacroExpansions); + return maybeFilterOutUnwantedDecls(known->second, name, flags); } bool NominalTypeDecl::createObjCMethodLookup() { @@ -2377,9 +2388,26 @@ static bool isAcceptableLookupResult(const DeclContext *dc, return false; } + // Check that it has the appropriate ABI role. + if (!ABIRoleInfo(decl).matchesOptions(options)) + return false; + return true; } +bool namelookup::isInABIAttr(SourceFile *sourceFile, SourceLoc loc) { + // Make sure that the source location is actually within the given source + // file. + if (sourceFile && loc.isValid()) { + sourceFile = + sourceFile->getParentModule()->getSourceFileContainingLocation(loc); + if (!sourceFile) + return false; + } + + return ASTScope::lookupEnclosingABIAttributeScope(sourceFile, loc) != nullptr; +} + void namelookup::pruneLookupResultSet(const DeclContext *dc, NLOptions options, SmallVectorImpl &decls) { // If we're supposed to remove overridden declarations, do so now. @@ -2609,6 +2637,8 @@ QualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC, flags |= NominalTypeDecl::LookupDirectFlags::IncludeAttrImplements; if (options & NL_ExcludeMacroExpansions) flags |= NominalTypeDecl::LookupDirectFlags::ExcludeMacroExpansions; + if (options & NL_ABIProviding) + flags |= NominalTypeDecl::LookupDirectFlags::ABIProviding; // Note that the source loc argument doesn't matter, because excluding // macro expansions is already propagated through the lookup flags above. @@ -2751,7 +2781,8 @@ AnyObjectLookupRequest::evaluate(Evaluator &evaluator, const DeclContext *dc, QualifiedLookupResult decls; // Type-only and macro lookup won't find anything on AnyObject. - if (options & (NL_OnlyTypes | NL_OnlyMacros)) + // AnyObject doesn't provide ABI. + if (options & (NL_OnlyTypes | NL_OnlyMacros | NL_ABIProviding)) return decls; // Collect all of the visible declarations. @@ -2945,6 +2976,11 @@ static DirectlyReferencedTypeDecls directReferencesForUnqualifiedTypeLookup( if (namelookup::isInMacroArgument(dc->getParentSourceFile(), loc)) options |= UnqualifiedLookupFlags::ExcludeMacroExpansions; + // If the source location is located anywhere within an `@abi` attribute, look + // up using ABI names. + if (namelookup::isInABIAttr(dc->getParentSourceFile(), loc)) + options |= UnqualifiedLookupFlags::ABIProviding; + // In a protocol or protocol extension, the 'where' clause can refer to // associated types without 'Self' qualification: // @@ -4131,6 +4167,7 @@ void swift::simple_display(llvm::raw_ostream &out, NLOptions options) { FLAG(NL_ExcludeMacroExpansions) FLAG(NL_OnlyMacros) FLAG(NL_IgnoreMissingImports) + FLAG(NL_ABIProviding) #undef FLAG }; diff --git a/lib/AST/NameLookupRequests.cpp b/lib/AST/NameLookupRequests.cpp index cd3dd1af60019..ca9d9ec3f9e5f 100644 --- a/lib/AST/NameLookupRequests.cpp +++ b/lib/AST/NameLookupRequests.cpp @@ -583,60 +583,57 @@ swift::extractNearestSourceLoc(CustomRefCountingOperationDescriptor desc) { // so it doesn't break caching for those immediate requests. /// Exclude macros in the unqualified lookup descriptor if we need to. -static UnqualifiedLookupDescriptor excludeMacrosIfNeeded( +static UnqualifiedLookupDescriptor contextualizeOptions( UnqualifiedLookupDescriptor descriptor) { - if (descriptor.Options.contains( - UnqualifiedLookupFlags::ExcludeMacroExpansions)) - return descriptor; + if (!descriptor.Options.contains( + UnqualifiedLookupFlags::ExcludeMacroExpansions) + && namelookup::isInMacroArgument( + descriptor.DC->getParentSourceFile(), descriptor.Loc)) + descriptor.Options |= UnqualifiedLookupFlags::ExcludeMacroExpansions; + if (!descriptor.Options.contains(UnqualifiedLookupFlags::ABIProviding) + && namelookup::isInABIAttr( + descriptor.DC->getParentSourceFile(), descriptor.Loc)) + descriptor.Options |= UnqualifiedLookupFlags::ABIProviding; - auto isInMacroArgument = namelookup::isInMacroArgument( - descriptor.DC->getParentSourceFile(), descriptor.Loc); - - if (!isInMacroArgument) - return descriptor; - - descriptor.Options |= UnqualifiedLookupFlags::ExcludeMacroExpansions; return descriptor; } /// Exclude macros in the direct lookup descriptor if we need to. -static DirectLookupDescriptor excludeMacrosIfNeeded( +static DirectLookupDescriptor contextualizeOptions( DirectLookupDescriptor descriptor, SourceLoc loc) { - if (descriptor.Options.contains( - NominalTypeDecl::LookupDirectFlags::ExcludeMacroExpansions)) - return descriptor; - - auto isInMacroArgument = namelookup::isInMacroArgument( - descriptor.DC->getParentSourceFile(), loc); - - if (!isInMacroArgument) - return descriptor; - - descriptor.Options |= - NominalTypeDecl::LookupDirectFlags::ExcludeMacroExpansions; + if (!descriptor.Options.contains( + NominalTypeDecl::LookupDirectFlags::ExcludeMacroExpansions) + && namelookup::isInMacroArgument( + descriptor.DC->getParentSourceFile(), loc)) + descriptor.Options |= + NominalTypeDecl::LookupDirectFlags::ExcludeMacroExpansions; + if (!descriptor.Options.contains( + NominalTypeDecl::LookupDirectFlags::ABIProviding) + && namelookup::isInABIAttr( + descriptor.DC->getParentSourceFile(), loc)) + descriptor.Options |= + NominalTypeDecl::LookupDirectFlags::ABIProviding; return descriptor; } /// Exclude macros in the name lookup options if we need to. static NLOptions -excludeMacrosIfNeeded(const DeclContext *dc, SourceLoc loc, - NLOptions options) { - if (options & NL_ExcludeMacroExpansions) - return options; - - auto isInMacroArgument = namelookup::isInMacroArgument( - dc->getParentSourceFile(), loc); - - if (!isInMacroArgument) - return options; +contextualizeOptions(const DeclContext *dc, SourceLoc loc, + NLOptions options) { + if (!(options & NL_ExcludeMacroExpansions) + && namelookup::isInMacroArgument(dc->getParentSourceFile(), loc)) + options |= NL_ExcludeMacroExpansions; + if (!(options & NL_ABIProviding) + && namelookup::isInABIAttr(dc->getParentSourceFile(), loc)) + options |= NL_ABIProviding; - return options | NL_ExcludeMacroExpansions; + return options; } UnqualifiedLookupRequest::UnqualifiedLookupRequest( UnqualifiedLookupDescriptor descriptor -) : SimpleRequest(excludeMacrosIfNeeded(descriptor)) { } +) : SimpleRequest(contextualizeOptions(descriptor)) { } LookupInModuleRequest::LookupInModuleRequest( const DeclContext *moduleOrFile, DeclName name, NLKind lookupKind, @@ -645,13 +642,13 @@ LookupInModuleRequest::LookupInModuleRequest( SourceLoc loc, NLOptions options ) : SimpleRequest(moduleOrFile, name, lookupKind, resolutionKind, moduleScopeContext, - excludeMacrosIfNeeded(moduleOrFile, loc, options)) { } + contextualizeOptions(moduleOrFile, loc, options)) { } ModuleQualifiedLookupRequest::ModuleQualifiedLookupRequest( const DeclContext *dc, ModuleDecl *module, DeclNameRef name, SourceLoc loc, NLOptions options ) : SimpleRequest(dc, module, name, - excludeMacrosIfNeeded(dc, loc, options)) { } + contextualizeOptions(dc, loc, options)) { } QualifiedLookupRequest::QualifiedLookupRequest( const DeclContext *dc, @@ -659,10 +656,10 @@ QualifiedLookupRequest::QualifiedLookupRequest( DeclNameRef name, SourceLoc loc, NLOptions options ) : SimpleRequest(dc, std::move(decls), name, - excludeMacrosIfNeeded(dc, loc, options)) { } + contextualizeOptions(dc, loc, options)) { } DirectLookupRequest::DirectLookupRequest(DirectLookupDescriptor descriptor, SourceLoc loc) - : SimpleRequest(excludeMacrosIfNeeded(descriptor, loc)) { } + : SimpleRequest(contextualizeOptions(descriptor, loc)) { } // Implement the clang importer type zone. #define SWIFT_TYPEID_ZONE ClangImporter diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 85b5bbf59684e..6daacec7252fc 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -481,6 +481,8 @@ void UnqualifiedLookupFactory::addImportedResults(const DeclContext *const dc) { nlOptions |= NL_IncludeUsableFromInline; if (options.contains(Flags::ExcludeMacroExpansions)) nlOptions |= NL_ExcludeMacroExpansions; + if (options.contains(Flags::ABIProviding)) + nlOptions |= NL_ABIProviding; lookupInModule(dc, Name.getFullName(), CurModuleResults, NLKind::UnqualifiedLookup, resolutionKind, dc, Loc, nlOptions); @@ -595,6 +597,8 @@ NLOptions UnqualifiedLookupFactory::computeBaseNLOptions( baseNLOptions |= NL_IgnoreAccessControl; if (options.contains(Flags::IgnoreMissingImports)) baseNLOptions |= NL_IgnoreMissingImports; + if (options.contains(Flags::ABIProviding)) + baseNLOptions |= NL_ABIProviding; return baseNLOptions; } @@ -701,9 +705,12 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( continue; } + if (!ABIRoleInfo(value).matchesOptions(factory.options)) + continue; + factory.Results.push_back(LookupResultEntry(value)); #ifndef NDEBUG - factory.stopForDebuggingIfAddingTargetLookupResult(factory.Results.back()); + factory.addedResult(factory.Results.back()); #endif } factory.recordCompletionOfAScope(); @@ -716,10 +723,14 @@ bool ASTScopeDeclConsumerForUnqualifiedLookup::consumePossiblyNotInScope( return false; for (auto *var : vars) { - if (!factory.Name.getFullName().isSimpleName(var->getName())) + if (!factory.Name.getFullName().isSimpleName(var->getName()) + || !ABIRoleInfo(var).matchesOptions(factory.options)) continue; factory.Results.push_back(LookupResultEntry(var)); +#ifndef NDEBUG + factory.addedResult(factory.Results.back()); +#endif } return false; @@ -872,14 +883,19 @@ class ASTScopeDeclConsumerForLocalLookup : public AbstractASTScopeDeclConsumer { DeclName name; bool stopAfterInnermostBraceStmt; + ABIRole roleFilter; SmallVectorImpl &results; public: ASTScopeDeclConsumerForLocalLookup( DeclName name, bool stopAfterInnermostBraceStmt, - SmallVectorImpl &results) + ABIRole roleFilter, SmallVectorImpl &results) : name(name), stopAfterInnermostBraceStmt(stopAfterInnermostBraceStmt), - results(results) {} + roleFilter(roleFilter), results(results) {} + + bool hasCorrectABIRole(ValueDecl *vd) const { + return ABIRoleInfo(vd).matches(roleFilter); + } bool consume(ArrayRef values, NullablePtr baseDC) override { @@ -888,14 +904,16 @@ class ASTScopeDeclConsumerForLocalLookup if (auto *varDecl = dyn_cast(value)) { // Check if the name matches any auxiliary decls not in the AST varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) { - if (name.isSimpleName(auxiliaryVar->getName())) { + if (name.isSimpleName(auxiliaryVar->getName()) + && hasCorrectABIRole(auxiliaryVar)) { results.push_back(auxiliaryVar); foundMatch = true; } }); } - if (!foundMatch && value->getName().matchesRef(name)) + if (!foundMatch && value->getName().matchesRef(name) + && hasCorrectABIRole(value)) results.push_back(value); } @@ -923,9 +941,10 @@ class ASTScopeDeclConsumerForLocalLookup /// interface type computation. void ASTScope::lookupLocalDecls(SourceFile *sf, DeclName name, SourceLoc loc, bool stopAfterInnermostBraceStmt, + ABIRole roleFilter, SmallVectorImpl &results) { ASTScopeDeclConsumerForLocalLookup consumer(name, stopAfterInnermostBraceStmt, - results); + roleFilter, results); ASTScope::unqualifiedLookup(sf, loc, consumer); } diff --git a/lib/ASTGen/Sources/ASTGen/Bridge.swift b/lib/ASTGen/Sources/ASTGen/Bridge.swift index 5124be615e2ac..89b1a3654983c 100644 --- a/lib/ASTGen/Sources/ASTGen/Bridge.swift +++ b/lib/ASTGen/Sources/ASTGen/Bridge.swift @@ -26,6 +26,7 @@ extension BridgedNullable { extension BridgedSourceLoc: /*@retroactive*/ swiftASTGen.BridgedNullable {} extension BridgedIdentifier: /*@retroactive*/ swiftASTGen.BridgedNullable {} +extension BridgedNullableDecl: /*@retroactive*/ swiftASTGen.BridgedNullable {} extension BridgedNullableExpr: /*@retroactive*/ swiftASTGen.BridgedNullable {} extension BridgedNullableStmt: /*@retroactive*/ swiftASTGen.BridgedNullable {} extension BridgedNullableTypeRepr: /*@retroactive*/ swiftASTGen.BridgedNullable {} @@ -60,6 +61,9 @@ extension Optional where Wrapped: BridgedHasNullable { extension BridgedStmt: BridgedHasNullable { typealias Nullable = BridgedNullableStmt } +extension BridgedDecl: BridgedHasNullable { + typealias Nullable = BridgedNullableDecl +} extension BridgedExpr: BridgedHasNullable { typealias Nullable = BridgedNullableExpr } diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 17e165e15a2a0..90b5b8d0282b5 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -111,6 +111,8 @@ extension ASTGenVisitor { let attrName = identTy.name.rawText let attrKind = BridgedDeclAttrKind(from: attrName.bridged) switch attrKind { + case .ABI: + return handle(self.generateABIAttr(attribute: node)?.asDeclAttribute) case .alignment: return handle(self.generateAlignmentAttr(attribute: node)?.asDeclAttribute) case .allowFeatureSuppression: @@ -328,6 +330,52 @@ extension ASTGenVisitor { return handle(self.generateCustomAttr(attribute: node)?.asDeclAttribute) } + /// E.g.: + /// ``` + /// @abi(func fn()) + /// ``` + func generateABIAttr(attribute node: AttributeSyntax) -> BridgedABIAttr? { + guard + let arg = node.arguments?.as(ABIAttributeArgumentsSyntax.self) + else { + // TODO: diagnose + return nil + } + + let abiDecl: BridgedDecl? + switch arg.provider { + case .associatedType(let assocTyDecl): + abiDecl = self.generate(associatedTypeDecl: assocTyDecl)?.asDecl + case .deinitializer(let deinitDecl): + abiDecl = self.generate(deinitializerDecl: deinitDecl).asDecl + case .enumCase(let caseDecl): + abiDecl = self.generate(enumCaseDecl: caseDecl).asDecl + case .function(let funcDecl): + abiDecl = self.generate(functionDecl: funcDecl)?.asDecl + case .initializer(let initDecl): + abiDecl = self.generate(initializerDecl: initDecl).asDecl + case .`subscript`(let subscriptDecl): + abiDecl = self.generate(subscriptDecl: subscriptDecl).asDecl + case .typeAlias(let typealiasDecl): + abiDecl = self.generate(typeAliasDecl: typealiasDecl)?.asDecl + case .variable(let varDecl): + abiDecl = self.generate(variableDecl: varDecl).asDecl + case .missing(_): + // This error condition will have been diagnosed in SwiftSyntax. + abiDecl = nil + } + + // TODO: Diagnose if `abiDecl` has a body/initial value/etc. + // The C++ parser considers it syntactically invalid but SwiftSyntax does not. + + return .createParsed( + self.ctx, + atLoc: self.generateSourceLoc(node.atSign), + range: self.generateAttrSourceRange(node), + abiDecl: abiDecl.asNullable + ) + } + /// E.g.: /// ``` /// @_alignment(8) diff --git a/lib/ASTGen/Sources/ASTGen/Decls.swift b/lib/ASTGen/Sources/ASTGen/Decls.swift index 19abd40645b06..166648045c774 100644 --- a/lib/ASTGen/Sources/ASTGen/Decls.swift +++ b/lib/ASTGen/Sources/ASTGen/Decls.swift @@ -105,7 +105,7 @@ extension ASTGenVisitor { underlyingType: self.generate(type: node.initializer.value), genericWhereClause: self.generate(genericWhereClause: node.genericWhereClause) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) return decl } @@ -129,7 +129,7 @@ extension ASTGenVisitor { end: node.memberBlock.rightBrace ) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) let members = self.withDeclContext(decl.asDeclContext) { self.generate(memberBlockItemList: node.memberBlock.members) @@ -163,7 +163,7 @@ extension ASTGenVisitor { end: node.memberBlock.rightBrace ) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) let members = self.withDeclContext(decl.asDeclContext) { self.generate(memberBlockItemList: node.memberBlock.members) @@ -198,7 +198,7 @@ extension ASTGenVisitor { ), isActor: false ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) let members = self.withDeclContext(decl.asDeclContext) { self.generate(memberBlockItemList: node.memberBlock.members) @@ -233,7 +233,7 @@ extension ASTGenVisitor { ), isActor: true ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) let members = self.withDeclContext(decl.asDeclContext) { self.generate(memberBlockItemList: node.memberBlock.members) @@ -270,7 +270,7 @@ extension ASTGenVisitor { end: node.memberBlock.rightBrace ) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) let members = self.withDeclContext(decl.asDeclContext) { self.generate(memberBlockItemList: node.memberBlock.members) @@ -300,7 +300,7 @@ extension ASTGenVisitor { defaultType: self.generate(type: node.initializer?.value), genericWhereClause: self.generate(genericWhereClause: node.genericWhereClause) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) return decl } } @@ -322,7 +322,7 @@ extension ASTGenVisitor { end: node.memberBlock.rightBrace ) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) let members = self.withDeclContext(decl.asDeclContext) { self.generate(memberBlockItemList: node.memberBlock.members) @@ -364,7 +364,7 @@ extension ASTGenVisitor { guard let elemDecl = self.generate(enumCaseElement: elem) else { return nil } - elemDecl.asDecl.setAttrs(attrs.attributes) + elemDecl.asDecl.attachParsedAttrs(attrs.attributes) return elemDecl }) return .createParsed( @@ -439,7 +439,7 @@ extension ASTGenVisitor { throwsSpecifierLoc: self.generateSourceLoc(node.effectSpecifiers?.throwsClause), thrownType: self.generate(type: node.effectSpecifiers?.thrownError) ) - accessor.asDecl.setAttrs(attrs) + accessor.asDecl.attachParsedAttrs(attrs) if let body = node.body { self.withDeclContext(accessor.asDeclContext) { accessor.setParsedBody(self.generate(codeBlock: body)) @@ -600,7 +600,7 @@ extension ASTGenVisitor { arrowLoc: self.generateSourceLoc(node.returnClause.arrow), returnType: self.generate(type: node.returnClause.type) ) - subscriptDecl.asDecl.setAttrs(attrs.attributes) + subscriptDecl.asDecl.attachParsedAttrs(attrs.attributes) if let accessors = node.accessorBlock { let storage = subscriptDecl.asAbstractStorageDecl @@ -635,7 +635,7 @@ extension ASTGenVisitor { returnType: self.generate(type: node.signature.returnClause?.type), genericWhereClause: self.generate(genericWhereClause: node.genericWhereClause) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) if let body = node.body { self.withDeclContext(decl.asDeclContext) { @@ -662,7 +662,7 @@ extension ASTGenVisitor { thrownType: self.generate(type: node.signature.effectSpecifiers?.thrownError), genericWhereClause: self.generate(genericWhereClause: node.genericWhereClause) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) if let body = node.body { self.withDeclContext(decl.asDeclContext) { @@ -681,7 +681,7 @@ extension ASTGenVisitor { declContext: self.declContext, deinitKeywordLoc: self.generateSourceLoc(node.deinitKeyword) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) if let body = node.body { self.withDeclContext(decl.asDeclContext) { @@ -711,7 +711,7 @@ extension ASTGenVisitor { resultType: self.generate(type: node.signature.returnClause?.type), definition: self.generate(expr: node.definition?.value) ) - decl.asDecl.setAttrs(attrs.attributes); + decl.asDecl.attachParsedAttrs(attrs.attributes) return decl; } } @@ -733,7 +733,7 @@ extension ASTGenVisitor { rightAngleLoc: info.rightAngleLoc, args: info.arguments ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) return decl } @@ -890,7 +890,7 @@ extension ASTGenVisitor { lowerThanNames: self.generate(precedenceGroupNameList: body.lowerThanRelation?.precedenceGroups), rightBraceLoc: self.generateSourceLoc(node.rightBrace) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) return decl } } @@ -937,7 +937,7 @@ extension ASTGenVisitor { self.generateLocatedIdentifier($0.name) }.bridgedArray(in: self) ) - decl.asDecl.setAttrs(attrs.attributes) + decl.asDecl.attachParsedAttrs(attrs.attributes) return decl } } diff --git a/lib/ASTGen/Sources/ASTGen/SourceFile.swift b/lib/ASTGen/Sources/ASTGen/SourceFile.swift index ba0036fa2b2d0..4529723a1518f 100644 --- a/lib/ASTGen/Sources/ASTGen/SourceFile.swift +++ b/lib/ASTGen/Sources/ASTGen/SourceFile.swift @@ -76,6 +76,7 @@ extension Parser.ExperimentalFeatures { mapFeature(.TrailingComma, to: .trailingComma) mapFeature(.CoroutineAccessors, to: .coroutineAccessors) mapFeature(.ValueGenerics, to: .valueGenerics) + mapFeature(.ABIAttribute, to: .abiAttribute) } } diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 4796757a03a41..40f7be7116df7 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -554,6 +554,8 @@ namespace { if (auto wrapped = pointeeType->wrapInPointer(pointerKind)) { return {wrapped, ImportHint::OtherPointer}; } else { + addImportDiagnostic(Diagnostic(diag::bridged_pointer_type_not_found, + pointerKind)); return Type(); } } @@ -611,8 +613,11 @@ namespace { pointerKind = PTK_UnsafeMutablePointer; } - return {pointeeType->wrapInPointer(pointerKind), - ImportHint::None}; + auto pointerType = pointeeType->wrapInPointer(pointerKind); + if (!pointerType) + addImportDiagnostic(Diagnostic(diag::bridged_pointer_type_not_found, + pointerKind)); + return {pointerType, ImportHint::None}; } ImportResult VisitMemberPointer(const clang::MemberPointerType *type) { @@ -1414,7 +1419,8 @@ static Type maybeImportNSErrorOutParameter(ClangImporter::Implementation &impl, static Type maybeImportCFOutParameter(ClangImporter::Implementation &impl, Type importedType, - ImportTypeAttrs attrs) { + ImportTypeAttrs attrs, + llvm::function_ref addImportDiagnostic) { PointerTypeKind PTK; auto elementType = importedType->getAnyPointerElementType(PTK); if (!elementType || PTK != PTK_UnsafeMutablePointer) @@ -1446,6 +1452,9 @@ static Type maybeImportCFOutParameter(ClangImporter::Implementation &impl, pointerKind = PTK_AutoreleasingUnsafeMutablePointer; resultTy = resultTy->wrapInPointer(pointerKind); + if (!resultTy) + addImportDiagnostic(Diagnostic(diag::bridged_pointer_type_not_found, + pointerKind)); return resultTy; } @@ -1483,7 +1492,12 @@ static ImportedType adjustTypeForConcreteImport( importKind != ImportTypeKind::Result) { return {Type(), false}; } - importedType = impl.getNamedSwiftType(impl.getStdlibModule(), "Void"); + importedType = impl.SwiftContext.getVoidType(); + if (!importedType) { + addImportDiagnostic(Diagnostic(diag::bridged_type_not_found_in_module, + "Void", "Swift")); + return {Type(), false}; + } break; case ImportHint::ObjCBridged: @@ -1599,7 +1613,8 @@ static ImportedType adjustTypeForConcreteImport( if (attrs.contains(ImportTypeAttr::CFRetainedOutParameter) || attrs.contains(ImportTypeAttr::CFUnretainedOutParameter)) { if (Type outParamTy = - maybeImportCFOutParameter(impl, importedType, attrs)) { + maybeImportCFOutParameter(impl, importedType, attrs, + addImportDiagnostic)) { importedType = outParamTy; break; } @@ -2220,8 +2235,15 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType( op == clang::OverloadedOperatorKind::OO_MinusEqual || op == clang::OverloadedOperatorKind::OO_StarEqual || op == clang::OverloadedOperatorKind::OO_SlashEqual) && - clangDecl->getReturnType()->isReferenceType()) - return {SwiftContext.getVoidType(), false}; + clangDecl->getReturnType()->isReferenceType()) { + auto voidTy = SwiftContext.getVoidType(); + if (!voidTy) + addImportDiagnostic(clangDecl, + Diagnostic(diag::bridged_type_not_found_in_module, + "Void", "Swift"), + clangDecl->getLocation()); + return {voidTy, false}; + } // Fix up optionality. OptionalTypeKind OptionalityOfReturn; @@ -2388,7 +2410,10 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( auto genericType = findGenericTypeInGenericDecls(*this, templateParamType, genericParams, getImportTypeAttrs(clangDecl), addDiag); - importedType = {genericType->wrapInPointer(pointerKind), false}; + auto genericPointerType = genericType->wrapInPointer(pointerKind); + if (!genericPointerType) + addDiag(Diagnostic(diag::bridged_pointer_type_not_found, pointerKind)); + importedType = {genericPointerType, false}; } else if (!(isa(returnType) || isa(returnType)) || // TODO: we currently don't lazily load operator return types, but @@ -2472,8 +2497,11 @@ ClangImporter::Implementation::importParameterType( auto genericType = findGenericTypeInGenericDecls( *this, templateParamType, genericParams, attrs, addImportDiagnosticFn); swiftParamTy = genericType->wrapInPointer(pointerKind); - if (!swiftParamTy) + if (!swiftParamTy) { + addImportDiagnosticFn(Diagnostic(diag::bridged_pointer_type_not_found, + pointerKind)); return std::nullopt; + } } else if (isa(paramTy) && isa(paramTy->getPointeeType())) { // We don't support universal reference, bail. @@ -3515,6 +3543,11 @@ ImportedType ClangImporter::Implementation::importAccessorParamsAndReturnType( *params = ParameterList::create(SwiftContext, paramInfo); resultTy = SwiftContext.getVoidType(); + if (!resultTy) + addImportDiagnostic(clangDecl, + Diagnostic(diag::bridged_type_not_found_in_module, + "Void", "Swift"), + clangDecl->getLocation()); isIUO = false; } diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 81dd43885a5fe..d5934f15bd8b1 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -686,7 +686,7 @@ static void addKeyword(CodeCompletionResultSink &Sink, StringRef Name, } static void addDeclKeywords(CodeCompletionResultSink &Sink, DeclContext *DC, - bool IsConcurrencyEnabled) { + const LangOptions &langOpts) { auto isTypeDeclIntroducer = [](CodeCompletionKeywordKind Kind, std::optional DAK) -> bool { switch (Kind) { @@ -799,10 +799,16 @@ static void addDeclKeywords(CodeCompletionResultSink &Sink, DeclContext *DC, return; // Remove keywords only available when concurrency is enabled. - if (DAK.has_value() && !IsConcurrencyEnabled && + if (DAK.has_value() && !langOpts.EnableExperimentalConcurrency && DeclAttribute::isConcurrencyOnly(*DAK)) return; + // Remove experimental keywords. + if (DAK.has_value()) + if (auto feature = DeclAttribute::getRequiredFeature(*DAK)) + if (!langOpts.hasFeature(*feature)) + return; + CodeCompletionFlair flair = getFlair(Kind, DAK); // Special case for 'actor'. Get the same flair with 'kw_class'. @@ -1019,8 +1025,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, LLVM_FALLTHROUGH; } case CompletionKind::StmtOrExpr: - addDeclKeywords(Sink, CurDeclContext, - Context.LangOpts.EnableExperimentalConcurrency); + addDeclKeywords(Sink, CurDeclContext, Context.LangOpts); addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody); addClosureSignatureKeywordsIfApplicable(Sink, CurDeclContext); @@ -1122,8 +1127,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, .Default(false); }) != ParsedKeywords.end(); if (!HasDeclIntroducer) { - addDeclKeywords(Sink, CurDeclContext, - Context.LangOpts.EnableExperimentalConcurrency); + addDeclKeywords(Sink, CurDeclContext, Context.LangOpts); addLetVarKeywords(Sink); } break; diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index b9f11ee573621..a6e0d6726fdf9 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -3023,7 +3023,7 @@ void CompletionLookup::getGenericRequirementCompletions( } bool CompletionLookup::canUseAttributeOnDecl(DeclAttrKind DAK, bool IsInSil, - bool IsConcurrencyEnabled, + const LangOptions &langOpts, std::optional DK, StringRef Name) { if (DeclAttribute::isUserInaccessible(DAK)) @@ -3034,8 +3034,12 @@ bool CompletionLookup::canUseAttributeOnDecl(DeclAttrKind DAK, bool IsInSil, return false; if (!IsInSil && DeclAttribute::isSilOnly(DAK)) return false; - if (!IsConcurrencyEnabled && DeclAttribute::isConcurrencyOnly(DAK)) + if (!langOpts.EnableExperimentalConcurrency + && DeclAttribute::isConcurrencyOnly(DAK)) return false; + if (auto feature = DeclAttribute::getRequiredFeature(DAK)) + if (!langOpts.hasFeature(*feature)) + return false; if (!DK.has_value()) return true; // Hide underscored attributes even if they are not marked as user @@ -3059,11 +3063,10 @@ void CompletionLookup::getAttributeDeclCompletions(bool IsInSil, #include "swift/AST/DeclNodes.def" } } - bool IsConcurrencyEnabled = Ctx.LangOpts.EnableExperimentalConcurrency; std::string Description = TargetName.str() + " Attribute"; #define DECL_ATTR_ALIAS(KEYWORD, NAME) DECL_ATTR(KEYWORD, NAME, 0, 0) #define DECL_ATTR(KEYWORD, NAME, ...) \ - if (canUseAttributeOnDecl(DeclAttrKind::NAME, IsInSil, IsConcurrencyEnabled, \ + if (canUseAttributeOnDecl(DeclAttrKind::NAME, IsInSil, Ctx.LangOpts, \ DK, #KEYWORD)) \ addDeclAttrKeyword(#KEYWORD, Description); #include "swift/AST/DeclAttr.def" diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 323eeaef50401..e7f4fdf13e5e4 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3490,6 +3490,56 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, } break; } + + case DeclAttrKind::ABI: { + SourceLoc lParenLoc = Tok.getLoc(); + if (!consumeIfAttributeLParen()) { + diagnose(lParenLoc, diag::attr_expected_lparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return makeParserSuccess(); + } + + Decl *abiDecl = nullptr; + Status |= parseDecl(/*atStart=*/true, /*ifConfigInAttr=*/true, [&](Decl *D){ + // Look through a TopLevelCodeDecl. + // FIXME: Do we need to reparent these decls to match their counterparts? + // Feels like there might be more to do here. + if (auto tlcd = dyn_cast(D)) { + ASSERT(tlcd->getBody()->getNumElements() == 1 + && "TopLevelCodeDecl with != 1 element?"); + D = tlcd->getBody()->getElements().front().dyn_cast(); + if (!D) + return; + } + // TODO: MacroExpansionDecl? + + // When parsing a compound decl, like a PBD, we'll get called for both the + // enclosing decl and the ones inside it. We only want the enclosing decl, + // which will always come first. + if (!abiDecl) + abiDecl = D; + }, /*stubOnly=*/true); + ASSERT(!isa_and_nonnull(abiDecl) + && "should have gotten PBD, not VarDecl"); + + SourceLoc rParenLoc; + if (parseMatchingToken(tok::r_paren, rParenLoc, + DiagRef(diag::attr_expected_rparen, + { AttrName, + DeclAttribute::isDeclModifier(DK) }), + lParenLoc)) { + return makeParserSuccess(); + } + + if (abiDecl) { + Attributes.add(new (Context) ABIAttr(abiDecl, + AtLoc, { Loc, rParenLoc }, + /*implicit=*/false)); + } + + break; + } + case DeclAttrKind::Available: { if (!consumeIfAttributeLParen()) { diagnose(Loc, diag::attr_expected_lparen, AttrName, @@ -6030,20 +6080,6 @@ void Parser::consumeDecl(ParserPosition BeginParserPosition, bool IsTopLevel) { } } -/// Set the original declaration in `@differentiable` attributes. -/// -/// Necessary because `Parser::parseNewDeclAttribute` (which calls -/// `Parser::parseDifferentiableAttribute`) does not have access to the -/// parent declaration of parsed attributes. -static void -setOriginalDeclarationForDifferentiableAttributes(DeclAttributes attrs, - Decl *D) { - for (auto *attr : attrs.getAttributes()) - const_cast(attr)->setOriginalDeclaration(D); - for (auto *attr : attrs.getAttributes()) - const_cast(attr)->setOriginalDeclaration(D); -} - /// Determine the declaration parsing options to use when parsing the decl in /// the given context. static Parser::ParseDeclOptions getParseDeclOptions(DeclContext *DC) { @@ -6098,8 +6134,12 @@ static Parser::ParseDeclOptions getParseDeclOptions(DeclContext *DC) { /// \endverbatim ParserStatus Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi, bool IfConfigsAreDeclAttrs, - llvm::function_ref Handler) { + llvm::function_ref Handler, + bool stubOnly) { ParseDeclOptions Flags = getParseDeclOptions(CurDeclContext); + if (stubOnly) { + Flags |= PD_StubOnly; + } ParserPosition BeginParserPosition; if (isIDEInspectionFirstPass()) BeginParserPosition = getParserPosition(); @@ -6469,7 +6509,6 @@ ParserStatus Parser::parseDecl(bool IsAtStartOfLineOrPreviousHadSemi, Decl *D = DeclResult.get(); if (!HandlerAlreadyCalled) Handler(D); - setOriginalDeclarationForDifferentiableAttributes(D->getAttrs(), D); } if (!DeclResult.isParseErrorOrHasCompletion()) { @@ -6665,7 +6704,7 @@ ParserResult Parser::parseDeclImport(ParseDeclOptions Flags, auto *ID = ImportDecl::create(Context, CurDeclContext, ImportLoc, Kind, KindLoc, importPath.get()); - ID->getAttrs() = Attributes; + ID->attachParsedAttrs(Attributes); return DCC.fixupParserResult(ID); } @@ -6904,7 +6943,18 @@ ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi, bool Parser::parseMemberDeclList(SourceLoc &LBLoc, SourceLoc &RBLoc, Diag<> LBraceDiag, Diag<> RBraceDiag, - IterableDeclContext *IDC) { + IterableDeclContext *IDC, + ParseDeclOptions Flags) { + if (Flags.contains(PD_StubOnly) && Tok.isNot(tok::l_brace)) { + // Hey, look, it really is a stub! + LBLoc = RBLoc = SourceLoc(); + + // Cache the empty result to prevent delayed parsing. + Context.evaluator.cacheOutput(ParseMembersRequest{IDC}, + FingerprintAndMembers{std::nullopt, {}}); + return false; + } + if (parseToken(tok::l_brace, LBLoc, LBraceDiag)) { LBLoc = RBLoc = PreviousLoc; @@ -6925,7 +6975,8 @@ bool Parser::parseMemberDeclList(SourceLoc &LBLoc, SourceLoc &RBLoc, if (canDelayMemberDeclParsing(HasOperatorDeclarations, HasNestedClassDeclarations, - HasDerivativeDeclarations)) { + HasDerivativeDeclarations, + Flags)) { if (HasOperatorDeclarations) IDC->setMaybeHasOperatorDeclarations(); if (HasNestedClassDeclarations) @@ -6945,6 +6996,19 @@ bool Parser::parseMemberDeclList(SourceLoc &LBLoc, SourceLoc &RBLoc, IDC->setMaybeHasOperatorDeclarations(); IDC->setMaybeHasNestedClassDeclarations(); IDC->setMaybeHasDerivativeDeclarations(); + + if (Flags.contains(PD_StubOnly)) { + diagnose(LBLoc, diag::stub_decl_cannot_have_body, + IDC->getDecl()->getDescriptiveKind()) + .fixItRemove({LBLoc, RBLoc}); + + // Cache the empty result to prevent delayed parsing. + Context.evaluator.cacheOutput(ParseMembersRequest{IDC}, + FingerprintAndMembers{std::nullopt, {}}); + + return true; + } + Context.evaluator.cacheOutput( ParseMembersRequest{IDC}, FingerprintAndMembers{ @@ -7006,9 +7070,10 @@ Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, bool Parser::canDelayMemberDeclParsing(bool &HasOperatorDeclarations, bool &HasNestedClassDeclarations, - bool &HasDerivativeDeclarations) { + bool &HasDerivativeDeclarations, + ParseDeclOptions Flags) { // If explicitly disabled, respect the flag. - if (!isDelayedParsingEnabled()) + if (!isDelayedParsingEnabled() || Flags.contains(PD_StubOnly)) return false; // Skip until the matching right curly bracket; if we find a pound directive, @@ -7109,7 +7174,7 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { Context.AllocateCopy(Inherited), CurDeclContext, trailingWhereClause); - ext->getAttrs() = Attributes; + ext->attachParsedAttrs(Attributes); if (trailingWhereHadCodeCompletion && CodeCompletionCallbacks) CodeCompletionCallbacks->setParsedDecl(ext); @@ -7121,7 +7186,7 @@ Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) { if (parseMemberDeclList(LBLoc, RBLoc, diag::expected_lbrace_extension, diag::expected_rbrace_extension, - ext)) + ext, Flags)) status.setIsParseError(); // Don't propagate the code completion bit from members: we cannot help @@ -7444,7 +7509,7 @@ parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) { } TAD->setUnderlyingTypeRepr(UnderlyingTy.getPtrOrNull()); - TAD->getAttrs() = Attributes; + TAD->attachParsedAttrs(Attributes); // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { @@ -7561,7 +7626,7 @@ ParserResult Parser::parseDeclAssociatedType(Parser::ParseDeclOptions auto assocType = AssociatedTypeDecl::createParsed( Context, CurDeclContext, AssociatedTypeLoc, Id, IdLoc, UnderlyingTy.getPtrOrNull(), TrailingWhere); - assocType->getAttrs() = Attributes; + assocType->attachParsedAttrs(Attributes); if (!Inherited.empty()) assocType->setInherited(Context.AllocateCopy(Inherited)); return makeParserResult(Status, assocType); @@ -7617,9 +7682,11 @@ static ParameterList *parseOptionalAccessorArgument(SourceLoc SpecifierLoc, return ParameterList::create(P.Context, StartLoc, param, EndLoc); } -bool Parser::canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations) { +bool Parser::canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations, + ParseDeclOptions Flags) { // If explicitly disabled, respect the flag. - if (!isDelayedParsingEnabled() && !isIDEInspectionFirstPass()) + if (!isDelayedParsingEnabled() && !isIDEInspectionFirstPass() && + !Flags.contains(PD_StubOnly)) return false; // Skip until the matching right curly bracket; If it has a potential regex @@ -7916,7 +7983,7 @@ bool Parser::parseAccessorAfterIntroducer( auto *accessor = AccessorDecl::createParsed( Context, Kind, storage, /*declLoc*/ Loc, /*accessorKeywordLoc*/ Loc, param, asyncLoc, throwsLoc, thrownTy, CurDeclContext); - accessor->getAttrs() = Attributes; + accessor->attachParsedAttrs(Attributes); // Collect this accessor and detect conflicts. if (auto existingAccessor = accessors.add(accessor)) { @@ -7943,8 +8010,8 @@ bool Parser::parseAccessorAfterIntroducer( // It's okay not to have a body if there's an external asm name. if (!Tok.is(tok::l_brace)) { - // Accessors don't need bodies in module interfaces - if (SF.Kind == SourceFileKind::Interface) + // Accessors don't need bodies in module interfaces or @abi attrs + if (SF.Kind == SourceFileKind::Interface || Flags.contains(PD_StubOnly)) return false; // _silgen_name'd accessors don't need bodies. @@ -7958,7 +8025,7 @@ bool Parser::parseAccessorAfterIntroducer( return false; } - parseAbstractFunctionBody(accessor); + parseAbstractFunctionBody(accessor, Flags); return false; } @@ -7984,7 +8051,7 @@ ParserStatus Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices, /*asyncLoc*/ SourceLoc(), /*throwsLoc*/ SourceLoc(), /*thrownTy*/ nullptr, CurDeclContext); accessors.add(getter); - parseAbstractFunctionBody(getter); + parseAbstractFunctionBody(getter, Flags); accessors.RBLoc = getter->getEndLoc(); }; @@ -8195,7 +8262,7 @@ void Parser::parseExpandedAttributeList(SmallVectorImpl &items, // macro will attach the attribute list to. MissingDecl *missing = MissingDecl::create(Context, CurDeclContext, Tok.getLoc()); - missing->getAttrs() = attributes; + missing->attachParsedAttrs(attributes); items.push_back(ASTNode(missing)); return; @@ -8323,12 +8390,15 @@ Parser::parseDeclVarGetSet(PatternBindingEntry &entry, ParseDeclOptions Flags, Invalid = true; } - accessors.record(*this, PrimaryVar, Invalid); + // Reject accessors when we're parsing stubs only. + if (Flags.contains(PD_StubOnly) && !accessors.Accessors.empty()) { + diagnose(Tok, diag::stub_decl_cannot_have_body, + PrimaryVar->getDescriptiveKind()) + .fixItRemove({ accessors.LBLoc, accessors.RBLoc }); + Invalid = true; + } - // Set original declaration in `@differentiable` attributes. - for (auto *accessor : accessors.Accessors) - setOriginalDeclarationForDifferentiableAttributes(accessor->getAttrs(), - accessor); + accessors.record(*this, PrimaryVar, Invalid); return makeParserResult(AccessorStatus, PrimaryVar); } @@ -8588,6 +8658,8 @@ Parser::parseDeclVar(ParseDeclOptions Flags, return makeParserResult(Status, PBD); }; bool HasNext; + bool DisallowInit = + Flags.contains(PD_DisallowInit) || Flags.contains(PD_StubOnly); do { Pattern *pattern; { @@ -8619,12 +8691,9 @@ Parser::parseDeclVar(ParseDeclOptions Flags, // Configure all vars with attributes, 'static' and parent pattern. pattern->forEachVariable([&](VarDecl *VD) { VD->setStatic(StaticLoc.isValid()); - VD->getAttrs() = Attributes; + VD->attachParsedAttrs(Attributes); VD->setTopLevelGlobal(topLevelDecl); - // Set original declaration in `@differentiable` attributes. - setOriginalDeclarationForDifferentiableAttributes(Attributes, VD); - Decls.push_back(VD); if (hasOpaqueReturnTy && sf && !InInactiveClauseEnvironment && !InFreestandingMacroArgument) { @@ -8668,7 +8737,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, // If this Pattern binding was not supposed to have an initializer, but it // did, diagnose this and remove it. - if (Flags & PD_DisallowInit && init.isNonNull()) { + if (DisallowInit && init.isNonNull()) { diagnose(EqualLoc, diag::disallowed_init); init = nullptr; } @@ -8678,7 +8747,7 @@ Parser::parseDeclVar(ParseDeclOptions Flags, // that downstream clients know that it was present (well, at least the = // was present). This silences downstream diagnostics checking to make // sure that some PBD's that require initializers actually had them. - if (!(Flags & PD_DisallowInit) && init.isNull()) + if (!DisallowInit && init.isNull()) init = makeParserResult(init, new (Context) ErrorExpr(EqualLoc)); @@ -8944,7 +9013,7 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, diagnoseOperatorFixityAttributes(*this, Attributes, FD); // Add the attributes here so if we need them while parsing the body // they are available. - FD->getAttrs() = Attributes; + FD->attachParsedAttrs(Attributes); // Pass the function signature to code completion. if (Status.hasCodeCompletion()) { @@ -8960,7 +9029,7 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, skipSingle(); } } else if (!Status.hasCodeCompletion()) { - parseAbstractFunctionBody(FD); + parseAbstractFunctionBody(FD, Flags); } return DCC.fixupParserResult(FD); @@ -9016,7 +9085,8 @@ Parser::parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD) { } /// Parse function body into \p AFD or skip it for delayed parsing. -void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { +void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD, + ParseDeclOptions Flags) { if (!Tok.is(tok::l_brace)) { checkForInputIncomplete(); return; @@ -9049,7 +9119,9 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { }; bool HasNestedTypeDeclarations; - if (canDelayFunctionBodyParsing(HasNestedTypeDeclarations)) { + if (canDelayFunctionBodyParsing(HasNestedTypeDeclarations, Flags)) { + ASSERT(!Flags.contains(PD_StubOnly) + && "stub-only should parse body immediately"); BodyRange.End = PreviousLoc; assert(SourceMgr.isBeforeInBuffer(BodyRange.Start, BodyRange.End) || @@ -9066,6 +9138,13 @@ void Parser::parseAbstractFunctionBody(AbstractFunctionDecl *AFD) { (void)parseAbstractFunctionBodyImpl(AFD); BodyRange = AFD->getBodySourceRange(); setIDEInspectionDelayedDeclStateIfNeeded(); + + if (Flags.contains(PD_StubOnly)) { + diagnose(BodyRange.Start, diag::stub_decl_cannot_have_body, + AFD->getDescriptiveKind()) + .fixItRemove(BodyRange); + AFD->setBody(nullptr, AbstractFunctionDecl::BodyKind::None); + } } BodyAndFingerprint @@ -9135,7 +9214,7 @@ ParserResult Parser::parseDeclEnum(ParseDeclOptions Flags, EnumDecl *ED = new (Context) EnumDecl(EnumLoc, EnumName, EnumNameLoc, { }, GenericParams, CurDeclContext); - ED->getAttrs() = Attributes; + ED->attachParsedAttrs(Attributes); ContextChange CC(*this, ED); @@ -9165,7 +9244,7 @@ ParserResult Parser::parseDeclEnum(ParseDeclOptions Flags, if (parseMemberDeclList(LBLoc, RBLoc, diag::expected_lbrace_enum, diag::expected_rbrace_enum, - ED)) + ED, Flags)) Status.setIsParseError(); } @@ -9330,7 +9409,7 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags, result->setImplicit(); // Parse error } - result->getAttrs() = Attributes; + result->attachParsedAttrs(Attributes); Elements.push_back(result); // Continue through the comma-separated list. @@ -9397,7 +9476,7 @@ ParserResult Parser::parseDeclStruct(ParseDeclOptions Flags, { }, GenericParams, CurDeclContext); - SD->getAttrs() = Attributes; + SD->attachParsedAttrs(Attributes); ContextChange CC(*this, SD); @@ -9429,7 +9508,7 @@ ParserResult Parser::parseDeclStruct(ParseDeclOptions Flags, if (parseMemberDeclList(LBLoc, RBLoc, diag::expected_lbrace_struct, diag::expected_rbrace_struct, - SD)) + SD, Flags)) Status.setIsParseError(); } @@ -9486,7 +9565,7 @@ ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, ClassDecl *CD = new (Context) ClassDecl(ClassLoc, ClassName, ClassNameLoc, { }, GenericParams, CurDeclContext, isExplicitActorDecl); - CD->getAttrs() = Attributes; + CD->attachParsedAttrs(Attributes); // Parsed classes never have missing vtable entries. CD->setHasMissingVTableEntries(false); @@ -9545,7 +9624,7 @@ ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, : diag::expected_lbrace_class, isExplicitActorDecl ? diag::expected_rbrace_actor : diag::expected_rbrace_class, - CD)) + CD, Flags)) Status.setIsParseError(); } @@ -9665,7 +9744,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) { Context.AllocateCopy(PrimaryAssociatedTypeNames), Context.AllocateCopy(InheritedProtocols), TrailingWhere); - Proto->getAttrs() = Attributes; + Proto->attachParsedAttrs(Attributes); if (whereClauseHadCodeCompletion && CodeCompletionCallbacks) CodeCompletionCallbacks->setParsedDecl(Proto); @@ -9680,7 +9759,7 @@ parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) { if (parseMemberDeclList(LBraceLoc, RBraceLoc, diag::expected_lbrace_protocol, diag::expected_rbrace_protocol, - Proto)) + Proto, Flags)) Status.setIsParseError(); } @@ -9792,7 +9871,7 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, auto *const Subscript = SubscriptDecl::createParsed( Context, StaticLoc, StaticSpelling, SubscriptLoc, Indices.get(), ArrowLoc, ElementTy.get(), CurDeclContext, GenericParams); - Subscript->getAttrs() = Attributes; + Subscript->attachParsedAttrs(Attributes); // Let the source file track the opaque return type mapping, if any. if (ElementTy.get() && ElementTy.get()->hasOpaque() && @@ -9853,11 +9932,6 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, accessors.record(*this, Subscript, (Invalid || !Status.isSuccess() || Status.hasCodeCompletion())); - // Set original declaration in `@differentiable` attributes. - for (auto *accessor : accessors.Accessors) - setOriginalDeclarationForDifferentiableAttributes(accessor->getAttrs(), - accessor); - // No need to setLocalDiscriminator because subscripts cannot // validly appear outside of type decls. return makeParserResult(Status, Subscript); @@ -9967,7 +10041,7 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { thrownTy, BodyParams, GenericParams, CurDeclContext, FuncRetTy); CD->setImplicitlyUnwrappedOptional(IUO); - CD->getAttrs() = Attributes; + CD->attachParsedAttrs(Attributes); // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { @@ -10001,7 +10075,7 @@ Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) { skipSingle(); } } else if(!Status.hasCodeCompletion()) { - parseAbstractFunctionBody(CD); + parseAbstractFunctionBody(CD, Flags); } return makeParserResult(Status, CD); @@ -10054,9 +10128,9 @@ parseDeclDeinit(ParseDeclOptions Flags, DeclAttributes &Attributes) { } auto *DD = new (Context) DestructorDecl(DestructorLoc, CurDeclContext); - parseAbstractFunctionBody(DD); + parseAbstractFunctionBody(DD, Flags); - DD->getAttrs() = Attributes; + DD->attachParsedAttrs(Attributes); // Reject 'destructor' functions outside of structs, enums, classes, or // extensions that provide objc implementations. @@ -10263,7 +10337,7 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, diagnoseOperatorFixityAttributes(*this, Attributes, res); - res->getAttrs() = Attributes; + res->attachParsedAttrs(Attributes); return makeParserResult(res); } @@ -10320,7 +10394,7 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags, higherThanKeywordLoc, higherThan, lowerThanKeywordLoc, lowerThan, rbraceLoc); - result->getAttrs() = attributes; + result->attachParsedAttrs(attributes); return result; }; auto createInvalid = [&](bool hasCodeCompletion) { @@ -10575,7 +10649,7 @@ ParserResult Parser::parseDeclMacro(DeclAttributes &attributes) { auto *macro = new (Context) MacroDecl( macroLoc, macroFullName, macroNameLoc, genericParams, parameterList, arrowLoc, resultType, definition, CurDeclContext); - macro->getAttrs() = attributes; + macro->attachParsedAttrs(attributes); // Parse a 'where' clause if present. if (Tok.is(tok::kw_where)) { @@ -10611,7 +10685,7 @@ Parser::parseDeclMacroExpansion(ParseDeclOptions flags, auto *med = MacroExpansionDecl::create( CurDeclContext, poundLoc, macroNameRef, macroNameLoc, leftAngleLoc, Context.AllocateCopy(genericArgs), rightAngleLoc, argList); - med->getAttrs() = attributes; + med->attachParsedAttrs(attributes); return makeParserResult(status, med); } diff --git a/lib/Parse/ParseGeneric.cpp b/lib/Parse/ParseGeneric.cpp index 818e3bab7f79e..265ba7cec0c82 100644 --- a/lib/Parse/ParseGeneric.cpp +++ b/lib/Parse/ParseGeneric.cpp @@ -148,7 +148,7 @@ Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc, GenericParams.push_back(Param); // Attach attributes. - Param->getAttrs() = attributes; + Param->attachParsedAttrs(attributes); // Parse the comma, if the list continues. HasComma = consumeIf(tok::comma); diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 0f775790a29c2..7f91715b2c2c1 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -571,7 +571,7 @@ mapParsedParameters(Parser &parser, auto param = ParamDecl::createParsed( ctx, paramInfo.SpecifierLoc, argNameLoc, argName, paramNameLoc, paramName, paramInfo.DefaultArg, parser.CurDeclContext); - param->getAttrs() = paramInfo.Attrs; + param->attachParsedAttrs(paramInfo.Attrs); bool parsingEnumElt = (paramContext == Parser::ParameterContextKind::EnumElement); diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 75a33c75c6791..9431384a75bcc 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -190,6 +190,143 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(PreInverseGenerics) #undef IGNORED_ATTR +private: + static unsigned getABIArity(AbstractFunctionDecl *afd) { + unsigned arity = afd->getParameters()->size(); + arity += afd->getGenericSignature().getGenericParams().size(); + if (afd->hasImplicitSelfDecl()) + arity += 1; + return arity; + } + + void checkABIAttrPBD(PatternBindingDecl *APBD, VarDecl *VD) { + auto PBD = VD->getParentPatternBinding(); + + // To make sure we only diagnose this stuff once, check that VD is the first + // anchoring variable in the PBD. + bool isFirstAnchor = false; + for (auto i : range(PBD->getNumPatternEntries())) { + auto anchorVD = PBD->getAnchoringVarDecl(i); + if (anchorVD) { + isFirstAnchor = (anchorVD == VD); + break; + } + } + + if (!isFirstAnchor) + return; + + // Check that the PBDs have the same number of patterns. + if (PBD->getNumPatternEntries() < APBD->getNumPatternEntries()) { + diagnose(APBD->getPattern(PBD->getNumPatternEntries())->getLoc(), + diag::attr_abi_mismatched_pbd_size, /*abiHasExtra=*/false); + return; + } + if (PBD->getNumPatternEntries() > APBD->getNumPatternEntries()) { + diagnose(PBD->getPattern(APBD->getNumPatternEntries())->getLoc(), + diag::attr_abi_mismatched_pbd_size, /*abiHasExtra=*/true); + return; + } + + // Check that each pattern has the same number of variables. + for (auto i : range(PBD->getNumPatternEntries())) { + SmallVector VDs; + SmallVector AVDs; + + PBD->getPattern(i)->collectVariables(VDs); + APBD->getPattern(i)->collectVariables(AVDs); + + if (VDs.size() < AVDs.size()) { + for (auto AVD : drop_begin(AVDs, VDs.size())) { + AVD->diagnose(diag::attr_abi_mismatched_var, + AVD, /*isABI=*/true); + } + } + else if (VDs.size() > AVDs.size()) { + for (auto VD : drop_begin(VDs, AVDs.size())) { + VD->diagnose(diag::attr_abi_mismatched_var, + VD, /*isABI=*/false); + } + } + } + } + +public: + void visitABIAttr(ABIAttr *attr) { + Decl *AD = attr->abiDecl; + if (isa(D) && isa(AD)) { + auto VD = cast(D); + auto APBD = cast(AD); + + // Diagnose dissimilar PBD structures. + checkABIAttrPBD(APBD, VD); + + // Do the rest of this checking on the corresponding VarDecl, not the + // PBD that's actually in the attribute. Note that `AD` will become null + // if they're too dissimilar to match up. + AD = APBD->getVarAtSimilarStructuralPosition(VD); + } + // TODO: EnumElementDecl? + + if (!AD) + return; + + // Check the ABI decl and bail if there was a problem with it. + TypeChecker::typeCheckDecl(AD); + if (AD->isInvalid()) + return; + + // Do the declarations have the same kind, broadly speaking? Many kinds have + // special mangling behavior (e.g. inits vs normal funcs) that make it + // unrealistic to treat one kind as though it were another. + if (D->getKind() != AD->getKind()) { + // FIXME: DescriptiveDeclKind is overly specific; we really just want to + // say that e.g. a `func` can't have the ABI of a `var`. + diagnoseAndRemoveAttr(attr, diag::attr_abi_mismatched_kind, + D, AD->getDescriptiveKind()); + return; + } + + if (isa(D)) { + auto AFD = cast(D); + auto AAFD = cast(AD); + + // FIXME: How much should we diagnose in IRGen for more precise ABI info? + + // Do the declarations have roughly the same number of parameters? We'll + // allow some fuzziness for what these parameters *are*, since there isn't + // always an ABI difference between e.g. a free function with N parameters + // and an instance method with N-1 parameters (plus an implicit `self`). + if (getABIArity(AFD) != getABIArity(AAFD)) { + diagnoseAndRemoveAttr(attr, diag::attr_abi_mismatched_arity, + AFD); + } + + // Do the declarations match in throwing behavior? We don't care about + // `throws` vs. `rethrows` here, just whether callers will account for an + // error return. + // FIXME: Typed throws? + if (AFD->hasThrows() != AAFD->hasThrows()) { + diagnoseAndRemoveAttr(attr, diag::attr_abi_mismatched_throws, + AFD, /*abiCanThrow=*/AAFD->hasThrows()); + } + + // Do the declarations match in async-ness? + if (AFD->hasAsync() != AAFD->hasAsync()) { + diagnoseAndRemoveAttr(attr, diag::attr_abi_mismatched_async, + AFD, /*abiHasAsync=*/AAFD->hasAsync()); + } + } + + // TODO: Diagnose if Protocol::isMarkerProtocol() - contradiction in terms + // (and mangler can't handle invertible protocols with @abi) + + // TODO: Validate more + // FIXME: The list of properties that have to match is practically endless + // and will grow as new features are added to the compiler. We might want to + // write an AttributeVisitor just to help us catch omissions over time. + } + void visitAlignmentAttr(AlignmentAttr *attr) { // Alignment must be a power of two. auto value = attr->getValue(); @@ -1785,6 +1922,18 @@ void TypeChecker::checkDeclAttributes(Decl *D) { for (auto attr : D->getExpandedAttrs()) { if (!attr->isValid()) continue; + // If the attribute requires a feature that is not enabled, and it is not + // an implicit attribute, diagnose and disable it. + if (auto feature = DeclAttribute::getRequiredFeature(attr->getKind())) { + if (!attr->isImplicit() + && !D->getASTContext().LangOpts.hasFeature(*feature)) { + Checker.diagnoseAndRemoveAttr(attr, diag::requires_experimental_feature, + attr->getAttrName(), false, + getFeatureName(*feature)); + continue; + } + } + // If Attr.def says that the attribute cannot appear on this kind of // declaration, diagnose it and disable it. if (attr->canAppearOnDecl(D)) { @@ -2510,6 +2659,11 @@ void AttributeChecker::visitSILGenNameAttr(SILGenNameAttr *A) { diagnose(A->getLocation(), diag::reserved_runtime_symbol_name, A->Name); } + + if (D->getAttrs().hasAttribute()) { + diagnoseAndRemoveAttr(A, diag::attr_abi_incompatible_with_silgen_name, + D->getDescriptiveKind()); + } } void AttributeChecker::visitUsedAttr(UsedAttr *attr) { diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index b46c058b801ec..1736094088d7b 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1744,6 +1744,10 @@ namespace { UNINTERESTING_ATTR(Safe) #undef UNINTERESTING_ATTR + void visitABIAttr(ABIAttr *attr) { + // TODO: Match or infer + } + void visitAvailableAttr(AvailableAttr *attr) { // FIXME: Check that this declaration is at least as available as the // one it overrides. diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 920d7df17fcbd..34d504f543d4d 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -605,6 +605,20 @@ static void checkGenericParams(GenericContext *ownerCtx) { TypeChecker::checkShadowedGenericParams(ownerCtx); } +/// Returns \c true if \p current and \p other are in the same source file +/// \em and \c current appears before \p other in that file. +static bool isBeforeInSameFile(Decl *current, Decl *other) { + if (current->getDeclContext()->getParentSourceFile() != + other->getDeclContext()->getParentSourceFile()) + return false; + + if (!current->getLoc().isValid()) + return false; + + return current->getASTContext().SourceMgr + .isBeforeInBuffer(current->getLoc(), other->getLoc()); +} + template static void checkOperatorOrPrecedenceGroupRedeclaration( T *decl, Diag<> diagID, Diag<> noteID, @@ -624,16 +638,8 @@ static void checkOperatorOrPrecedenceGroupRedeclaration( if (other == decl || other->isInvalid()) continue; - bool shouldDiagnose = false; - if (currentFile == other->getDeclContext()->getParentSourceFile()) { - // For a same-file redeclaration, make sure we get the diagnostic ordering - // to be sensible. - if (decl->getLoc().isValid() && other->getLoc().isValid() && - ctx.SourceMgr.isBeforeInBuffer(decl->getLoc(), other->getLoc())) { - std::swap(decl, other); - } - shouldDiagnose = true; - } else { + bool shouldDiagnose = true; + if (currentFile != other->getDeclContext()->getParentSourceFile()) { // If the declarations are in different files, only diagnose if we've // enabled the new operator lookup behaviour where decls in the current // module are now favored over imports. @@ -641,6 +647,12 @@ static void checkOperatorOrPrecedenceGroupRedeclaration( } if (shouldDiagnose) { + // For a same-file redeclaration, make sure we get the diagnostic ordering + // to be sensible. + if (isBeforeInSameFile(decl, other)) { + std::swap(decl, other); + } + ctx.Diags.diagnose(decl, diagID); ctx.Diags.diagnose(other, noteID); decl->setInvalid(); @@ -689,28 +701,49 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current, } auto &ctx = current->getASTContext(); + bool currentIsABIOnly = !ABIRoleInfo(current).providesAPI(); + + // If true, two conflicting decls may not be in scope at the same time, so + // we might only detect a redeclaration from one and not the other. + bool partialScopes = currentDC->isLocalContext() && isa(current); // Find other potential definitions. SmallVector otherDefinitions; if (currentDC->isTypeContext()) { // Look within a type context. if (auto nominal = currentDC->getSelfNominalTypeDecl()) { - auto found = nominal->lookupDirect(current->getBaseName()); + OptionSet flags; + if (currentIsABIOnly) + flags |= NominalTypeDecl::LookupDirectFlags::ABIProviding; + auto found = nominal->lookupDirect(current->getBaseName(), SourceLoc(), + flags); otherDefinitions.append(found.begin(), found.end()); } } else if (currentDC->isLocalContext()) { if (!current->isImplicit()) { + ABIRole roleFilter; + if (partialScopes) + // We don't know if conflicts will be detected when the other decl is + // `current`, so if this decl has `ABIRole::Both`, we need this lookup + // to include both ProvidesABI *and* ProvidesAPI decls. + roleFilter = ABIRoleInfo(current).getRole(); + else + roleFilter = currentIsABIOnly ? ABIRole::ProvidesABI + : ABIRole::ProvidesAPI; ASTScope::lookupLocalDecls(currentFile, current->getBaseName(), current->getLoc(), /*stopAfterInnermostBraceStmt=*/true, - otherDefinitions); + roleFilter, otherDefinitions); } } else { assert(currentDC->isModuleScopeContext()); // Look within a module context. + OptionSet flags; + if (currentIsABIOnly) + flags |= ModuleLookupFlags::ABIProviding; currentFile->getParentModule()->lookupValue(current->getBaseName(), NLKind::QualifiedLookup, - otherDefinitions); + flags, otherDefinitions); } // Compare this signature against the signature of other @@ -731,12 +764,17 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current, if (currentModule != otherDC->getParentModule()) continue; - // If both declarations are in the same file, only diagnose the second one. - if (currentFile == otherDC->getParentSourceFile()) - if (current->getLoc().isValid() && - ctx.SourceMgr.isBeforeInBuffer( - current->getLoc(), other->getLoc())) + bool otherIsABIOnly = !ABIRoleInfo(other).providesAPI(); + + if (currentIsABIOnly == otherIsABIOnly || partialScopes) { + // If both declarations are in the same file, only diagnose the second one + if (isBeforeInSameFile(current, other)) continue; + } + else if (!currentIsABIOnly && otherIsABIOnly) + // Don't diagnose a non-ABI-only decl as conflicting with an ABI-only + // decl. (We'll diagnose it in the other direction instead.) + continue; // Don't compare methods vs. non-methods (which only happens with // operators). @@ -2749,7 +2787,8 @@ class DeclChecker : public DeclVisitor { if (!var->hasStorage()) return; - if (var->getAttrs().hasAttribute()) + if (var->getAttrs().hasAttribute() + || !ABIRoleInfo(var).providesAPI()) return; if (var->isInvalid() || PBD->isInvalid()) diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index acfba6a64bfe3..bc85910503b71 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -515,6 +515,9 @@ Expr *TypeChecker::buildRefExpr(ArrayRef Decls, DeclContext *UseDC, DeclNameLoc NameLoc, bool Implicit, FunctionRefInfo functionRefInfo) { assert(!Decls.empty() && "Must have at least one declaration"); + ASSERT(llvm::any_of(Decls, [](ValueDecl *VD) { + return ABIRoleInfo(VD).providesAPI(); + }) && "DeclRefExpr can't refer to ABI-only decl"); auto &Context = UseDC->getASTContext(); diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index 47032efe2bceb..85d24c446c8b2 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -32,6 +32,27 @@ using namespace swift; +void swift::simple_display(llvm::raw_ostream &out, NameLookupOptions options) { + using Flag = std::pair; + Flag possibleFlags[] = { + {NameLookupFlags::IgnoreAccessControl, "IgnoreAccessControl"}, + {NameLookupFlags::IncludeOuterResults, "IncludeOuterResults"}, + {NameLookupFlags::IncludeUsableFromInline, "IncludeUsableFromInline"}, + {NameLookupFlags::ExcludeMacroExpansions, "ExcludeMacroExpansions"}, + {NameLookupFlags::IgnoreMissingImports, "IgnoreMissingImports"}, + {NameLookupFlags::ABIProviding, "ABIProviding"}, + }; + + auto flagsToPrint = llvm::make_filter_range( + possibleFlags, [&](Flag flag) { return options.contains(flag.first); }); + + out << "{ "; + interleave( + flagsToPrint, [&](Flag flag) { out << flag.second; }, + [&] { out << ", "; }); + out << " }"; +} + namespace { /// Builder that helps construct a lookup result from the raw lookup /// data. @@ -225,6 +246,8 @@ convertToUnqualifiedLookupOptions(NameLookupOptions options) { newOptions |= UnqualifiedLookupFlags::ExcludeMacroExpansions; if (options.contains(NameLookupFlags::IgnoreMissingImports)) newOptions |= UnqualifiedLookupFlags::IgnoreMissingImports; + if (options.contains(NameLookupFlags::ABIProviding)) + newOptions |= UnqualifiedLookupFlags::ABIProviding; return newOptions; } @@ -330,6 +353,8 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, subOptions |= NL_IgnoreAccessControl; if (options.contains(NameLookupFlags::IgnoreMissingImports)) subOptions |= NL_IgnoreMissingImports; + if (options.contains(NameLookupFlags::ABIProviding)) + subOptions |= NL_ABIProviding; // We handle our own overriding/shadowing filtering. subOptions &= ~NL_RemoveOverridden; @@ -430,6 +455,8 @@ LookupTypeResult TypeChecker::lookupMemberType(DeclContext *dc, subOptions |= NL_IgnoreMissingImports; if (options.contains(NameLookupFlags::IncludeUsableFromInline)) subOptions |= NL_IncludeUsableFromInline; + if (options.contains(NameLookupFlags::ABIProviding)) + subOptions |= NL_ABIProviding; // Make sure we've resolved implicit members, if we need them. namelookup::installSemanticMembersIfNeeded(type, name); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index e31361e01cb6e..e1539a622a3f0 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2776,6 +2776,23 @@ BraceStmt *PreCheckClosureBodyRequest::evaluate(Evaluator &evaluator, return body; } + +/// Determine whether the given declaration should not have a definition. +static bool requiresNoDefinition(Decl *decl) { + if (auto func = dyn_cast(decl)) { + // Function with @_extern should not have a body. + if (func->getAttrs().hasAttribute()) + return true; + } + // FIXME: Should be able to write this more nicely + if (!ABIRoleInfo(decl).providesAPI()) + // Decls which merely define the ABI of another decl should not have a + // body. + return true; + // Everything else can have a definition. + return false; +} + /// Determine whether the given declaration requires a definition. /// /// Only valid for declarations that can have definitions, i.e., @@ -2793,7 +2810,6 @@ static bool requiresDefinition(Decl *decl) { // Functions can have _silgen_name, semantics, and NSManaged attributes. if (auto func = dyn_cast(decl)) { if (func->getAttrs().hasAttribute() || - func->getAttrs().hasAttribute() || func->getAttrs().hasAttribute() || func->getAttrs().hasAttribute()) return false; @@ -2820,20 +2836,15 @@ static bool requiresDefinition(Decl *decl) { if (fileUnit->getKind() == FileUnitKind::SerializedAST) return false; + // Declarations that cannot possibly have a definition definitely don't + // require one. + if (requiresNoDefinition(decl)) + return false; + // Everything else requires a definition. return true; } -/// Determine whether the given declaration should not have a definition. -static bool requiresNoDefinition(Decl *decl) { - if (auto func = dyn_cast(decl)) { - // Function with @_extern should not have a body. - return func->getAttrs().hasAttribute(); - } - // Everything else can have a definition. - return false; -} - BraceStmt * TypeCheckFunctionBodyRequest::evaluate(Evaluator &eval, AbstractFunctionDecl *AFD) const { diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 03f744f1486d1..a7bd754d87be7 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -161,11 +161,18 @@ enum class NameLookupFlags { /// Whether to include members that would otherwise be filtered out because /// they come from a module that has not been imported. IgnoreMissingImports = 1 << 4, + /// If @abi attributes are present, return the decls representing the ABI, + /// not the API. + ABIProviding = 1 << 5, + + // Reminder: If you add a flag, make sure you update simple_display() below }; /// A set of options that control name lookup. using NameLookupOptions = OptionSet; +void simple_display(llvm::raw_ostream &out, NameLookupOptions opts); + inline NameLookupOptions operator|(NameLookupFlags flag1, NameLookupFlags flag2) { return NameLookupOptions(flag1) | flag2; diff --git a/lib/Serialization/DeclTypeRecordNodes.def b/lib/Serialization/DeclTypeRecordNodes.def index f4637fbbbb1b7..21197929ff32a 100644 --- a/lib/Serialization/DeclTypeRecordNodes.def +++ b/lib/Serialization/DeclTypeRecordNodes.def @@ -211,11 +211,12 @@ OTHER(CLANG_TYPE, 153) OTHER(DERIVATIVE_FUNCTION_CONFIGURATION, 154) OTHER(ERROR_FLAG, 155) +OTHER(ABI_ONLY_COUNTERPART, 156) TRAILING_INFO(CONDITIONAL_SUBSTITUTION) TRAILING_INFO(CONDITIONAL_SUBSTITUTION_COND) -OTHER(LIFETIME_DEPENDENCE, 158) +OTHER(LIFETIME_DEPENDENCE, 160) TRAILING_INFO(INHERITED_PROTOCOLS) diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index fb7258d3471cd..9fcc0c306d5b6 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -3247,33 +3247,6 @@ static bool attributeChainContains(DeclAttribute *attr) { return tempAttrs.hasAttribute(); } -// Set original declaration and parameter indices in `@differentiable` -// attributes. -// -// Serializing/deserializing the original declaration DeclID in -// `@differentiable` attributes does not work because it causes -// `@differentiable` attribute deserialization to enter an infinite loop. -// -// Instead, call this ad-hoc function after deserializing a declaration to set -// the original declaration and parameter indices for its `@differentiable` -// attributes. -static void setOriginalDeclarationAndParameterIndicesInDifferentiableAttributes( - Decl *decl, DeclAttribute *attrs, - llvm::DenseMap - &diffAttrParamIndicesMap) { - DeclAttributes tempAttrs; - tempAttrs.setRawAttributeChain(attrs); - for (auto *attr : tempAttrs.getAttributes()) { - auto *diffAttr = const_cast(attr); - diffAttr->setOriginalDeclaration(decl); - diffAttr->setParameterIndices(diffAttrParamIndicesMap[diffAttr]); - } - for (auto *attr : tempAttrs.getAttributes()) { - auto *derAttr = const_cast(attr); - derAttr->setOriginalDeclaration(decl); - } -} - Decl *ModuleFile::getDecl(DeclID DID) { Expected deserialized = getDeclChecked(DID); if (!deserialized) { @@ -3306,6 +3279,11 @@ class DeclDeserializer { // Auxiliary map for deserializing `@differentiable` attributes. llvm::DenseMap diffAttrParamIndicesMap; + /// State for resolving the declaration in an ABIAttr. + std::optional> unresolvedABIAttr; + /// State for setting up an ABIAttr's counterpart relationship. + DeclID ABIDeclCounterpartID = 0; + void AddAttribute(DeclAttribute *Attr) { // Advance the linked list. // This isn't just using DeclAttributes because that would result in the @@ -3349,6 +3327,8 @@ class DeclDeserializer { decl.get()->setInherited(inherited); } + llvm::Error finishRecursiveAttrs(Decl *decl, DeclAttribute *attrs); + public: DeclDeserializer(ModuleFile &MF, Serialized &declOrOffset) : MF(MF), ctx(MF.getContext()), declOrOffset(declOrOffset) {} @@ -5737,10 +5717,31 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { if (recordID == ERROR_FLAG) { assert(!IsInvalid && "Error flag written multiple times"); IsInvalid = true; + } else if (recordID == ABI_ONLY_COUNTERPART) { + assert(ABIDeclCounterpartID == 0 + && "ABI-only counterpart written multiple times"); + DeclID counterpartID; + serialization::decls_block::ABIOnlyCounterpartLayout::readRecord( + scratch, counterpartID); + // Defer resolving `ABIDeclCounterpartID` until `finishRecursiveAttrs()` + // because the two decls reference each other. + ABIDeclCounterpartID = counterpartID; } else if (isDeclAttrRecord(recordID)) { DeclAttribute *Attr = nullptr; bool skipAttr = false; switch (recordID) { + case decls_block::ABI_DECL_ATTR: { + bool isImplicit; + DeclID abiDeclID; + serialization::decls_block::ABIDeclAttrLayout::readRecord( + scratch, isImplicit, abiDeclID); + Attr = new (ctx) ABIAttr(nullptr, isImplicit); + // Defer resolving `abiDeclID` until `finishRecursiveAttrs()` because + // the two decls reference each other. + unresolvedABIAttr.emplace(cast(Attr), abiDeclID); + break; + } + case decls_block::SILGenName_DECL_ATTR: { bool isImplicit; serialization::decls_block::SILGenNameDeclAttrLayout::readRecord( @@ -6481,6 +6482,51 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() { } } +/// Complete attributes that contain recursive references to the decl being +/// deserialized or to other decls. This method is called after \p decl is +/// created and stored into the \c ModuleFile::Decls table, so any cycles +/// between mutually-referencing decls will be broken. +/// +/// Attributes handled here include: +/// +/// \li \c \@differentiable +/// \li \c \@derivative +/// \li \c \@abi +llvm::Error DeclDeserializer::finishRecursiveAttrs(Decl *decl, DeclAttribute *attrs) { + DeclAttributes tempAttrs; + tempAttrs.setRawAttributeChain(attrs); + + // @differentiable and @derivative + for (auto *attr : tempAttrs.getAttributes()) { + auto *diffAttr = const_cast(attr); + diffAttr->setOriginalDeclaration(decl); + diffAttr->setParameterIndices(diffAttrParamIndicesMap[diffAttr]); + } + for (auto *attr : tempAttrs.getAttributes()) { + auto *derAttr = const_cast(attr); + derAttr->setOriginalDeclaration(decl); + } + + // @abi + if (unresolvedABIAttr) { + auto abiDeclOrError = MF.getDeclChecked(unresolvedABIAttr->second); + if (!abiDeclOrError) + return abiDeclOrError.takeError(); + unresolvedABIAttr->first->abiDecl = abiDeclOrError.get(); + decl->recordABIAttr(unresolvedABIAttr->first); + } + if (ABIDeclCounterpartID != 0) { + // This decl is the `abiDecl` of an `ABIAttr`. Force the decl that `ABIAttr` + // belongs to so that `recordABIAttr()` will be called. + auto counterpartOrError = MF.getDeclChecked(ABIDeclCounterpartID); + if (!counterpartOrError) + return counterpartOrError.takeError(); + (void)counterpartOrError.get(); + } + + return llvm::Error::success(); +} + Expected DeclDeserializer::getDeclCheckedImpl( llvm::function_ref matchAttributes) { @@ -6525,17 +6571,11 @@ DeclDeserializer::getDeclCheckedImpl( #define CASE(RECORD_NAME) \ case decls_block::RECORD_NAME##Layout::Code: {\ auto declOrError = deserialize##RECORD_NAME(scratch, blobData); \ - if (declOrError) { \ - /* \ - // Set original declaration and parameter indices in `@differentiable` \ - // attributes. \ - */ \ - setOriginalDeclarationAndParameterIndicesInDifferentiableAttributes(\ - declOrError.get(), DAttrs, diffAttrParamIndicesMap); \ - } \ if (!declOrError) \ return declOrError; \ declOrOffset = declOrError.get(); \ + if (auto finishError = finishRecursiveAttrs(declOrError.get(), DAttrs)) \ + return finishError; \ break; \ } diff --git a/lib/Serialization/ModuleFile.cpp b/lib/Serialization/ModuleFile.cpp index 7090f42db9990..eacba1282582b 100644 --- a/lib/Serialization/ModuleFile.cpp +++ b/lib/Serialization/ModuleFile.cpp @@ -330,6 +330,7 @@ bool ModuleFile::mayHaveDiagnosticsPointingAtBuffer() const { ModuleFile::~ModuleFile() { } void ModuleFile::lookupValue(DeclName name, + OptionSet flags, SmallVectorImpl &results) { PrettyStackTraceModuleFile stackEntry(*this); @@ -350,7 +351,8 @@ void ModuleFile::lookupValue(DeclName name, } auto VD = cast(declOrError.get()); if (name.isSimpleName() || VD->getName().matchesRef(name)) - results.push_back(VD); + if (ABIRoleInfo(VD).matchesOptions(flags)) + results.push_back(VD); } } } @@ -368,7 +370,8 @@ void ModuleFile::lookupValue(DeclName name, continue; } auto VD = cast(declOrError.get()); - results.push_back(VD); + if (ABIRoleInfo(VD).matchesOptions(flags)) + results.push_back(VD); } } } @@ -453,7 +456,8 @@ TypeDecl *ModuleFile::lookupNestedType(Identifier name, diagnoseAndConsumeError(typeOrErr.takeError()); continue; } - return cast(typeOrErr.get()); + if (ABIRoleInfo(typeOrErr.get()).providesAPI()) // FIXME: flags? + return cast(typeOrErr.get()); } } } @@ -622,6 +626,8 @@ void ModuleFile::lookupVisibleDecls(ImportPath::Access accessPath, diagnoseAndConsumeError(declOrError.takeError()); return; } + if (!ABIRoleInfo(declOrError.get()).providesAPI()) // FIXME: flags? + return; consumer.foundDecl(cast(declOrError.get()), DeclVisibilityKind::VisibleAtTopLevel); }; @@ -1005,6 +1011,8 @@ void ModuleFile::getTopLevelDecls( consumeError(declOrError.takeError()); continue; } + if (!ABIRoleInfo(declOrError.get()).providesAPI()) // FIXME: flags + continue; results.push_back(declOrError.get()); } } @@ -1053,6 +1061,8 @@ ModuleFile::getLocalTypeDecls(SmallVectorImpl &results) { for (auto DeclID : Core->LocalTypeDecls->data()) { auto TD = cast(getDecl(DeclID)); + if (!ABIRoleInfo(TD).providesAPI()) // FIXME: flags + continue; results.push_back(TD); } } diff --git a/lib/Serialization/ModuleFile.h b/lib/Serialization/ModuleFile.h index 8cc54e47e6cd5..031a7270ded1e 100644 --- a/lib/Serialization/ModuleFile.h +++ b/lib/Serialization/ModuleFile.h @@ -763,7 +763,8 @@ class ModuleFile ModuleDecl *getUnderlyingModule() const { return UnderlyingModule; } /// Searches the module's top-level decls for the given identifier. - void lookupValue(DeclName name, SmallVectorImpl &results); + void lookupValue(DeclName name, OptionSet flags, + SmallVectorImpl &results); /// Searches the module's local type decls for the given mangled name. TypeDecl *lookupLocalType(StringRef MangledName); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index e8de214f8294f..f73d94665bc62 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,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 = 908; // debug scopes and source locs +const uint16_t SWIFTMODULE_VERSION_MINOR = 909; // @abi attribute /// A standard hash seed used for all string hashes in a serialized module. /// @@ -1226,6 +1226,12 @@ namespace decls_block { ERROR_FLAG >; + /// A field marking a decl as being ABI-only and pointing to its API counterpart. + using ABIOnlyCounterpartLayout = BCRecordLayout< + ABI_ONLY_COUNTERPART, + DeclIDField // API decl + >; + /// A placeholder for invalid types TYPE_LAYOUT(ErrorTypeLayout, ERROR_TYPE, @@ -2347,6 +2353,12 @@ namespace decls_block { BCFixed<2> // exclusivity mode >; + using ABIDeclAttrLayout = BCRecordLayout< + ABI_DECL_ATTR, + BCFixed<1>, // implicit flag + DeclIDField // ABI decl + >; + using AvailableDeclAttrLayout = BCRecordLayout< Available_DECL_ATTR, BCFixed<1>, // implicit flag diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 7fda736611c2b..508757e9f8ea0 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2885,6 +2885,15 @@ class Serializer::DeclSerializer : public DeclVisitor { } #include "swift/AST/DeclAttr.def" + case DeclAttrKind::ABI: { + auto *theAttr = cast(DA); + auto abbrCode = S.DeclTypeAbbrCodes[ABIDeclAttrLayout::Code]; + auto abiDeclID = S.addDeclRef(theAttr->abiDecl); + ABIDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, + theAttr->isImplicit(), abiDeclID); + return; + } + case DeclAttrKind::SILGenName: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[SILGenNameDeclAttrLayout::Code]; @@ -4011,6 +4020,10 @@ class Serializer::DeclSerializer : public DeclVisitor { writeDeserializationSafety(D); + auto abiRole = ABIRoleInfo(D); + if (!abiRole.providesAPI()) + writeABIOnlyCounterpart(abiRole.getCounterpartUnchecked()); + // Emit attributes (if any). for (auto Attr : D->getAttrs()) writeDeclAttribute(D, Attr); @@ -4031,6 +4044,13 @@ class Serializer::DeclSerializer : public DeclVisitor { ErrorFlagLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode); } + void writeABIOnlyCounterpart(const Decl *counterpart) { + using namespace decls_block; + unsigned abbrCode = S.DeclTypeAbbrCodes[ABIOnlyCounterpartLayout::Code]; + ABIOnlyCounterpartLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, + S.addDeclRef(counterpart)); + } + void noteUseOfExportedPrespecialization(const AbstractFunctionDecl *afd) { bool hasNoted = false; for (auto *A : afd->getAttrs().getAttributes()) { @@ -6211,6 +6231,7 @@ void Serializer::writeAllDeclsAndTypes() { registerDeclTypeAbbr(); registerDeclTypeAbbr(); + registerDeclTypeAbbr(); registerDeclTypeAbbr(); diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 0ad9b6cba8f08..d807628d30231 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -1851,7 +1851,7 @@ bool SerializedASTFile::isSystemModule() const { void SerializedASTFile::lookupValue(DeclName name, NLKind lookupKind, OptionSet Flags, SmallVectorImpl &results) const{ - File.lookupValue(name, results); + File.lookupValue(name, Flags, results); } StringRef diff --git a/test/ASTGen/attrs.swift b/test/ASTGen/attrs.swift index 813ff884e0540..cb7c6ed5a7182 100644 --- a/test/ASTGen/attrs.swift +++ b/test/ASTGen/attrs.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -dump-parse -disable-availability-checking -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Extern -enable-experimental-move-only -enable-experimental-feature ParserASTGen > %t/astgen.ast.raw -// RUN: %target-swift-frontend %s -dump-parse -disable-availability-checking -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Extern -enable-experimental-move-only > %t/cpp-parser.ast.raw +// RUN: %target-swift-frontend %s -dump-parse -disable-availability-checking -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature ABIAttribute -enable-experimental-feature Extern -enable-experimental-move-only -enable-experimental-feature ParserASTGen > %t/astgen.ast.raw +// RUN: %target-swift-frontend %s -dump-parse -disable-availability-checking -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature ABIAttribute -enable-experimental-feature Extern -enable-experimental-move-only > %t/cpp-parser.ast.raw // Filter out any addresses in the dump, since they can differ. // RUN: sed -E 's#0x[0-9a-fA-F]+##g' %t/cpp-parser.ast.raw > %t/cpp-parser.ast @@ -8,13 +8,14 @@ // RUN: %diff -u %t/astgen.ast %t/cpp-parser.ast -// RUN: %target-typecheck-verify-swift -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature Extern -enable-experimental-move-only -enable-experimental-feature ParserASTGen +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature ABIAttribute -enable-experimental-feature Extern -enable-experimental-move-only -enable-experimental-feature ParserASTGen // REQUIRES: executable_test // REQUIRES: swift_swift_parser // REQUIRES: swift_feature_SymbolLinkageMarkers // REQUIRES: swift_feature_Extern // REQUIRES: swift_feature_ParserASTGen +// REQUIRES: swift_feature_ABIAttribute // rdar://116686158 // UNSUPPORTED: asan @@ -62,7 +63,10 @@ struct S4 {} @implementation extension ObjCClass1 {} // expected-error {{cannot find type 'ObjCClass1' in scope}} @implementation(Category) extension ObjCClass1 {} // expected-error {{cannot find type 'ObjCClass1' in scope}} -@_alignment(8) struct AnyAlignment {} +@abi(func fn_abi()) // expected-error {{cannot give global function 'fn' the ABI of a global function with a different number of low-level parameters}} +func fn(_: Int) {} + +@_alignment(8) struct AnyAlignment {} @_allowFeatureSuppression(IsolatedAny) public func testFeatureSuppression(fn: @isolated(any) @Sendable () -> ()) {} diff --git a/test/IDE/complete_decl_attribute_feature_requirement.swift b/test/IDE/complete_decl_attribute_feature_requirement.swift new file mode 100644 index 0000000000000..adeb35c9ed7af --- /dev/null +++ b/test/IDE/complete_decl_attribute_feature_requirement.swift @@ -0,0 +1,126 @@ +// This contains code completion test cases for features covered by experimental +// feature flags, and tests both the case when the feature is disabled and when +// it's enabled. When a feature becomes non-experimental, move its test cases +// into the normal complete_decl_attribute.swift test file. + +// REQUIRES: asserts + +// RUN: %batch-code-completion -filecheck-additional-suffix _DISABLED +// RUN: %batch-code-completion -filecheck-additional-suffix _ENABLED -enable-experimental-feature ABIAttribute + +// NOTE: Please do not include the ", N items" after "Begin completions". The +// item count creates needless merge conflicts given that an "End completions" +// line exists for each test. + +@#^KEYWORD2^# func method(){} + +// KEYWORD2: Begin completions +// KEYWORD2_ENABLED-DAG: Keyword/None: abi[#Func Attribute#]; name=abi +// KEYWORD2_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// KEYWORD2: End completions + +@#^KEYWORD3^# class C {} + +// KEYWORD3: Begin completions +// KEYWORD3_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// KEYWORD3_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// KEYWORD3: End completions + +@#^KEYWORD3_2?check=KEYWORD3^#IB class C2 {} +// Same as KEYWORD3. + +@#^KEYWORD4^# enum E {} +// KEYWORD4: Begin completions +// KEYWORD4_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// KEYWORD4_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// KEYWORD4: End completions + +@#^KEYWORD5^# struct S{} +// KEYWORD5: Begin completions +// KEYWORD5_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// KEYWORD5_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// KEYWORD5: End completions + +@#^ON_GLOBALVAR^# var globalVar +// ON_GLOBALVAR: Begin completions +// ON_GLOBALVAR_ENABLED-DAG: Keyword/None: abi[#Var Attribute#]; name=abi +// ON_GLOBALVAR_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// ON_GLOBALVAR: End completions + +struct _S { + @#^ON_INIT^# init() +// ON_INIT: Begin completions +// ON_INIT_ENABLED-DAG: Keyword/None: abi[#Constructor Attribute#]; name=abi +// ON_INIT_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// ON_INIT: End completions + + @#^ON_PROPERTY^# var foo +// ON_PROPERTY: Begin completions +// ON_PROPERTY_ENABLED-DAG: Keyword/None: abi[#Var Attribute#]; name=abi +// ON_PROPERTY_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// ON_PROPERTY: End completions + + @#^ON_METHOD^# private + func foo() +// ON_METHOD: Begin completions +// ON_METHOD_ENABLED-DAG: Keyword/None: abi[#Func Attribute#]; name=abi +// ON_METHOD_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// ON_METHOD: End completions + + + func bar(@#^ON_PARAM_1?check=ON_PARAM^#) +// ON_PARAM: Begin completions +// ON_PARAM_ENABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// ON_PARAM_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// ON_PARAM: End completions + + func bar( + @#^ON_PARAM_2?check=ON_PARAM^# + + arg: Int + ) +// Same as ON_PARAM. + + @#^ON_MEMBER_INDEPENDENT_1?check=ON_MEMBER_LAST^# + + func dummy1() {} +// Same as ON_MEMBER_LAST. + + @#^ON_MEMBER_INDEPENDENT_2?check=ON_MEMBER_LAST^# + func dummy2() {} +// Same as ON_MEMBER_LAST. + + + @#^ON_MEMBER_LAST^# +// ON_MEMBER_LAST: Begin completions +// ON_MEMBER_LAST_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi +// ON_MEMBER_LAST_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// ON_MEMBER_LAST: End completions +} + +func takeClosure(_: () -> Void) { + takeClosure { @#^IN_CLOSURE^# in + print("x") + } +} +// IN_CLOSURE: Begin completions +// FIXME: Not valid in this position (but CompletionLookup can't tell that) +// IN_CLOSURE_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi +// IN_CLOSURE_DISABLED-NOT: Keyword/None: abi[#{{.*}} Attribute#]; name=abi +// IN_CLOSURE: End completions + +@#^KEYWORD_INDEPENDENT_1?check=KEYWORD_LAST^# + +func dummy1() {} +// Same as KEYWORD_LAST. + +@#^KEYWORD_INDEPENDENT_2?check=KEYWORD_LAST^# +func dummy2() {} +// Same as KEYWORD_LAST. + +@#^KEYWORD_LAST^# + +// KEYWORD_LAST: Begin completions +// KEYWORD_LAST_ENABLED-DAG: Keyword/None: abi[#Declaration Attribute#]; name=abi +// KEYWORD_LAST_DISABLED-NOT: Keyword/None: abi[#Declaration Attribute#]; name=abi +// KEYWORD_LAST: End completions diff --git a/test/IRGen/asmname.swift b/test/IRGen/asmname.swift index c5543a364dc0e..b86cc485723c1 100644 --- a/test/IRGen/asmname.swift +++ b/test/IRGen/asmname.swift @@ -1,7 +1,9 @@ -// RUN: %target-swift-frontend %s -emit-ir | %FileCheck %s - -// REQUIRES: CPU=i386 || CPU=x86_64 +// RUN: %target-swift-frontend -enable-experimental-feature ABIAttribute %s -emit-ir > %t.ir +// RUN: %FileCheck --input-file %t.ir %s +// RUN: %FileCheck --check-prefix NEGATIVE --input-file %t.ir %s +// REQUIRES: CPU=i386 || CPU=x86_64 || CPU=arm64 +// REQUIRES: swift_feature_ABIAttribute // Non-Swift _silgen_name definitions @@ -35,7 +37,7 @@ private func PlainPrivate() { } // CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @silgen_name_public // CHECK: define hidden swiftcc void @silgen_name_internal // CHECK: define internal swiftcc void @silgen_name_private -// CHECK-NOT: SilgenName +// NEGATIVE-NOT: define {{.*}}SilgenName // Swift cdecl definitions @@ -52,6 +54,8 @@ private func PlainPrivate() { } // CHECK: define hidden swiftcc void @"$s7asmname13CDeclInternal // CHECK: define internal void @cdecl_private() + + // silgen_name on enum constructors public enum X { case left(Int64) @@ -65,3 +69,43 @@ extension X { self = .left(blah) } } + + + +// Swift abi definitions + +@abi(func abi_ABIAttrPublic()) public func api_ABIAttrPublic() { } +@abi(func abi_ABIAttrInternal()) internal func api_ABIAttrInternal() { } +@abi(func abi_ABIAttrPrivate()) private func api_ABIAttrPrivate() { } +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s7asmname17abi_ABIAttrPublic +// CHECK: define hidden swiftcc void @"$s7asmname19abi_ABIAttrInternal +// CHECK: define internal swiftcc void @"$s7asmname18abi_ABIAttrPrivate + +@abi(var abi_ABIAttrPublic_var: Int64) public var api_ABIAttrPublic_var: Int64 { get { 1 } set {} } +@abi(var abi_ABIAttrInternal_var: Int64) internal var api_ABIAttrInternal_var: Int64 { get { 1 } set {} } +@abi(var abi_ABIAttrPrivate_var: Int64) private var api_ABIAttrPrivate_var: Int64 { get { 1 } set {} } +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc i64 @"$s7asmname21abi_ABIAttrPublic_vars5Int64Vvg" +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s7asmname21abi_ABIAttrPublic_vars5Int64Vvs" +// CHECK: define hidden swiftcc i64 @"$s7asmname23abi_ABIAttrInternal_vars5Int64Vvg" +// CHECK: define hidden swiftcc void @"$s7asmname23abi_ABIAttrInternal_vars5Int64Vvs" +// CHECK: define internal swiftcc i64 @"$s7asmname22abi_ABIAttrPrivate_vars5Int64Vvg" +// CHECK: define internal swiftcc void @"$s7asmname22abi_ABIAttrPrivate_vars5Int64Vvs" + +@abi(public func abi_ABIAttrGenericNonSendableToSendable(_ value: T) -> T) +public func api_ABIAttrGenericNonSendableToSendable(_ value: T) -> T { return value } +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s7asmname031abi_ABIAttrGenericNonSendableToF0yxxlF" +// NEGATIVE-NOT: @"$s7asmname031abi_ABIAttrGenericNonSendableToF0yxxs0F0RzlF" + +// Similar to hack applied to `AsyncStream.init(unfolding:onCancel:)` +@abi(public func abi_ABIAttrPreconcurrencyToNotPreconcurrency(_ c1: () -> Void, _ c2: @Sendable () -> Void)) +@preconcurrency public func api_ABIAttrPreconcurrencyToNotPreconcurrency(_ c1: () -> Void, _ c2: @Sendable () -> Void) {} +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s7asmname030abi_ABIAttrPreconcurrencyToNotD0yyyyXE_yyYbXEtF" + +extension X { +// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc { i64, i8 } @"$s7asmname1XO8abi_blahACs5Int64V_tcfC" + @abi(init(abi_blah: Int64)) + public init(api_blah: Int64) { + self = .left(api_blah) + } +} +// NEGATIVE-NOT: define {{.*}}api_ diff --git a/test/Misc/verify-swift-feature-testing.test-sh b/test/Misc/verify-swift-feature-testing.test-sh index 9edfd59fc302f..acfcc0e305f9a 100755 --- a/test/Misc/verify-swift-feature-testing.test-sh +++ b/test/Misc/verify-swift-feature-testing.test-sh @@ -20,6 +20,10 @@ EXCEPTIONAL_FILES = [ pathlib.Path("test/ModuleInterface/swift-export-as.swift"), # Uses the pseudo-feature AvailabilityMacro= pathlib.Path("test/Sema/availability_define.swift"), + # Tests behavior when you try to use a feature without enabling it + pathlib.Path("test/attr/feature_requirement.swift"), + # Tests completion with features both enabled and disabled + pathlib.Path("test/IDE/complete_decl_attribute_feature_requirement.swift"), ] FEATURE_USAGE_RE = re.compile( diff --git a/test/ModuleInterface/attrs.swift b/test/ModuleInterface/attrs.swift index f63db11059cb0..2e33b59183b78 100644 --- a/test/ModuleInterface/attrs.swift +++ b/test/ModuleInterface/attrs.swift @@ -1,6 +1,8 @@ -// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name attrs +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name attrs -enable-experimental-feature ABIAttribute // RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name attrs -// RUN: %FileCheck %s < %t.swiftinterface +// RUN: %FileCheck %s --input-file %t.swiftinterface + +// REQUIRES: swift_feature_ABIAttribute // CHECK: @_transparent public func glass() -> Swift.Int { return 0 }{{$}} @_transparent public func glass() -> Int { return 0 } @@ -27,3 +29,23 @@ public func someGenericFunction(_ t: T) -> Int { return 0 } internal func __specialize_someGenericFunction(_ t: T) -> Int { fatalError("don't call") } + +@abi(public func __abi__abiAttrOnFunction(param: Int)) +public func abiAttrOnFunction(param: Int) {} +// CHECK: #if {{.*}} $ABIAttribute +// CHECK: @abi(public func __abi__abiAttrOnFunction(param: Swift.Int)) +// CHECK: public func abiAttrOnFunction(param: Swift.Int) +// CHECK: #else +// CHECK: @_silgen_name("$s5attrs07__abi__B14AttrOnFunction5paramySi_tF") +// CHECK: public func abiAttrOnFunction(param: Swift.Int) +// CHECK: #endif + +@abi(public let __abi__abiAttrOnVar: Int) +public var abiAttrOnVar: Int = 42 +// CHECK: #if {{.*}} $ABIAttribute +// CHECK: @abi(public var __abi__abiAttrOnVar: Swift.Int) +// CHECK: public var abiAttrOnVar: Swift.Int +// CHECK: #else +// CHECK: @available(*, unavailable, message: "this compiler cannot match the ABI specified by the @abi attribute") +// CHECK: public var abiAttrOnVar: Swift.Int +// CHECK: #endif diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift new file mode 100644 index 0000000000000..a0f8a8e21ea8b --- /dev/null +++ b/test/attr/attr_abi.swift @@ -0,0 +1,321 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature Extern -enable-experimental-feature ABIAttribute -parse-as-library + +// REQUIRES: swift_feature_ABIAttribute +// REQUIRES: swift_feature_Extern + +// +// Same-kind checking +// + +@abi(func funcForFunc_abi()) +func funcForFunc() {} + +@abi(var varForVar_abi: Int) +var varForVar: Int = 0 + +@abi(func funcForVar_abi()) // expected-error {{cannot give var 'funcForVar' the ABI of a global function}} +var funcForVar: Int = 0 + +@abi(var varForFunc_abi: Int) // expected-error {{cannot give global function 'varForFunc()' the ABI of a pattern binding}} +func varForFunc() {} + +// +// Function arity checking +// + +@abi(func param00_generic00() -> Int) +func param00_generic00() -> Int { fatalError() } + +@abi(func param10_generic00(_: Int) -> Int) // expected-error {{cannot give global function 'param10_generic00()' the ABI of a global function with a different number of low-level parameters}} +func param10_generic00() -> Int { fatalError() } + +@abi(func param01_generic00() -> Int) // expected-error {{cannot give global function 'param01_generic00' the ABI of a global function with a different number of low-level parameters}} +func param01_generic00(_: Int) -> Int { fatalError() } + +@abi(func param11_generic00(_: Int) -> Int) +func param11_generic00(_: Int) -> Int { fatalError() } + + + +@abi(func param00_generic10() -> T) // expected-error {{cannot give global function 'param00_generic10()' the ABI of a global function with a different number of low-level parameters}} +func param00_generic10() -> Int { fatalError() } + +@abi(func param10_generic10(_: Int) -> T) // expected-error {{cannot give global function 'param10_generic10()' the ABI of a global function with a different number of low-level parameters}} +func param10_generic10() -> Int { fatalError() } + +@abi(func param01_generic10() -> T) +func param01_generic10(_: Int) -> Int { fatalError() } + +@abi(func param11_generic10(_: Int) -> T) // expected-error {{cannot give global function 'param11_generic10' the ABI of a global function with a different number of low-level parameters}} +func param11_generic10(_: Int) -> Int { fatalError() } + + + +@abi(func param00_generic01() -> Int) // expected-error {{cannot give global function 'param00_generic01()' the ABI of a global function with a different number of low-level parameters}} +func param00_generic01() -> T { fatalError() } + +@abi(func param10_generic01(_: Int) -> Int) +func param10_generic01() -> T { fatalError() } + +@abi(func param01_generic01() -> Int) // expected-error {{cannot give global function 'param01_generic01' the ABI of a global function with a different number of low-level parameters}} +func param01_generic01(_: Int) -> T { fatalError() } + +@abi(func param11_generic01(_: Int) -> Int) // expected-error {{cannot give global function 'param11_generic01' the ABI of a global function with a different number of low-level parameters}} +func param11_generic01(_: Int) -> T { fatalError() } + + + +@abi(func param00_generic11() -> T) +func param00_generic11() -> T { fatalError() } + +@abi(func param10_generic11(_: Int) -> T) // expected-error {{cannot give global function 'param10_generic11()' the ABI of a global function with a different number of low-level parameters}} +func param10_generic11() -> T { fatalError() } + +@abi(func param01_generic11() -> T) // expected-error {{cannot give global function 'param01_generic11' the ABI of a global function with a different number of low-level parameters}} +func param01_generic11(_: Int) -> T { fatalError() } + +@abi(func param11_generic11(_: Int) -> T) +func param11_generic11(_: Int) -> T { fatalError() } + +// +// Throws effect checking +// + +@abi(func throws00(_: () throws -> Void)) +func throws00(_: () throws -> Void) {} + +@abi(func throws10(_: () throws -> Void) throws) // expected-error {{cannot give 'throws10' the ABI of a global function which can throw}} +func throws10(_: () throws -> Void) {} + +@abi(func throws20(_: () throws -> Void) rethrows) // expected-error {{cannot give 'throws20' the ABI of a global function which can throw}} +func throws20(_: () throws -> Void) {} + +@abi(func throws01(_: () throws -> Void)) // expected-error {{cannot give 'throws01' the ABI of a global function which cannot throw}} +func throws01(_: () throws -> Void) throws {} + +@abi(func throws11(_: () throws -> Void) throws) +func throws11(_: () throws -> Void) throws {} + +@abi(func throws21(_: () throws -> Void) rethrows) +func throws21(_: () throws -> Void) throws {} + +@abi(func throws02(_: () throws -> Void)) // expected-error {{cannot give 'throws02' the ABI of a global function which cannot throw}} +func throws02(_: () throws -> Void) rethrows {} + +@abi(func throws12(_: () throws -> Void) throws) +func throws12(_: () throws -> Void) rethrows {} + +@abi(func throws22(_: () throws -> Void) rethrows) +func throws22(_: () throws -> Void) rethrows {} + +// +// Async effect checking +// + +@abi(func async00()) +func async00() {} + +@abi(func async10() async) // expected-error {{cannot give 'async10()' the ABI of an async global function}} +func async10() {} + +@abi(func async01()) // expected-error {{cannot give 'async01()' the ABI of a non-async global function}} +func async01() async {} + +@abi(func async11() async) +func async11() async {} + +// +// Miscellaneous function checking +// + +@_silgen_name("conflictingAttrsSilgenName") +@abi(func conflictingAttrsABI()) +func conflictingAttrsAPI() {} // expected-error@-2 {{cannot use '@_silgen_name' and '@abi' on the same global function because they serve the same purpose}} {{1-44=}} + +// +// PBD shape checking +// + +@abi(var x1, y1: Int) // expected-error {{cannot give pattern binding the ABI of a binding with more patterns}} +var x1: Int = 0 + +@abi(var x2: Int) +var x2 = 0, y2: Int = 0 // expected-error {{cannot give pattern binding the ABI of a binding with fewer patterns}} + +@abi(var (x3, y3): (Int, Int), (a3, b3): (Int, Int)) // expected-error {{no match for ABI var 'b3'}} +var (x3, y3): (Int, Int) = (0, 0), a3: Int = 0 + +@abi(var (x4, y4): (Int, Int), a4: Int) +var (x4, y4): (Int, Int) = (0, 0), (a4, b4): (Int, Int) = (0, 0) // expected-error {{no match for var 'b4' in the ABI}} + +// +// Conflict diagnostics +// + +@abi(func noConflictWithSelf()) +func noConflictWithSelf() {} + +@abi(func noConflictWithMutualChanges1()) +func noConflictWithMutualChanges2() {} + +@abi(func noConflictWithMutualChanges2()) +func noConflictWithMutualChanges1() {} + +@abi(func conflictsWithOtherABI()) // expected-note {{'conflictsWithOtherABI()' previously declared here}} +func conflictsWithOtherABI1() {} + +@abi(func conflictsWithOtherABI()) // expected-error {{invalid redeclaration of 'conflictsWithOtherABI()'}} +func conflictsWithOtherABI2() {} + +@abi(func conflictsWithNonABI()) // expected-error {{invalid redeclaration of 'conflictsWithNonABI()'}} +func conflictsWithNonABI_formal() {} + +func conflictsWithNonABI() {} // expected-note {{'conflictsWithNonABI()' previously declared here}} + +@abi(var var_noConflictWithSelf: Int) +var var_noConflictWithSelf: Int = 0 + +@abi(var var_noConflictWithMutualChanges1: Int) +var var_noConflictWithMutualChanges2: Int = 0 + +@abi(var var_noConflictWithMutualChanges2: Int) +var var_noConflictWithMutualChanges1: Int = 0 + +@abi(var var_conflictsWithOtherABI: Int) // expected-note {{'var_conflictsWithOtherABI' previously declared here}} +var var_conflictsWithOtherABI1: Int = 0 + +@abi(var var_conflictsWithOtherABI: Int) // expected-error {{invalid redeclaration of 'var_conflictsWithOtherABI'}} +var var_conflictsWithOtherABI2: Int = 0 + +@abi(var var_conflictsWithNonABI: Int) // expected-error {{invalid redeclaration of 'var_conflictsWithNonABI'}} +var var_conflictsWithNonABI_formal: Int = 0 + +var var_conflictsWithNonABI: Int = 0 // expected-note {{'var_conflictsWithNonABI' previously declared here}} + +struct Foo { + @abi(func noConflictWithSelf()) + func noConflictWithSelf() {} + + @abi(func noConflictWithMutualChanges1()) + func noConflictWithMutualChanges2() {} + + @abi(func noConflictWithMutualChanges2()) + func noConflictWithMutualChanges1() {} + + @abi(func conflictsWithOtherABI()) // expected-note {{'conflictsWithOtherABI()' previously declared here}} + func conflictsWithOtherABI1() {} + + @abi(func conflictsWithOtherABI()) // expected-error {{invalid redeclaration of 'conflictsWithOtherABI()'}} + func conflictsWithOtherABI2() {} + + @abi(func conflictsWithNonABI()) // expected-error {{invalid redeclaration of 'conflictsWithNonABI()'}} + func conflictsWithNonABI_formal() {} + + func conflictsWithNonABI() {} // expected-note {{'conflictsWithNonABI()' previously declared here}} + + @abi(var var_noConflictWithSelf: Int) + var var_noConflictWithSelf: Int = 0 + + @abi(var var_noConflictWithMutualChanges1: Int) + var var_noConflictWithMutualChanges2: Int = 0 + + @abi(var var_noConflictWithMutualChanges2: Int) + var var_noConflictWithMutualChanges1: Int = 0 + + @abi(var var_conflictsWithOtherABI: Int) // expected-note {{'var_conflictsWithOtherABI' previously declared here}} + var var_conflictsWithOtherABI1: Int = 0 + + @abi(var var_conflictsWithOtherABI: Int) // expected-error {{invalid redeclaration of 'var_conflictsWithOtherABI'}} + var var_conflictsWithOtherABI2: Int = 0 + + @abi(var var_conflictsWithNonABI: Int) // expected-error {{invalid redeclaration of 'var_conflictsWithNonABI'}} + var var_conflictsWithNonABI_formal: Int = 0 + + var var_conflictsWithNonABI: Int = 0 // expected-note {{'var_conflictsWithNonABI' previously declared here}} +} + +func fn() { + // TODO: Figure out if @abi makes sense in local scope and, if so, when we + // should complain about redecls. + + @abi(func noConflictWithSelf()) + func noConflictWithSelf() {} + + @abi(func noConflictWithMutualChanges1()) + func noConflictWithMutualChanges2() {} + + @abi(func noConflictWithMutualChanges2()) + func noConflictWithMutualChanges1() {} + + @abi(func conflictsWithOtherABI()) // expected-note {{'conflictsWithOtherABI()' previously declared here}} + func conflictsWithOtherABI1() {} + + @abi(func conflictsWithOtherABI()) // expected-error {{invalid redeclaration of 'conflictsWithOtherABI()'}} + func conflictsWithOtherABI2() {} + + @abi(func conflictsWithNonABI()) // expected-error {{invalid redeclaration of 'conflictsWithNonABI()'}} + func conflictsWithNonABI_formal() {} + + func conflictsWithNonABI() {} // expected-note {{'conflictsWithNonABI()' previously declared here}} + + var a = 1 // expected-note {{'a' previously declared here}} + var a = 1 // expected-error {{invalid redeclaration of 'a'}} + + @abi(var var_noConflictWithSelf: Int) + var var_noConflictWithSelf: Int = 0 + + @abi(var var_noConflictWithMutualChanges1: Int) + var var_noConflictWithMutualChanges2: Int = 0 + + @abi(var var_noConflictWithMutualChanges2: Int) + var var_noConflictWithMutualChanges1: Int = 0 + + @abi(var var_conflictsWithOtherABI: Int) // expected-note {{'var_conflictsWithOtherABI' previously declared here}} + var var_conflictsWithOtherABI1: Int = 0 + + @abi(var var_conflictsWithOtherABI: Int) // expected-error {{invalid redeclaration of 'var_conflictsWithOtherABI'}} + var var_conflictsWithOtherABI2: Int = 0 + + // Diagnosed in the opposite order from usual due to being in a local scope where visibility may be limited: + @abi(var var_conflictsWithNonABI: Int) // expected-note {{'var_conflictsWithNonABI' previously declared here}} + var var_conflictsWithNonABI_formal: Int = 0 + + var var_conflictsWithNonABI: Int = 0 // expected-error {{invalid redeclaration of 'var_conflictsWithNonABI'}} +} + +// +// Incorrect usage +// + +@abi(func bar()) // expected-note {{attribute already specified here}} +@abi(func foo()) // expected-error {{duplicate attribute}} +func duplicateABIAttr() {} + +// Test parser recovery by having something that +// should parse fine. +func somethingThatShouldParseFine() {} + +func func_with_nested_abi() { + @abi(func _exit(_ code: UInt32) -> Void) + func exit(_ code : UInt32) -> Void {} + exit(0) +} + +@abi(var x = 1) // expected-error {{initial value is not allowed here}} expected-error {{type annotation missing in pattern}} +var x = 1 + +// +// Examples of expected use cases +// + +@abi(func originallySendable() -> @Sendable () -> Void) +func originallySendable() -> sending () -> Void { fatalError() } + +@abi(func originallyGenericSendable() -> T) +func originallyGenericSendable() -> sending T { fatalError() } + +@abi(func originallyAnySendable() -> any Sendable) +func originallyAnySendable() -> sending Any { fatalError() } + +@abi(nonisolated func explicitIsolationChanged() -> @Sendable () -> Void) +@MainActor func explicitIsolationChanged() -> sending () -> Void { fatalError() } diff --git a/test/attr/feature_requirement.swift b/test/attr/feature_requirement.swift new file mode 100644 index 0000000000000..f568a707b91ad --- /dev/null +++ b/test/attr/feature_requirement.swift @@ -0,0 +1,16 @@ +// RUN: %target-typecheck-verify-swift -parse-as-library -disable-experimental-parser-round-trip -verify-additional-prefix disabled- +// RUN: %target-typecheck-verify-swift -parse-as-library -verify-additional-prefix enabled- -enable-experimental-feature ABIAttribute + +// REQUIRES: asserts + +// This test checks whether DECL_ATTR_FEATURE_REQUIREMENT is being applied correctly. +// It is expected to need occasional edits as experimental features are stabilized. + +@abi(func fn()) +func fn() {} // expected-disabled-error@-1 {{'abi' attribute is only valid when experimental feature ABIAttribute is enabled}} + +#if hasAttribute(abi) + #error("does have @abi") // expected-enabled-error {{does have @abi}} +#else + #error("doesn't have @abi") // expected-disabled-error {{doesn't have @abi}} +#endif diff --git a/test/type/parameterized_protocol.swift b/test/type/parameterized_protocol.swift index a52027b18a4d5..83f7a74ca4d9c 100644 --- a/test/type/parameterized_protocol.swift +++ b/test/type/parameterized_protocol.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift -disable-availability-checking -// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -disable-availability-checking 2>&1 | %FileCheck %s - +// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -disable-availability-checking >%t.output 2>&1 +// RUN: %FileCheck --input-file %t.output %s /// Test some invalid syntax first @@ -176,7 +176,7 @@ struct OpaqueTypes { // CHECK: Generic signature: extension Sequence { - // CHECK-LABEL: parameterized_protocol.(file).Sequence extension.doSomethingGeneric@ + // CHECK-LABEL: parameterized_protocol.(file).Sequence extension.doSomethingGeneric@ // CHECK: Generic signature: func doSomethingGeneric(_: E) {} } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp index 111e6f1b9ac5a..a6448f90a7676 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp @@ -219,6 +219,11 @@ class AnnotatingPrinter : public StreamPrinter { assert(!EntitiesStack.empty()); TextEntity Entity = std::move(EntitiesStack.back()); EntitiesStack.pop_back(); + + // We only care about API for documentation purposes. + if (!ABIRoleInfo(D).providesAPI()) + return; + unsigned EndOffset = OS.tell(); Entity.Range.Length = EndOffset - Entity.Range.Offset; if (EntitiesStack.empty()) { diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index c7be87d30625a..37df87bbdab03 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -457,6 +457,11 @@ FileCheckPath("filecheck", llvm::cl::value_desc("path"), static llvm::cl::opt SkipFileCheck("skip-filecheck", llvm::cl::desc("Skip 'FileCheck' checking"), llvm::cl::cat(Category)); +static llvm::cl::opt +FileCheckSuffix("filecheck-additional-suffix", + llvm::cl::value_desc("check-prefix-suffix"), + llvm::cl::desc("Additional suffix to add to check prefixes as an alternative"), + llvm::cl::cat(Category)); // '-code-completion' options. @@ -1537,11 +1542,32 @@ static int doBatchCodeCompletion(const CompilerInvocation &InitInvok, options::CodeCompletionToken != Token.Name) continue; + SmallVector expandedCheckPrefixes; + llvm::errs() << "----\n"; llvm::errs() << "Token: " << Token.Name << "; offset=" << Token.Offset << "; pos=" << Token.Line << ":" << Token.Column; - for (auto Prefix : Token.CheckPrefixes) { - llvm::errs() << "; check=" << Prefix; + for (auto joinedPrefix : Token.CheckPrefixes) { + if (options::FileCheckSuffix.empty()) { + // Simple case: just copy what we have + expandedCheckPrefixes.push_back(joinedPrefix.str()); + } else { + // For each comma-separated prefix, insert a variant with the suffix + // added to it: "X,Y" with suffix "_FOO" -> "X,X_FOO,Y,Y_FOO" + std::string expandedPrefix; + llvm::raw_string_ostream os(expandedPrefix); + + SmallVector splitPrefix; + joinedPrefix.split(splitPrefix, ','); + + llvm::interleaveComma(splitPrefix, os, [&](StringRef prefix) { + os << prefix << ',' << prefix << options::FileCheckSuffix; + }); + + expandedCheckPrefixes.push_back(expandedPrefix); + } + + llvm::errs() << "; check=" << expandedCheckPrefixes.back(); } llvm::errs() << "\n"; @@ -1662,7 +1688,7 @@ static int doBatchCodeCompletion(const CompilerInvocation &InitInvok, assert(!options::FileCheckPath.empty()); bool isFileCheckFailed = false; - for (auto Prefix : Token.CheckPrefixes) { + for (auto Prefix : expandedCheckPrefixes) { StringRef FileCheckArgs[] = {options::FileCheckPath, SourceFilename, "--check-prefixes", Prefix, "--input-file", resultFilename};