From cd61fd4dc5ce98c92fcbdfa9f5aaaa91373a31fa Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Fri, 20 Dec 2024 20:11:46 -0800 Subject: [PATCH 01/22] [NFC] Fix ImplementsAttr::clone() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct an issue with `ImplementsAttr` that would come up if you ever tried to clone an attribute that had been deserialized. No tests because there’s nothing in the compiler yet that might actually do so. --- include/swift/AST/Attr.h | 22 +++++++++++++++------- lib/AST/Attr.cpp | 14 +++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 2e3d66f82fda3..6f2fbd54defba 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -1768,14 +1768,16 @@ class StorageRestrictionsAttr final /// The @_implements attribute, which treats a decl as the implementation for /// some named protocol requirement (but otherwise not-visible by that name). class ImplementsAttr : public DeclAttribute { - TypeRepr *TyR; + /// If constructed by the \c create() variant with a TypeRepr, the TypeRepr; + /// if constructed by the \c create() variant with a DeclContext and + /// ProtocolDecl, the DeclContext. + llvm::PointerUnion TyROrDC; DeclName MemberName; DeclNameLoc MemberNameLoc; ImplementsAttr(SourceLoc atLoc, SourceRange Range, - TypeRepr *TyR, - DeclName MemberName, - DeclNameLoc MemberNameLoc); + llvm::PointerUnion TyROrDC, + DeclName MemberName, DeclNameLoc MemberNameLoc); public: static ImplementsAttr *create(ASTContext &Ctx, SourceLoc atLoc, @@ -1795,7 +1797,9 @@ class ImplementsAttr : public DeclAttribute { /// otherwise `nullopt`. This should only be used for dumping. std::optional getCachedProtocol(DeclContext *dc) const; - TypeRepr *getProtocolTypeRepr() const { return TyR; } + TypeRepr *getProtocolTypeRepr() const { + return TyROrDC.dyn_cast(); + } DeclName getMemberName() const { return MemberName; } DeclNameLoc getMemberNameLoc() const { return MemberNameLoc; } @@ -1806,8 +1810,12 @@ class ImplementsAttr : public DeclAttribute { /// Create a copy of this attribute. ImplementsAttr *clone(ASTContext &ctx) const { - return new (ctx) ImplementsAttr( - AtLoc, Range, TyR, getMemberName(), getMemberNameLoc()); + if (auto tyR = getProtocolTypeRepr()) { + return create(ctx, AtLoc, Range, tyR, getMemberName(), + getMemberNameLoc()); + } + auto dc = TyROrDC.dyn_cast(); + return create(dc, getProtocol(dc), getMemberName()); } }; diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index c1713a6b6278d..eac91aa303a4d 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -2712,11 +2712,12 @@ StorageRestrictionsAttr::create( /*implicit=*/false); } -ImplementsAttr::ImplementsAttr(SourceLoc atLoc, SourceRange range, - TypeRepr *TyR, DeclName MemberName, - DeclNameLoc MemberNameLoc) +ImplementsAttr:: +ImplementsAttr(SourceLoc atLoc, SourceRange range, + llvm::PointerUnion TyROrDC, + DeclName MemberName, DeclNameLoc MemberNameLoc) : DeclAttribute(DeclAttrKind::Implements, atLoc, range, /*Implicit=*/false), - TyR(TyR), MemberName(MemberName), MemberNameLoc(MemberNameLoc) {} + TyROrDC(TyROrDC), MemberName(MemberName), MemberNameLoc(MemberNameLoc) {} ImplementsAttr *ImplementsAttr::create(ASTContext &Ctx, SourceLoc atLoc, SourceRange range, @@ -2733,9 +2734,8 @@ ImplementsAttr *ImplementsAttr::create(DeclContext *DC, DeclName MemberName) { auto &ctx = DC->getASTContext(); void *mem = ctx.Allocate(sizeof(ImplementsAttr), alignof(ImplementsAttr)); - auto *attr = new (mem) ImplementsAttr( - SourceLoc(), SourceRange(), nullptr, - MemberName, DeclNameLoc()); + auto *attr = new (mem) ImplementsAttr(SourceLoc(), SourceRange(), DC, + MemberName, DeclNameLoc()); ctx.evaluator.cacheOutput(ImplementsAttrProtocolRequest{attr, DC}, std::move(Proto)); return attr; From 1af5c04765fa627d700bb4e06b5ce28dcdb3157a Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Thu, 20 Feb 2025 19:06:13 -0800 Subject: [PATCH 02/22] [NFC] Hoist getTypeSourceRangeForDiagnostics() Allows code to get this for any AbstractStorageDecl. --- include/swift/AST/Decl.h | 14 +++++++------- lib/AST/Decl.cpp | 11 ++++++++--- lib/Sema/AssociatedTypeInference.cpp | 9 +++------ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index ab3d8fde31c39..2c09987c4bca3 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5919,6 +5919,13 @@ class AbstractStorageDecl : public ValueDecl { /// Return the interface type of the stored value. Type getValueInterfaceType() const; + /// Retrieve the source range of the variable type, or an invalid range if the + /// variable's type is not explicitly written in the source. + /// + /// Only for use in diagnostics. It is not always possible to always + /// precisely point to the variable type because of type aliases. + SourceRange getTypeSourceRangeForDiagnostics() const; + /// Determine how this storage is implemented. StorageImplInfo getImplInfo() const; @@ -6353,13 +6360,6 @@ class VarDecl : public AbstractStorageDecl { /// and not just getInterfaceType(). Type getTypeInContext() const; - /// Retrieve the source range of the variable type, or an invalid range if the - /// variable's type is not explicitly written in the source. - /// - /// Only for use in diagnostics. It is not always possible to always - /// precisely point to the variable type because of type aliases. - SourceRange getTypeSourceRangeForDiagnostics() const; - /// Determine the mutability of this variable declaration when /// accessed from a given declaration context. StorageMutability mutability( diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 7953bba24d572..508d601894a3e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7874,14 +7874,19 @@ SourceRange VarDecl::getSourceRange() const { return getNameLoc(); } -SourceRange VarDecl::getTypeSourceRangeForDiagnostics() const { +SourceRange AbstractStorageDecl::getTypeSourceRangeForDiagnostics() const { + // Subscripts always have an explicitly-written type. + if (auto *SD = dyn_cast(this)) + return SD->getElementTypeSourceRange(); + // For a parameter, map back to its parameter to get the TypeLoc. if (auto *PD = dyn_cast(this)) { if (auto typeRepr = PD->getTypeRepr()) return typeRepr->getSourceRange(); } - - Pattern *Pat = getParentPattern(); + + auto *VD = cast(this); + Pattern *Pat = VD->getParentPattern(); if (!Pat || Pat->isImplicit()) return SourceRange(); diff --git a/lib/Sema/AssociatedTypeInference.cpp b/lib/Sema/AssociatedTypeInference.cpp index 31a5c3285c1f6..720343abcca7e 100644 --- a/lib/Sema/AssociatedTypeInference.cpp +++ b/lib/Sema/AssociatedTypeInference.cpp @@ -3724,15 +3724,12 @@ bool AssociatedTypeInference::diagnoseNoSolutions( failed.Result.getKind() != CheckTypeWitnessResult::Superclass) { Type resultType; SourceRange typeRange; - if (auto *var = dyn_cast(failed.Witness)) { - resultType = var->getValueInterfaceType(); - typeRange = var->getTypeSourceRangeForDiagnostics(); + if (auto *storage = dyn_cast(failed.Witness)) { + resultType = storage->getValueInterfaceType(); + typeRange = storage->getTypeSourceRangeForDiagnostics(); } else if (auto *func = dyn_cast(failed.Witness)) { resultType = func->getResultInterfaceType(); typeRange = func->getResultTypeSourceRange(); - } else if (auto *subscript = dyn_cast(failed.Witness)) { - resultType = subscript->getElementInterfaceType(); - typeRange = subscript->getElementTypeSourceRange(); } // If the type witness was inferred from an existential From d8349e72cab605e80fb6956c96ed34d0a368da0a Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Thu, 20 Feb 2025 19:06:57 -0800 Subject: [PATCH 03/22] [NFC] Expose default param specifier computation Make this logic accessible outside of the mangler. --- include/swift/AST/Types.h | 4 ++++ lib/AST/ASTMangler.cpp | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 300107268c571..73dbd7a9fdd9b 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -2378,6 +2378,10 @@ enum class ParamSpecifier : uint8_t { StringRef getNameForParamSpecifier(ParamSpecifier name); +/// What does \c ParamSpecifier::Default mean for a parameter that's directly +/// attached to \p VD ? Pass \c nullptr for the value for a closure. +ParamSpecifier getDefaultParamSpecifier(const ValueDecl *VD); + /// Provide parameter type relevant flags, i.e. variadic, autoclosure, and /// escaping. class ParameterTypeFlags { diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index de62f634dd4f3..10d11c8c36ef0 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -3379,8 +3379,7 @@ void ASTMangler::appendFunctionSignature(AnyFunctionType *fn, } } -static ParamSpecifier -getDefaultOwnership(const ValueDecl *forDecl) { +ParamSpecifier swift::getDefaultParamSpecifier(const ValueDecl *forDecl) { // `consuming` is the default ownership for initializers and setters. // Everything else defaults to borrowing. if (!forDecl) { @@ -3451,7 +3450,7 @@ getParameterFlagsForMangling(ParameterTypeFlags flags, void ASTMangler::appendFunctionInputType( AnyFunctionType *fnType, ArrayRef params, GenericSignature sig, const ValueDecl *forDecl, bool isRecursedInto) { - auto defaultSpecifier = getDefaultOwnership(forDecl); + auto defaultSpecifier = getDefaultParamSpecifier(forDecl); switch (params.size()) { case 0: From b4d545f8738bb248c47d24490f40911d90149917 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Fri, 28 Feb 2025 14:56:37 -0800 Subject: [PATCH 04/22] [NFC] Fix AllowFeatureSuppressionAttr atLoc --- lib/Parse/ParseDecl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7b770e0717c5a..f4aced389a1b9 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2189,7 +2189,7 @@ Parser::parseAllowFeatureSuppressionAttribute(bool inverted, SourceLoc atLoc, auto range = SourceRange(loc, parensRange.End); return makeParserResult(AllowFeatureSuppressionAttr::create( - Context, loc, range, /*implicit*/ false, /*inverted*/ inverted, + Context, atLoc, range, /*implicit*/ false, /*inverted*/ inverted, features)); } From 775bdacefc3141848a7bdd80728faa4bd2226c6e Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 12 Mar 2025 16:39:02 -0700 Subject: [PATCH 05/22] [NFC] Extract Decl::getExplicitObjCName() Create a helper method which looks for an `@objc` attribute with an explicit name and returns it, and adopt it in various existing places. --- include/swift/AST/Decl.h | 4 ++++ lib/AST/ASTMangler.cpp | 8 +++----- lib/AST/Decl.cpp | 13 ++++++++++--- lib/Sema/TypeCheckProtocol.cpp | 6 +----- tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp | 5 ++--- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 2c09987c4bca3..0544e49bed8b6 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1041,6 +1041,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated, public Swi /// from source code. void attachParsedAttrs(DeclAttributes attrs); + /// Retrieve the custom name in the \c @objc attribute, if present. + std::optional + getExplicitObjCName(bool allowInvalid = false) const; + /// 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. diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 10d11c8c36ef0..7af62c05439c4 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -1193,11 +1193,9 @@ getOverriddenSwiftProtocolObjCName(const ValueDecl *decl, return std::nullopt; // If there is an 'objc' attribute with a name, use that name. - if (auto objc = proto->getAttrs().getAttribute()) { - if (auto name = objc->getName()) { - llvm::SmallString<4> buffer; - return std::string(name->getString(buffer)); - } + if (auto objcName = proto->getExplicitObjCName()) { + llvm::SmallString<4> buffer; + return std::string(objcName->getString(buffer)); } return std::nullopt; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 508d601894a3e..6c19ffe971e14 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4445,6 +4445,14 @@ ValueDecl::getObjCRuntimeName(bool skipIsObjCResolution) const { return std::nullopt; } +std::optional +Decl::getExplicitObjCName(bool allowInvalid) const { + auto objcAttr = getAttrs().getAttribute(allowInvalid); + if (objcAttr && !objcAttr->isNameImplicit()) + return objcAttr->getName(); + return std::nullopt; +} + bool ValueDecl::canInferObjCFromRequirement(ValueDecl *requirement) { // Only makes sense for a requirement of an @objc protocol. auto proto = cast(requirement->getDeclContext()); @@ -4457,9 +4465,8 @@ bool ValueDecl::canInferObjCFromRequirement(ValueDecl *requirement) { // If there is already an @objc attribute with an explicit name, we // can't infer a name (it's already there). - if (auto objcAttr = getAttrs().getAttribute()) { - if (objcAttr->hasName() && !objcAttr->isNameImplicit()) - return false; + if (getExplicitObjCName().has_value()) { + return false; } // If the nominal type doesn't conform to the protocol at all, we diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 114eea73ddd5e..3df6c0611e531 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -6279,11 +6279,7 @@ static bool hasExplicitObjCName(ClassDecl *classDecl) { if (classDecl->getAttrs().hasAttribute()) return true; - auto objcAttr = classDecl->getAttrs().getAttribute(); - if (!objcAttr) - return false; - - return objcAttr->hasName() && !objcAttr->isNameImplicit(); + return classDecl->getExplicitObjCName().has_value(); } /// Check if the name of a class might be unstable, and if so, emit a diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index cb0ff4f8f47c2..6a7e6067c7c51 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -1454,9 +1454,8 @@ class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker { // We only report runtime name for classes and protocols with an explicitly // defined ObjC name, i.e. those that have @objc("SomeName") if (D && (isa(D) || isa(D))) { - auto *ObjCNameAttr = D->getAttrs().getAttribute(); - if (ObjCNameAttr && ObjCNameAttr->hasName()) - return ObjCNameAttr->getName()->getString(Buf); + if (auto objcName = D->getExplicitObjCName()) + return objcName->getString(Buf); } return StringRef(); } From 5bb6245a5ab47a5946c53f41a336ffebc0f2743c Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 12 Mar 2025 16:58:13 -0700 Subject: [PATCH 06/22] =?UTF-8?q?[NFC]=20Check=20that=20SILGen=20doesn?= =?UTF-8?q?=E2=80=99t=20use=20ABI-only=20decls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds assertions in various places where properties that can vary between ABI-only decls and their counterparts—particularly function and parameter attributes—are handled in SILGen, ensuring that we don’t accidentally end up processing ABI-only decls there. --- lib/SIL/IR/SILDeclRef.cpp | 4 ++++ lib/SIL/IR/SILFunctionBuilder.cpp | 5 +++++ lib/SIL/IR/SILSymbolVisitor.cpp | 3 +++ lib/SILGen/SILGen.cpp | 3 +++ 4 files changed, 15 insertions(+) diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 56fa11d92b8f7..c8c714fef2149 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -451,6 +451,7 @@ static LinkageLimit getLinkageLimit(SILDeclRef constant) { using Kind = SILDeclRef::Kind; auto *d = constant.getDecl(); + ASSERT(ABIRoleInfo(d).providesAPI() && "getLinkageLimit() for ABI decl?"); // Back deployment thunks and fallbacks are emitted into the client. if (constant.backDeploymentKind != SILDeclRef::BackDeploymentKind::None) @@ -1267,6 +1268,9 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { if (auto *ACE = getAbstractClosureExpr()) return mangler.mangleClosureEntity(ACE, SKind); + ASSERT(ABIRoleInfo(getDecl()).providesAPI() + && "SILDeclRef mangling ABI decl directly?"); + // As a special case, functions can have manually mangled names. // Use the SILGen name only for the original non-thunked, non-curried entry // point. diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 74647128c2872..b15f75b4645c8 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -377,6 +377,9 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction( if (auto *accessor = dyn_cast(decl)) { auto *storage = accessor->getStorage(); // Add attributes for e.g. computed properties. + ASSERT(ABIRoleInfo(storage).providesAPI() + && "addFunctionAttributes() on ABI-only accessor?"); + addFunctionAttributes(F, storage->getAttrs(), mod, getOrCreateDeclaration); @@ -395,6 +398,8 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction( F->setInlineStrategy(NoInline); } } + ASSERT(ABIRoleInfo(decl).providesAPI() + && "addFunctionAttributes() on ABI-only decl?"); addFunctionAttributes(F, decl->getAttrs(), mod, getOrCreateDeclaration, constant); } diff --git a/lib/SIL/IR/SILSymbolVisitor.cpp b/lib/SIL/IR/SILSymbolVisitor.cpp index f58c424180c53..b45b48d7202ad 100644 --- a/lib/SIL/IR/SILSymbolVisitor.cpp +++ b/lib/SIL/IR/SILSymbolVisitor.cpp @@ -491,6 +491,9 @@ class SILSymbolVisitorImpl : public ASTVisitor { addFunction(SILDeclRef(AFD)); + ASSERT(ABIRoleInfo(AFD).providesAPI() + && "SILSymbolVisitorImpl visiting ABI-only decl?"); + if (auto dynKind = getDynamicKind(AFD)) { // Add the global function pointer for a dynamically replaceable function. Visitor.addDynamicFunction(AFD, *dynKind); diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 0bd0a03e244ef..1a85e274fa077 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1439,6 +1439,9 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) { // Emit default arguments and property wrapper initializers. emitArgumentGenerators(AFD, AFD->getParameters()); + ASSERT(ABIRoleInfo(AFD).providesAPI() + && "emitAbstractFuncDecl() on ABI-only decl?"); + // If the declaration is exported as a C function, emit its native-to-foreign // thunk too, if it wasn't already forced. if (AFD->getAttrs().hasAttribute()) { From 3baba0b262e8a06d86098ad77e5b11688974373c Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 19 Mar 2025 16:51:10 -0700 Subject: [PATCH 07/22] Type check `@abi` decls (sans attrs) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit compares the decl inside the `@abi` attribute to the decl it’s attached to, diagnosing ABI-incompatible differences. It does not yet cover attributes, which are a large undertaking. --- include/swift/AST/DiagnosticEngine.h | 8 + include/swift/AST/DiagnosticsSema.def | 62 +- lib/Sema/CMakeLists.txt | 1 + lib/Sema/TypeCheckAttr.cpp | 137 +--- lib/Sema/TypeCheckAttrABI.cpp | 912 ++++++++++++++++++++++++++ lib/Sema/TypeChecker.h | 1 + test/ASTGen/attrs.swift | 2 +- test/IRGen/asmname.swift | 9 +- test/attr/attr_abi.swift | 790 +++++++++++++++++++++- 9 files changed, 1760 insertions(+), 162 deletions(-) create mode 100644 lib/Sema/TypeCheckAttrABI.cpp diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 6a4d7c2cb4262..147f8a5c1f751 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -702,6 +702,10 @@ namespace swift { /// Flush the active diagnostic to the diagnostic output engine. void flush(); + /// Returns the \c SourceManager associated with \c SourceLoc s for this + /// diagnostic. + SourceManager &getSourceManager(); + /// Prevent the diagnostic from behaving more severely than \p limit. For /// instance, if \c DiagnosticBehavior::Warning is passed, an error will be /// emitted as a warning, but a note will still be emitted as a note. @@ -1561,6 +1565,10 @@ namespace swift { } }; + inline SourceManager &InFlightDiagnostic::getSourceManager() { + return Engine->SourceMgr; + } + /// Remember details about the state of a diagnostic engine and restore them /// when the object is destroyed. /// diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 47cb892f60e7d..1bd689560e9af 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8351,16 +8351,16 @@ ERROR(attr_abi_mismatched_kind,none, ERROR(attr_abi_mismatched_arity,none, "cannot give %kind0 the ABI of a %kindonly0 with a different number of " - "low-level parameters", - (ValueDecl *)) + "%select{|generic }1parameters", + (Decl *, /*genericParams=*/bool)) ERROR(attr_abi_mismatched_throws,none, "cannot give %0 the ABI of a %kindonly0 which %select{cannot|can}1 throw", - (ValueDecl *, /*abiCanThrow=*/bool)) + (Decl *, /*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)) + (Decl *, /*abiIsAsync=*/bool)) ERROR(attr_abi_mismatched_pbd_size,none, "cannot give pattern binding the ABI of a binding with " @@ -8369,13 +8369,65 @@ ERROR(attr_abi_mismatched_pbd_size,none, ERROR(attr_abi_mismatched_var,none, "no match for %select{%kind0 in the ABI|ABI %kind0}1", - (ValueDecl *, /*isABI=*/bool)) + (Decl *, /*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)) +ERROR(attr_abi_mismatched_type,none, + "type %0 in '@abi' should match %1", + (Type, Type)) +NOTE(attr_abi_should_match_type_here,none, + "should match type here", ()) + +ERROR(attr_abi_mismatched_generic_signature,none, + "generic signature '%0' in '@abi' is not compatible with '%1'", + (StringRef, StringRef)) +ERROR(attr_abi_missing_generic_signature,none, + "declaration in '@abi' should have generic signature compatible with " + "'%0'", + (StringRef)) +ERROR(attr_abi_extra_generic_signature,none, + "declaration in '@abi' should not have generic signature because %0 " + "is not generic", + (Decl *)) + +ERROR(attr_abi_mismatched_param_modifier,none, + "%select{default |}0%3 %select{attribute|modifier}2 " + "%select{|'%0' }0in '@abi' is not compatible with %select{default|'%1'}1", + (StringRef, StringRef, /*isModifier=*/bool, DescriptiveDeclKind)) +ERROR(attr_abi_no_default_arguments,none, + "%kind0 in '@abi' should not have a default argument; it does not " + "affect the parameter's ABI", + (Decl *)) + +// These macros insert 'final', 'non-final', or nothing depending on both the +// current decl and its counterpart, such that 'non-final' is used if the +// counterpart would be described as 'final' or 'static'. They must be kept in +// sync with `StaticnessAndFinality`. +#define NONFINAL_OR_NOTHING(COUNTERPART) \ + "%select{||non-final |non-final |non-final |%error}" #COUNTERPART +#define FINAL_OR_NONFINAL_OR_NOTHING(CURRENT, COUNTERPART, FINAL_OK) \ + "%select{|%select{" NONFINAL_OR_NOTHING(COUNTERPART) \ + "|" NONFINAL_OR_NOTHING(COUNTERPART) \ + "|final |final ||%error}" #CURRENT "}" #FINAL_OK + +ERROR(attr_abi_static_final_mismatch,none, + FINAL_OR_NONFINAL_OR_NOTHING(0, 2, 4) "%kind1 in '@abi' should be " + FINAL_OR_NONFINAL_OR_NOTHING(2, 0, 4) "%kindonly3 to ensure ABI " + "compatibility", + (uint8_t, Decl *, uint8_t, Decl *, /*isClass=*/bool)) + +#undef NONFINAL_OR_NOTHING +#undef FINAL_OR_NONFINAL_OR_NOTHING + +ERROR(attr_abi_failable_mismatch,none, + "cannot give %select{non-failable|failable}1 %kind0 the ABI of a " + "%select{non-failable|failable}2 %kindonly0", + (Decl *, bool, bool)) + //===----------------------------------------------------------------------===// // MARK: Isolated conformances //===----------------------------------------------------------------------===// diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 57df045386821..103435fa20d90 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -50,6 +50,7 @@ add_swift_host_library(swiftSema STATIC TypeOfReference.cpp TypeCheckAccess.cpp TypeCheckAttr.cpp + TypeCheckAttrABI.cpp TypeCheckAvailability.cpp TypeCheckBitwise.cpp TypeCheckCaptures.cpp diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index e3fc30db24619..e97b72af81d9f 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -195,68 +195,10 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(Unsafe) #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); - } - } - } + void visitABIAttr(ABIAttr *attr) { + TypeChecker::checkDeclABIAttribute(D, attr); } -public: void visitExecutionAttr(ExecutionAttr *attr) { auto *const decl = cast(D); @@ -300,81 +242,6 @@ class AttributeChecker : public AttributeVisitor { } } - 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(); diff --git a/lib/Sema/TypeCheckAttrABI.cpp b/lib/Sema/TypeCheckAttrABI.cpp new file mode 100644 index 0000000000000..b20a499a74e3d --- /dev/null +++ b/lib/Sema/TypeCheckAttrABI.cpp @@ -0,0 +1,912 @@ +//===--- TypeCheckAttrABI.cpp - Type Checking for @abi Attribute ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements diagnostics for the @abi attribute. +/// +//===----------------------------------------------------------------------===// + +#include "TypeChecker.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Effects.h" +#include "swift/AST/DiagnosticsSema.h" +#include "swift/AST/ParameterList.h" +#include "swift/AST/Types.h" +#include "swift/Basic/Assertions.h" +#include "swift/Parse/Lexer.h" +#include + +using namespace swift; + +namespace { + +/// Like ASTVisitor, but the visit methods are passed a pair of nodes to compare. +/// Either node might be \c nil , indicating that a matching node was not found. +template +class ASTComparisonVisitor { +public: + bool visit(Decl *D1, Decl *D2) { + DeclKind kind = D1 ? D1->getKind() : D2->getKind(); + switch (kind) { +#define DECL(CLASS, PARENT) \ + case DeclKind::CLASS: \ + return static_cast(this) \ + ->visit##CLASS##Decl(static_cast(D1), \ + static_cast(D2)); +#include "swift/AST/DeclNodes.def" + } + llvm_unreachable("Not reachable, all cases handled"); + } + +#define DECL(CLASS, PARENT) \ + bool visitParentOf##CLASS##Decl(CLASS##Decl *D1, CLASS##Decl *D2) {\ + return static_cast(this)->visit##PARENT(D1, D2); \ + } +#define ABSTRACT_DECL(CLASS, PARENT) DECL(CLASS, PARENT) +#include "swift/AST/DeclNodes.def" +}; + +/// Describes the effects that have been applied to a declaration, packaging up +/// various bits of info used for \c \@abi diagnostics. +struct DeclEffects { + PossibleEffects effects; + PossibleEffects polymorphicEffects; + + SourceLoc asyncLoc; + SourceLoc throwsLoc; + + std::optional effectiveThrownType; + TypeRepr *thrownTypeRepr; + + DeclEffects(AbstractFunctionDecl *afd) + : effects(), polymorphicEffects(), + asyncLoc(afd->getAsyncLoc()), throwsLoc(afd->getThrowsLoc()), + effectiveThrownType(afd->getEffectiveThrownErrorType()), + thrownTypeRepr(afd->getThrownTypeRepr()) + { + if (afd->hasEffect(EffectKind::Async)) + effects |= EffectKind::Async; + if (afd->hasPolymorphicEffect(EffectKind::Async)) + polymorphicEffects |= EffectKind::Async; + + if (afd->hasEffect(EffectKind::Throws)) + effects |= EffectKind::Throws; + if (afd->hasPolymorphicEffect(EffectKind::Throws)) + polymorphicEffects |= EffectKind::Throws; + } + + bool anyContains(EffectKind effect) const { + return effects.contains(effect) || polymorphicEffects.contains(effect); + } +}; + +/// Emit a fix-it replacing \p charRange with \p newText , inserting or +/// removing whitespace after \c charRange in a way suitable for editing a +/// sequence of whitespce-separated keywords. +void fixItReplaceKeywords(InFlightDiagnostic &diag, + CharSourceRange charRange, + StringRef newText) { + auto &SM = diag.getSourceManager(); + auto charRangeIsFollowedByWhitespace = [&]() -> bool { + auto str = SM.extractText({ charRange.getEnd(), 1 }); + return str.empty() ? false : isspace(str.front()); + }; + + SmallString<32> scratch; + scratch += newText; + + if (newText.empty()) { + // Eat trailing whitespace. + while (charRangeIsFollowedByWhitespace()) { + charRange = { charRange.getStart(), charRange.getByteLength() + 1 }; + } + } else { + if (!charRangeIsFollowedByWhitespace()) { + scratch.push_back(' '); + } + } + + diag.fixItReplaceChars(charRange.getStart(), charRange.getEnd(), scratch); +} + +/// Emit a fix-it replacing \p range with \p newText , inserting or +/// removing whitespace after \c range in a way suitable for editing a +/// sequence of whitespce-separated keywords. +void fixItReplaceKeywords(InFlightDiagnostic &diag, + SourceRange range, + StringRef newText) { + auto &SM = diag.getSourceManager(); + auto charRange = Lexer::getCharSourceRangeFromSourceRange(SM, range); + return fixItReplaceKeywords(diag, charRange, newText); +} + +/// Get the best available \c SourceLoc representing the type in \p storage . +SourceLoc getTypeLoc(AbstractStorageDecl *storage, Decl *owner = nullptr) { + auto loc = storage->getTypeSourceRangeForDiagnostics().Start; + if (loc.isInvalid()) + loc = storage->getLoc(); + if (loc.isInvalid() && owner) + loc = owner->getLoc(); + return loc; +} + +class ABIDeclChecker : public ASTComparisonVisitor { + ASTContext &ctx; + Decl *diagnoseOnDecl; + ABIAttr *abiAttr; + + /// Used to de-duplicate short-form \c \@available attrs. See \c checkAttr() . + SmallSetVector diagnosedAvailableAttrSourceLocs; + + /// This emits a diagnostic with a fixit to remove the attribute. + template + InFlightDiagnostic diagnoseAndRemoveAttr(DeclAttribute *attr, + ArgTypes &&...Args) { + return swift::diagnoseAndRemoveAttr(diagnoseOnDecl, attr, + std::forward(Args)...); + } + +public: + ABIDeclChecker(ASTContext &ctx, Decl *diagnoseOnDecl, ABIAttr *abiAttr) + : ctx(ctx), diagnoseOnDecl(diagnoseOnDecl), abiAttr(abiAttr) {} + + // MARK: @abi checking - decls + + void check(Decl *api, Decl *abi) { + // 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. + // (And if they don't, we can't really compare them properly. + if (api->getKind() != abi->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(abiAttr, diag::attr_abi_mismatched_kind, + api, abi->getDescriptiveKind()); + return; + } + + visit(api, abi); + } + + bool checkParameterFlags(ParameterTypeFlags api, ParameterTypeFlags abi, + ParameterTypeFlags apiOrig, + ParameterTypeFlags abiOrig, + Type apiType, Type abiType, + SourceLoc apiTypeLoc, SourceLoc abiTypeLoc, + DescriptiveDeclKind declKind, + bool isSelfParam) { + bool didDiagnose = false; + + auto noteShouldMatch = [&](bool isModifier) { + ctx.Diags.diagnose(apiTypeLoc, diag::attr_abi_should_match_type_here); + }; + + // These assertions represent values that should have been normalized. + ASSERT(!api.isVariadic() && !abi.isVariadic()); + ASSERT(!api.isAutoClosure() && !abi.isAutoClosure()); + ASSERT(!api.isNonEphemeral() && !abi.isNonEphemeral()); + ASSERT(!api.isIsolated() && !abi.isIsolated()); + ASSERT(!api.isSending() && !abi.isSending()); + ASSERT(!api.isCompileTimeLiteral() && !abi.isCompileTimeLiteral()); + + if (api.getOwnershipSpecifier() != abi.getOwnershipSpecifier()) { + auto getSpelling = [=](ParamSpecifier spec) -> StringRef { + // Customize a couple of names to match what the developer would + // actually write. + if (spec == ParamSpecifier::Default) + return ""; + if (spec == ParamSpecifier::InOut && isSelfParam) + return "mutating"; + if (spec == ParamSpecifier::LegacyOwned && isSelfParam) + return "__consuming"; + if (spec == ParamSpecifier::ImplicitlyCopyableConsuming) + return "sending"; + return getNameForParamSpecifier(spec); + }; + + ctx.Diags.diagnose(abiTypeLoc, diag::attr_abi_mismatched_param_modifier, + getSpelling(abiOrig.getOwnershipSpecifier()), + getSpelling(apiOrig.getOwnershipSpecifier()), + /*isModifier=*/true, declKind); + noteShouldMatch(/*isModifier=*/true); + didDiagnose = true; + } + + if (api.isNoDerivative() != abi.isNoDerivative()) { + ctx.Diags.diagnose(abiTypeLoc, diag::attr_abi_mismatched_param_modifier, + abiOrig.isNoDerivative() ? "noDerivative" : "", + apiOrig.isNoDerivative() ? "noDerivative" : "", + /*isModifier=*/false, declKind); + noteShouldMatch(/*isModifier=*/false); + didDiagnose = true; + } + + if (api.isAddressable() != abi.isAddressable()) { + StringRef spelling = isSelfParam ? "_addressableSelf" : "_addressable"; + ctx.Diags.diagnose(abiTypeLoc, diag::attr_abi_mismatched_param_modifier, + abiOrig.isAddressable() ? spelling : "", + apiOrig.isAddressable() ? spelling : "", + /*isModifier=*/false, declKind); + noteShouldMatch(/*isModifier=*/false); + didDiagnose = true; + } + + if (!didDiagnose && api != abi) { + // Flag difference not otherwise diagnosed. This is a fallback diagnostic. + ctx.Diags.diagnose(abiTypeLoc, diag::attr_abi_mismatched_type, + abiType, apiType); + ctx.Diags.diagnose(apiTypeLoc, diag::attr_abi_should_match_type_here); + didDiagnose = true; + } + + return didDiagnose; + } + + bool checkParameter(ParamDecl *api, ParamDecl *abi, + ValueDecl *apiDecl, ValueDecl *abiDecl) { + ASSERT(api && abi); + + bool didDiagnose = false; + if (auto defaultExpr = abi->getStructuralDefaultExpr()) { + // Forbidden. + ctx.Diags.diagnose(defaultExpr->getLoc(), + diag::attr_abi_no_default_arguments, abi); + // TODO: Fix removing default arg (requires SourceLoc for equal sign) + + // Don't return immediately because we can independently check the type. + didDiagnose = true; + } + + auto apiOrig = api->toFunctionParam(); + auto abiOrig = abi->toFunctionParam(); + // FIXME: Do `self` params have the same default param specifier behavior? + auto apiNorm = normalizeParam(apiOrig, apiDecl); + auto abiNorm = normalizeParam(abiOrig, abiDecl); + + // FIXME: Refine to point to specific modifiers where possible. + SourceLoc apiTypeLoc = getTypeLoc(api, apiDecl); + SourceLoc abiTypeLoc = getTypeLoc(abi, abiDecl); + + didDiagnose |= checkType(apiNorm.getPlainType(), abiNorm.getPlainType(), + apiTypeLoc, abiTypeLoc); + + auto declKind = api->isSelfParameter() ? apiDecl->getDescriptiveKind() + : DescriptiveDeclKind::Param; + + didDiagnose |= checkParameterFlags(apiNorm.getParameterFlags(), + abiNorm.getParameterFlags(), + apiOrig.getParameterFlags(), + abiOrig.getParameterFlags(), + apiNorm.getPlainType(), + abiNorm.getPlainType(), + apiTypeLoc, abiTypeLoc, + declKind, api->isSelfParameter()); + + didDiagnose |= checkAttrs(api->getAttrs(), abi->getAttrs(), api, abi); + + return didDiagnose; + } + + bool checkParameterList(ParameterList *api, ParameterList *abi, + ValueDecl *apiDecl, ValueDecl *abiDecl) { + // Do the declarations have the same number of parameters? + if (api->size() != abi->size()) { + diagnoseAndRemoveAttr(abiAttr, diag::attr_abi_mismatched_arity, apiDecl, + /*genericParams=*/false); + return true; + } + + bool didDiagnose = false; + + for (auto pair : llvm::zip(*api, *abi)) { + didDiagnose |= checkParameter(std::get<0>(pair), std::get<1>(pair), + apiDecl, abiDecl); + } + + return didDiagnose; + } + + bool checkImplicitSelfParam(ParamDecl *api, ParamDecl *abi, + ValueDecl *apiDecl, ValueDecl *abiDecl) { + if (!api && !abi) + // Nothing to check + return false; + + if ((api && !abi) || (!api && abi)) { + diagnoseAndRemoveAttr(abiAttr, diag::attr_abi_mismatched_arity, + apiDecl, /*genericParams=*/false); + return true; + } + + return checkParameter(api, abi, apiDecl, abiDecl); + } + + bool checkGenericSignature(GenericSignature api, GenericSignature abi, + Decl *apiDecl, Decl *abiDecl) { + if (api.isNull() && abi.isNull()) + return false; + + if (api.isNull()) { + abiDecl->diagnose(diag::attr_abi_extra_generic_signature, apiDecl); + return true; + } + + if (abi.isNull()) { + abiDecl->diagnose(diag::attr_abi_missing_generic_signature, + api.getAsString()); + return true; + } + + auto apiNorm = normalizeGenericSignature(api); + auto abiNorm = normalizeGenericSignature(abi); + + if (!apiNorm->isEqual(abiNorm)) { + abiDecl->diagnose(diag::attr_abi_mismatched_generic_signature, + abi.getAsString(), api.getAsString()); + apiDecl->diagnose(diag::attr_abi_should_match_type_here); + return true; + } + + return false; + } + + bool checkEffects(DeclEffects api, DeclEffects abi, Decl *apiDecl, + Decl *abiDecl) { + bool didDiagnose = false; + // 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. + if (api.anyContains(EffectKind::Throws) != abi.anyContains(EffectKind::Throws)) { + diagnoseAndRemoveAttr(abiAttr, diag::attr_abi_mismatched_throws, + apiDecl, /*abiCanThrow=*/abi.anyContains(EffectKind::Throws)); + didDiagnose = true; + // FIXME: Typed throws? + } + + // Do the declarations match in async-ness? + if (api.anyContains(EffectKind::Async) != abi.anyContains(EffectKind::Async)) { + diagnoseAndRemoveAttr(abiAttr, diag::attr_abi_mismatched_async, + apiDecl, /*abiHasAsync=*/abi.anyContains(EffectKind::Async)); + didDiagnose = true; + } + + return didDiagnose; + } + + bool checkFailable(ConstructorDecl *api, ConstructorDecl *abi) { + if (api->isFailable() == abi->isFailable()) { + return false; + } + + auto diag = ctx.Diags.diagnose(abiAttr->getLocation(), + diag::attr_abi_failable_mismatch, api, + api->isFailable(), abi->isFailable()); + + if (api->isFailable()) + diag.fixItInsertAfter(abi->getLoc(), + api->isImplicitlyUnwrappedOptional() ? "!" : "?"); + else + diag.fixItRemove(abi->getFailabilityLoc()); + + return true; + } + + bool checkStaticAndFinal(ValueDecl *api, ValueDecl *abi) { + // The `static`, `class`, and `final` keywords all need to be evaluated + // together because of their intertwined semantics. This pile of code + // diagnoses errors in either or both. + + /// Returns the type and location of the declaration's `static` or `class` keyword, if any. + auto getStaticSpelling = [](Decl *decl) -> Located { + if (auto var = dyn_cast(decl)) + decl = var->getParentPatternBinding(); + + if (auto pbd = dyn_cast(decl)) + return { pbd->getStaticSpelling(), pbd->getStaticLoc() }; + + if (auto subscript = dyn_cast(decl)) + return { subscript->getStaticSpelling(), subscript->getStaticLoc() }; + + if (auto func = dyn_cast(decl)) + return { func->getStaticSpelling(), func->getStaticLoc() }; + + return { StaticSpellingKind::None, SourceLoc() }; + }; + + /// Represents the combination of `class`, `static`, and `final` keywords + /// for a given declaration. + enum class StaticnessAndFinality : uint8_t { + InstanceAndOverridable, + ClassAndOverridable, + InstanceAndFinal, + ClassAndFinal, + Static, + }; + + /// Returns a `StaticnessAndFinality` corresponding to the given values. + auto getStaticnessAndFinality = [](StaticSpellingKind staticSpelling, + bool isFinal) { + switch (staticSpelling) { + case StaticSpellingKind::None: + return isFinal ? StaticnessAndFinality::InstanceAndFinal + : StaticnessAndFinality::InstanceAndOverridable; + + case StaticSpellingKind::KeywordStatic: + return StaticnessAndFinality::Static; + + case StaticSpellingKind::KeywordClass: + return isFinal ? StaticnessAndFinality::ClassAndFinal + : StaticnessAndFinality::ClassAndOverridable; + } + + llvm_unreachable("unknown StaticSpellingKind"); + }; + + auto apiSAF = getStaticnessAndFinality(getStaticSpelling(api).Item, + api->isFinal()); + + auto abiStaticSpelling = getStaticSpelling(abi); + auto abiSAF = getStaticnessAndFinality(abiStaticSpelling.Item, + abi->isFinal()); + + /// Collapses down the difference between `Static` and `ClassAndFinal`. + auto getSemantics = [](StaticnessAndFinality syntax) { + if (syntax == StaticnessAndFinality::ClassAndFinal) + return StaticnessAndFinality::Static; + return syntax; + }; + + if (getSemantics(apiSAF) != getSemantics(abiSAF)) { + auto diag = abi->diagnose(diag::attr_abi_static_final_mismatch, + uint8_t(abiSAF), abi, uint8_t(apiSAF), api, + api->getDeclContext()->getSelfClassDecl()); + + SourceLoc insertLoc = abi->getAttributeInsertionLoc(/*forModifier=*/true); + SourceLoc replaceLoc = abiStaticSpelling.Loc; + SourceLoc deleteLoc; + + // If there's a (non-implicit) `final`, we may want to fix it. + auto finalAttr = abi->getAttrs().getAttribute(); + if (finalAttr && !finalAttr->isImplicit()) + deleteLoc = finalAttr->getLocation(); + + // If only one is valid, that should be `replaceLoc`; if both are valid, + // `replaceLoc` should come before `deleteLoc`. + if (deleteLoc.isValid() && (replaceLoc.isInvalid() || + ctx.SourceMgr.isBefore(deleteLoc, replaceLoc))) + std::swap(deleteLoc, replaceLoc); + + // Delete the keyword at `deleteLoc`, if there is one. + if (deleteLoc.isValid()) + fixItReplaceKeywords(diag, deleteLoc, ""); + + StringRef spellings[] = { "", "class", "final", "final class", "static" }; + auto newKeywords = spellings[uint8_t(apiSAF)]; + + // Either replace the first keyword, or insert new keywords. + if (replaceLoc.isValid()) + fixItReplaceKeywords(diag, replaceLoc, newKeywords); + else + fixItReplaceKeywords(diag, CharSourceRange(insertLoc, 0), newKeywords); + + return true; + } + + return false; + } + + /// This declaration should not be in an `@abi` attribute. +#define UNSUPPORTED_DECL(NAME) \ + bool visit##NAME##Decl(NAME##Decl *api, NAME##Decl *abi) { \ + return visitParentOf##NAME##Decl(api, abi); \ + } + + /// This declaration has no additional validation logic. +#define PASSTHROUGH_DECL(NAME) \ + bool visit##NAME##Decl(NAME##Decl *api, NAME##Decl *abi) { \ + return visitParentOf##NAME##Decl(api, abi); \ + } + + bool visitDecl(Decl *api, Decl *abi) { + bool didDiagnose = checkAttrs(api->getAttrs(), abi->getAttrs(), api, abi); + + if (auto apiGenericCtx = api->getAsGenericContext()) { + auto abiGenericCtx = abi->getAsGenericContext(); + didDiagnose |= checkGenericSignature(apiGenericCtx->getGenericSignature(), + abiGenericCtx->getGenericSignature(), + api, abi); + } + + return didDiagnose; + } + + bool visitValueDecl(ValueDecl *api, ValueDecl *abi) { + if (visitParentOfValueDecl(api, abi)) + return true; + + return checkStaticAndFinal(api, abi); + } + + PASSTHROUGH_DECL(Type) + PASSTHROUGH_DECL(GenericType) + PASSTHROUGH_DECL(NominalType) + PASSTHROUGH_DECL(Operator) + + UNSUPPORTED_DECL(Enum) + UNSUPPORTED_DECL(Struct) + UNSUPPORTED_DECL(Class) + + // TODO: When supported, diagnose if Protocol::isMarkerProtocol() + // (mangler can't handle invertible protocols with @abi) + UNSUPPORTED_DECL(Protocol) + + UNSUPPORTED_DECL(BuiltinTuple) + UNSUPPORTED_DECL(OpaqueType) + UNSUPPORTED_DECL(TypeAlias) + UNSUPPORTED_DECL(GenericTypeParam) + UNSUPPORTED_DECL(AssociatedType) + UNSUPPORTED_DECL(Module) + UNSUPPORTED_DECL(Param) + UNSUPPORTED_DECL(Destructor) + UNSUPPORTED_DECL(Macro) + UNSUPPORTED_DECL(EnumElement) + UNSUPPORTED_DECL(Extension) + UNSUPPORTED_DECL(TopLevelCode) + UNSUPPORTED_DECL(Import) + UNSUPPORTED_DECL(PrecedenceGroup) + UNSUPPORTED_DECL(Missing) + UNSUPPORTED_DECL(MissingMember) + UNSUPPORTED_DECL(PatternBinding) + UNSUPPORTED_DECL(EnumCase) + UNSUPPORTED_DECL(Accessor) + UNSUPPORTED_DECL(InfixOperator) + UNSUPPORTED_DECL(PrefixOperator) + UNSUPPORTED_DECL(PostfixOperator) + UNSUPPORTED_DECL(MacroExpansion) + + bool visitAbstractFunctionDecl(AbstractFunctionDecl *api, + AbstractFunctionDecl *abi) { + if (visitParentOfAbstractFunctionDecl(api, abi)) + return true; + + // FIXME: How much should we diagnose in IRGen for more precise ABI info? + + if (checkImplicitSelfParam(api->getImplicitSelfDecl(), + abi->getImplicitSelfDecl(), + api, abi)) + return true; + + if (checkParameterList(api->getParameters(), abi->getParameters(), + api, abi)) + return true; + + return checkEffects(DeclEffects(api), DeclEffects(abi), api, abi); + // NOTE: Does not check result type--that's the subclass's responsibility! + } + + bool visitFuncDecl(FuncDecl *api, FuncDecl *abi) { + if (visitParentOfFuncDecl(api, abi)) + return true; + + // Intentionally ignoring `hasSendingResult()` because it doesn't affect + // calling convention. + + return checkType(api->getResultInterfaceType(), + abi->getResultInterfaceType(), + api->getResultTypeSourceRange().Start, + abi->getResultTypeSourceRange().Start); + } + + bool visitConstructorDecl(ConstructorDecl *api, ConstructorDecl *abi) { + if (visitParentOfConstructorDecl(api, abi)) + return true; + + return checkFailable(api, abi); + } + + bool visitAbstractStorageDecl(AbstractStorageDecl *api, + AbstractStorageDecl *abi) { + if (visitParentOfAbstractStorageDecl(api, abi)) + return true; + + if (checkType(api->getValueInterfaceType(), abi->getValueInterfaceType(), + getTypeLoc(api), getTypeLoc(abi))) + return true; + + return false; + } + + bool visitVarDecl(VarDecl *api, VarDecl *abi) { + if (visitParentOfVarDecl(api, abi)) + return true; + return false; + } + + bool visitSubscriptDecl(SubscriptDecl *api, SubscriptDecl *abi) { + if (visitParentOfSubscriptDecl(api, abi)) + return true; + + if (checkParameterList(api->getIndices(), abi->getIndices(), api, abi)) + return true; + + return false; + } + +#undef UNSUPPORTED_DECL +#undef PASSTHROUGH_DECL + + // MARK: @abi checking - attributes + + bool checkAttrs(DeclAttributes api, DeclAttributes abi, + Decl *apiDecl, Decl *abiDecl) { + bool didDiagnose = false; + // TODO: Actually check attributes + return didDiagnose; + } + + // MARK: @abi checking - types + + bool checkType(Type api, Type abi, SourceLoc apiLoc, SourceLoc abiLoc) { + if (!api.isNull() && !abi.isNull()) { + Type apiNorm = normalizeType(api); + Type abiNorm = normalizeType(abi); + if (apiNorm->isEqual(abiNorm)) { + return false; + } + } + + ctx.Diags.diagnose(abiLoc, diag::attr_abi_mismatched_type, abi, api); + ctx.Diags.diagnose(apiLoc, diag::attr_abi_should_match_type_here); + return true; + } + + /// Fold away details of \p original that do not affect the calling + /// conventions used for this type. + static Type normalizeType(Type original) { + return original.transformRec(&tryNormalizeOutermostType); + } + + /// Fold away details of \p original that do not affect the calling + /// conventions used for this parameter. Does \em not fully normalize + /// \c original.getPlainType() , though it may slightly modify it. + /// Pass \c nullptr to \p forDecl for a parameter belonging to a closure. + static AnyFunctionType::Param + normalizeParam(const AnyFunctionType::Param &original, ValueDecl *forDecl) { + Type ty = original.getPlainType(); + auto flags = original.getParameterFlags(); + + // We will smash away (non-parameter pack) variadics; turn the type into + // an array. + if (flags.isVariadic()) { + ty = original.getParameterType(); + } + + // Flatten ownership information down to consume/borrow/inout, which are the + // only distinctions that matter for calling conventions and memory + // management. This removes the distinction between e.g. `__owned` and + // `consuming`, or between the declaration's default parameter ownership + // convention and an explicit equivalent. + auto ownership = normalizeOwnership(flags.getOwnershipSpecifier(), + forDecl); + + // Eliminate flags with no effect on the calling convention. + flags = flags + .withVariadic(false) + .withCompileTimeLiteral(false) + .withAutoClosure(false) + .withNonEphemeral(false) + .withIsolated(false) + .withSending(false) + .withOwnershipSpecifier(ownership); + + return AnyFunctionType::Param(ty, Identifier(), flags, Identifier()); + } + + /// Folds away \p original to one of \c Consuming , \c Borrowing , or + /// \c InOut , which are the only parameter ownership behaviors relevant to ABI. + /// Pass \c nullptr to \p forDecl for a parameter belonging to a closure. + static ParamSpecifier normalizeOwnership(ParamSpecifier original, + ValueDecl *forDecl) { + switch (original) { + case ParamSpecifier::Default: + return getDefaultParamSpecifier(forDecl); + break; + case swift::ParamSpecifier::InOut: + return ParamSpecifier::InOut; + break; + case ParamSpecifier::Borrowing: + case ParamSpecifier::LegacyShared: + return ParamSpecifier::Borrowing; + break; + case swift::ParamSpecifier::Consuming: + case swift::ParamSpecifier::LegacyOwned: + case swift::ParamSpecifier::ImplicitlyCopyableConsuming: + return ParamSpecifier::Consuming; + break; + } + } + + static GenericSignature normalizeGenericSignature(GenericSignature original) { + // FIXME: Are there other ABI-tolerable generic signature differences? + return original.withoutMarkerProtocols(); + } + + static + FunctionTypeIsolation normalizeIsolation(FunctionTypeIsolation original) { + // Isolation doesn't affect the ABI, except that `@isolated(any)` (a.k.a. + // `FunctionTypeIsolation::Kind::Erased`) has a different ABI from all the + // others. See docs/SIL/Types.td for details. + return original.isErased() ? FunctionTypeIsolation::forErased() + : FunctionTypeIsolation::forNonIsolated(); + } + + static std::optional tryNormalizeOutermostType(TypeBase *original) { + // Function types: Eliminate anything that doesn't affect calling convention. + if (auto func = original->getAs()) { + // Ignore `@escaping`, `@Sendable`, `sending` on result, and most + // isolation; they have no ABI effect. + auto normalizedExt = func->getExtInfo().intoBuilder() + .withNoEscape(false) + .withSendable(false) + .withSendingResult(false) + .withIsolation(normalizeIsolation(func->getIsolation())) + .build(); + + // Ignore ignorable parts of parameters. + SmallVector normalizedParams; + for (auto param : func->getParams()) { + auto normalizedParam = normalizeParam(param, /*forDecl=*/nullptr); + normalizedParam = normalizedParam.withType( + normalizeType(normalizedParam.getPlainType())); + normalizedParams.push_back(normalizedParam); + } + + if (isa(func)) + return FunctionType::get(normalizedParams, + normalizeType(func->getResult()), + normalizedExt); + + ASSERT(isa(func)); + + // Ignore ignorable parts of the generic signature. + auto sig = original->getAs()->getGenericSignature(); + return GenericFunctionType::get(normalizeGenericSignature(sig), + normalizedParams, + normalizeType(func->getResult()), + normalizedExt); + } + + // Protocol-related types: Remove marker protocols. + if (auto comp = original->getAs()) { + auto normalized = comp->withoutMarkerProtocols(); + if (!normalized->isEqual(comp)) + return normalized; + } + + if (auto proto = original->getAs()) { + if (proto->getDecl()->isMarkerProtocol()) + return proto->getASTContext().TheAnyType; + } + + if (auto existential = original->getAs()) { + // Pull out the constraint and see how it'll normalize. + auto normConstraint = normalizeType(existential->getConstraintType()); + + // If the constraint is no longer existential, pull it out of the type. + if (!normConstraint->isExistentialType()) + return normConstraint; + } + + // Tuples: Remove labels. + if (auto tuple = original->getAs()) { + bool needsNormalization = false; + SmallVector unlabeledElements; + + for (auto elem : tuple->getElements()) { + needsNormalization |= !elem.getName().empty(); + unlabeledElements.push_back(elem.getWithoutName()); + } + + if (!needsNormalization) + return tuple; + + return TupleType::get(unlabeledElements, tuple->getASTContext()); + } + + // TODO: Allow Optional/non-Optional variance when ABI-compatible? + // TODO: Allow variance in exact class of object? + + return std::nullopt; + } +}; + +void checkABIAttrPBD(PatternBindingDecl *APBD, VarDecl *VD) { + auto &diags = VD->getASTContext().Diags; + 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()) { + diags.diagnose(APBD->getPattern(PBD->getNumPatternEntries())->getLoc(), + diag::attr_abi_mismatched_pbd_size, /*abiHasExtra=*/false); + return; + } + if (PBD->getNumPatternEntries() > APBD->getNumPatternEntries()) { + diags.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); + } + } + } +} +} // end anonymous namespace + +void TypeChecker::checkDeclABIAttribute(Decl *D, 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; + + // Apply more precise checks. + ABIDeclChecker(D->getASTContext(), D, attr).check(D, AD); +} diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index e79d4247f1a9e..cb14ea0bf1b93 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -506,6 +506,7 @@ void typeCheckDecl(Decl *D); void addImplicitDynamicAttribute(Decl *D); void checkDeclAttributes(Decl *D); +void checkDeclABIAttribute(Decl *apiDecl, ABIAttr *abiAttr); void checkClosureAttributes(ClosureExpr *closure); void checkParameterList(ParameterList *params, DeclContext *owner); diff --git a/test/ASTGen/attrs.swift b/test/ASTGen/attrs.swift index d55a408c48d46..307dd7770c3b1 100644 --- a/test/ASTGen/attrs.swift +++ b/test/ASTGen/attrs.swift @@ -94,7 +94,7 @@ 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}} -@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}} +@abi(func fn_abi()) // expected-error {{cannot give global function 'fn' the ABI of a global function with a different number of parameters}} func fn(_: Int) {} @_alignment(8) struct AnyAlignment {} diff --git a/test/IRGen/asmname.swift b/test/IRGen/asmname.swift index b86cc485723c1..5895977815e46 100644 --- a/test/IRGen/asmname.swift +++ b/test/IRGen/asmname.swift @@ -91,16 +91,21 @@ extension X { // CHECK: define internal swiftcc i64 @"$s7asmname22abi_ABIAttrPrivate_vars5Int64Vvg" // CHECK: define internal swiftcc void @"$s7asmname22abi_ABIAttrPrivate_vars5Int64Vvs" -@abi(public func abi_ABIAttrGenericNonSendableToSendable(_ value: T) -> T) +@abi(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)) +@abi(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" +@abi(var abi_ABIAttrVarEffects: Int) +public var api_ABIAttrVarEffects: Int { get async throws { fatalError() } } +// CHECK: define{{( dllexport)?}}{{( protected)?}} swifttailcc void @"$s7asmname21abi_ABIAttrVarEffectsSivg" +// CHECK: define internal swifttailcc void @"$s7asmname21abi_ABIAttrVarEffectsSivgTY0_" + extension X { // CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc { i64, i8 } @"$s7asmname1XO8abi_blahACs5Int64V_tcfC" @abi(init(abi_blah: Int64)) diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index a0f8a8e21ea8b..24351743f8a4f 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -26,10 +26,10 @@ func varForFunc() {} @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}} +@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 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}} +@abi(func param01_generic00() -> Int) // expected-error {{cannot give global function 'param01_generic00' the ABI of a global function with a different number of parameters}} func param01_generic00(_: Int) -> Int { fatalError() } @abi(func param11_generic00(_: Int) -> Int) @@ -37,30 +37,30 @@ 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}} +@abi(func param00_generic10() -> T) // expected-error {{declaration in '@abi' should not have generic signature}} 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}} +@abi(func param10_generic10(_: Int) -> T) // expected-error {{declaration in '@abi' should not have generic signature}} func param10_generic10() -> Int { fatalError() } -@abi(func param01_generic10() -> T) +@abi(func param01_generic10() -> T) // expected-error {{declaration in '@abi' should not have generic signature because 'param01_generic10' is not generic}} 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}} +@abi(func param11_generic10(_: Int) -> T) // expected-error {{declaration in '@abi' should not have generic signature because 'param11_generic10' is not generic}} 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}} +@abi(func param00_generic01() -> Int) // expected-error {{declaration in '@abi' should have generic signature compatible with ''}} func param00_generic01() -> T { fatalError() } -@abi(func param10_generic01(_: Int) -> Int) +@abi(func param10_generic01(_: Int) -> Int) // expected-error {{declaration in '@abi' should have generic signature compatible with ''}} 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}} +@abi(func param01_generic01() -> Int) // expected-error {{declaration in '@abi' should have generic signature compatible with ''}} 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}} +@abi(func param11_generic01(_: Int) -> Int) // expected-error {{declaration in '@abi' should have generic signature compatible with ''}} func param11_generic01(_: Int) -> T { fatalError() } @@ -68,10 +68,10 @@ 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}} +@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 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}} +@abi(func param01_generic11() -> T) // expected-error {{cannot give global function 'param01_generic11' the ABI of a global function with a different number of parameters}} func param01_generic11(_: Int) -> T { fatalError() } @abi(func param11_generic11(_: Int) -> T) @@ -108,6 +108,12 @@ func throws12(_: () throws -> Void) rethrows {} @abi(func throws22(_: () throws -> Void) rethrows) func throws22(_: () throws -> Void) rethrows {} +@abi(var throws00Var: Int) +var throws00Var: Int { get { fatalError() } } + +@abi(var throws11Var: Int) +var throws11Var: Int { get throws { fatalError() } } + // // Async effect checking // @@ -124,13 +130,11 @@ func async01() async {} @abi(func async11() async) func async11() async {} -// -// Miscellaneous function checking -// +@abi(var async00Var: Int) +var async00Var: Int { get { fatalError() } } -@_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=}} +@abi(var async11Var: Int) +var async11Var: Int { get async { fatalError() } } // // PBD shape checking @@ -149,7 +153,7 @@ var (x3, y3): (Int, Int) = (0, 0), a3: Int = 0 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 +// Redeclaration diagnostics // @abi(func noConflictWithSelf()) @@ -283,6 +287,754 @@ func fn() { var var_conflictsWithNonABI: Int = 0 // expected-error {{invalid redeclaration of 'var_conflictsWithNonABI'}} } +// +// Type differences +// + +@abi(func floatForIntParam(_: Float) -> Int) // expected-error @:31 {{type 'Float' in '@abi' should match 'Int'}} +func intForFloatParam(_: Int) -> Int { fatalError() } // expected-note @:26 {{should match type here}} + +@abi(func floatForIntResult(_: Int) -> Float) // expected-error @:40 {{type 'Float' in '@abi' should match 'Int'}} +func intForFloatResult(_: Int) -> Int { fatalError() } // expected-note @:35 {{should match type here}} + +@abi(func labeledForUnlabeledTuple(_: (x: Int, y: Int))) +func labeledForUnlabeledTuple(_: (Int, Int)) {} + +@abi(func unlabeledForLabeledTuple(_: (Int, Int))) +func unlabeledForLabeledTuple(_: (x: Int, y: Int)) {} + +@abi(func labeledForLabeledTuple(_: (x: Int, y: Int))) +func labeledForLabeledTuple(_: (a: Int, b: Int)) {} + +@abi( + func testDefaultArguments( + a: Int, + b: Int = 1, // expected-error {{'b' in '@abi' should not have a default argument; it does not affect the parameter's ABI}} + c: Int, + d: Int = 2 // expected-error {{'d' in '@abi' should not have a default argument; it does not affect the parameter's ABI}} + ) +) +func testDefaultArguments( + a: Int, + b: Int, + c: Int = 1, + d: Int = 2 +) {} + +@abi(func arrayForVariadicParam(a: [Int], b: Set)) // expected-error @:46 {{type 'Set' in '@abi' should match 'Float...'}} +func arrayForVariadicParam(a: Int..., b: Float...) {} // expected-note @:42 {{should match type here}} + +struct DefaultParamOwnership { + @abi( + func method( + _: AnyObject, + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with default}} + _: borrowing AnyObject, + _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with default}} + _: __shared AnyObject, + _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with default}} + _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with default}} + _: (AnyObject) -> Void, + _: (borrowing AnyObject) -> Void, + _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(AnyObject) -> Void'}} + ) + ) + func method( + _: AnyObject, + _: AnyObject, // expected-note {{should match type here}} + _: AnyObject, + _: AnyObject, // expected-note {{should match type here}} + _: AnyObject, + _: AnyObject, // expected-note {{should match type here}} + _: AnyObject, // expected-note {{should match type here}} + _: (AnyObject) -> Void, + _: (AnyObject) -> Void, + _: (AnyObject) -> Void // expected-note {{should match type here}} + ) {} + + @abi( + init( + _: AnyObject, + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with default}} + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with default}} + _: consuming AnyObject, + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with default}} + _: __owned AnyObject, + _: sending AnyObject, + _: (AnyObject) -> Void, + _: (borrowing AnyObject) -> Void, + _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(AnyObject) -> Void'}} + ) + ) + init( + _: AnyObject, + _: AnyObject, // expected-note {{should match type here}} + _: AnyObject, // expected-note {{should match type here}} + _: AnyObject, + _: AnyObject, // expected-note {{should match type here}} + _: AnyObject, + _: AnyObject, + _: (AnyObject) -> Void, + _: (AnyObject) -> Void, + _: (AnyObject) -> Void // expected-note {{should match type here}} + ) {} +} + +struct InoutParamOwnership { + @abi( + func method( + _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'inout'}} + _: inout AnyObject, + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'inout'}} + _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with 'inout'}} + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'inout'}} + _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with 'inout'}} + _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with 'inout'}} + _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + ) + ) + func method( + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, // expected-note {{should match type here}} + _: (inout AnyObject) -> Void, // expected-note {{should match type here}} + _: (inout AnyObject) -> Void, // expected-note {{should match type here}} + _: (inout AnyObject) -> Void // expected-note {{should match type here}} + ) {} + + @abi( + init( + _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'inout'}} + _: inout AnyObject, + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'inout'}} + _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with 'inout'}} + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'inout'}} + _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with 'inout'}} + _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with 'inout'}} + _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + ) + ) + init( + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, // expected-note {{should match type here}} + _: inout AnyObject, // expected-note {{should match type here}} + _: (inout AnyObject) -> Void, // expected-note {{should match type here}} + _: (inout AnyObject) -> Void, // expected-note {{should match type here}} + _: (inout AnyObject) -> Void // expected-note {{should match type here}} + ) {} +} + +struct BorrowingParamOwnership { + @abi( + func method( + _: AnyObject, + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'borrowing'}} + _: borrowing AnyObject, + _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with 'borrowing'}} + _: __shared AnyObject, + _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with 'borrowing'}} + _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with 'borrowing'}} + _: (AnyObject) -> Void, + _: (borrowing AnyObject) -> Void, + _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(borrowing AnyObject) -> Void'}} + ) + ) + func method( + _: borrowing AnyObject, + _: borrowing AnyObject, // expected-note {{should match type here}} + _: borrowing AnyObject, + _: borrowing AnyObject, // expected-note {{should match type here}} + _: borrowing AnyObject, + _: borrowing AnyObject, // expected-note {{should match type here}} + _: borrowing AnyObject, // expected-note {{should match type here}} + _: (borrowing AnyObject) -> Void, + _: (borrowing AnyObject) -> Void, + _: (borrowing AnyObject) -> Void // expected-note {{should match type here}} + ) {} + + @abi( + init( + _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'borrowing'}} + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'borrowing'}} + _: borrowing AnyObject, + _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with 'borrowing'}} + _: __shared AnyObject, + _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with 'borrowing'}} + _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with 'borrowing'}} + _: (AnyObject) -> Void, + _: (borrowing AnyObject) -> Void, + _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(borrowing AnyObject) -> Void'}} + ) + ) + init( + _: borrowing AnyObject, // expected-note {{should match type here}} + _: borrowing AnyObject, // expected-note {{should match type here}} + _: borrowing AnyObject, + _: borrowing AnyObject, // expected-note {{should match type here}} + _: borrowing AnyObject, + _: borrowing AnyObject, // expected-note {{should match type here}} + _: borrowing AnyObject, // expected-note {{should match type here}} + _: (borrowing AnyObject) -> Void, + _: (borrowing AnyObject) -> Void, + _: (borrowing AnyObject) -> Void // expected-note {{should match type here}} + ) {} +} + +struct ConsumingParamOwnership { + @abi( + func method( + _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'consuming'}} + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'consuming'}} + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'consuming'}} + _: consuming AnyObject, + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'consuming'}} + _: __owned AnyObject, + _: sending AnyObject, + _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} + _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} + _: (consuming AnyObject) -> Void + ) + ) + func method( + _: consuming AnyObject, // expected-note {{should match type here}} + _: consuming AnyObject, // expected-note {{should match type here}} + _: consuming AnyObject, // expected-note {{should match type here}} + _: consuming AnyObject, + _: consuming AnyObject, // expected-note {{should match type here}} + _: consuming AnyObject, + _: consuming AnyObject, + _: (consuming AnyObject) -> Void, // expected-note {{should match type here}} + _: (consuming AnyObject) -> Void, // expected-note {{should match type here}} + _: (consuming AnyObject) -> Void + ) {} + + @abi( + init( + _: AnyObject, + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'consuming'}} + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'consuming'}} + _: consuming AnyObject, + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'consuming'}} + _: __owned AnyObject, + _: sending AnyObject, + _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} + _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} + _: (consuming AnyObject) -> Void + ) + ) + init( + _: consuming AnyObject, + _: consuming AnyObject, // expected-note {{should match type here}} + _: consuming AnyObject, // expected-note {{should match type here}} + _: consuming AnyObject, + _: consuming AnyObject, // expected-note {{should match type here}} + _: consuming AnyObject, + _: consuming AnyObject, + _: (consuming AnyObject) -> Void, // expected-note {{should match type here}} + _: (consuming AnyObject) -> Void, // expected-note {{should match type here}} + _: (consuming AnyObject) -> Void + ) {} +} + +struct SharedParamOwnership { + @abi( + func method( + _: AnyObject, + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with '__shared'}} + _: borrowing AnyObject, + _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with '__shared'}} + _: __shared AnyObject, + _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with '__shared'}} + _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with '__shared'}} + _: (AnyObject) -> Void, + _: (borrowing AnyObject) -> Void, + _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(__shared AnyObject) -> Void'}} + ) + ) + func method( + _: __shared AnyObject, + _: __shared AnyObject, // expected-note {{should match type here}} + _: __shared AnyObject, + _: __shared AnyObject, // expected-note {{should match type here}} + _: __shared AnyObject, + _: __shared AnyObject, // expected-note {{should match type here}} + _: __shared AnyObject, // expected-note {{should match type here}} + _: (__shared AnyObject) -> Void, + _: (__shared AnyObject) -> Void, + _: (__shared AnyObject) -> Void // expected-note {{should match type here}} + ) {} + + @abi( + init( + _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with '__shared'}} + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with '__shared'}} + _: borrowing AnyObject, + _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with '__shared'}} + _: __shared AnyObject, + _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with '__shared'}} + _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with '__shared'}} + _: (AnyObject) -> Void, + _: (borrowing AnyObject) -> Void, + _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(__shared AnyObject) -> Void'}} + ) + ) + init( + _: __shared AnyObject, // expected-note {{should match type here}} + _: __shared AnyObject, // expected-note {{should match type here}} + _: __shared AnyObject, + _: __shared AnyObject, // expected-note {{should match type here}} + _: __shared AnyObject, + _: __shared AnyObject, // expected-note {{should match type here}} + _: __shared AnyObject, // expected-note {{should match type here}} + _: (__shared AnyObject) -> Void, + _: (__shared AnyObject) -> Void, + _: (__shared AnyObject) -> Void // expected-note {{should match type here}} + ) {} +} + +struct OwnedParamOwnership { + @abi( + func method( + _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with '__owned'}} + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with '__owned'}} + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with '__owned'}} + _: consuming AnyObject, + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with '__owned'}} + _: __owned AnyObject, + _: sending AnyObject, + _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} + _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} + _: (consuming AnyObject) -> Void + ) + ) + func method( + _: __owned AnyObject, // expected-note {{should match type here}} + _: __owned AnyObject, // expected-note {{should match type here}} + _: __owned AnyObject, // expected-note {{should match type here}} + _: __owned AnyObject, + _: __owned AnyObject, // expected-note {{should match type here}} + _: __owned AnyObject, + _: __owned AnyObject, + _: (__owned AnyObject) -> Void, // expected-note {{should match type here}} + _: (__owned AnyObject) -> Void, // expected-note {{should match type here}} + _: (__owned AnyObject) -> Void + ) {} + + @abi( + init( + _: AnyObject, + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with '__owned'}} + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with '__owned'}} + _: consuming AnyObject, + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with '__owned'}} + _: __owned AnyObject, + _: sending AnyObject, + _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} + _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} + _: (consuming AnyObject) -> Void + ) + ) + init( + _: __owned AnyObject, + _: __owned AnyObject, // expected-note {{should match type here}} + _: __owned AnyObject, // expected-note {{should match type here}} + _: __owned AnyObject, + _: __owned AnyObject, // expected-note {{should match type here}} + _: __owned AnyObject, + _: __owned AnyObject, + _: (__owned AnyObject) -> Void, // expected-note {{should match type here}} + _: (__owned AnyObject) -> Void, // expected-note {{should match type here}} + _: (__owned AnyObject) -> Void + ) {} +} + +struct SendingParamOwnership { + @abi( + func method( + _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'sending'}} + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'sending'}} + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'sending'}} + _: consuming AnyObject, + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'sending'}} + _: __owned AnyObject, + _: sending AnyObject, + _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} + _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} + _: (consuming AnyObject) -> Void + ) + ) + func method( + _: sending AnyObject, // expected-note {{should match type here}} + _: sending AnyObject, // expected-note {{should match type here}} + _: sending AnyObject, // expected-note {{should match type here}} + _: sending AnyObject, + _: sending AnyObject, // expected-note {{should match type here}} + _: sending AnyObject, + _: sending AnyObject, + _: (sending AnyObject) -> Void, // expected-note {{should match type here}} + _: (sending AnyObject) -> Void, // expected-note {{should match type here}} + _: (sending AnyObject) -> Void + ) {} + + @abi( + init( + _: AnyObject, + _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'sending'}} + _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'sending'}} + _: consuming AnyObject, + _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'sending'}} + _: __owned AnyObject, + _: sending AnyObject, + _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} + _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} + _: (consuming AnyObject) -> Void + ) + ) + init( + _: sending AnyObject, + _: sending AnyObject, // expected-note {{should match type here}} + _: sending AnyObject, // expected-note {{should match type here}} + _: sending AnyObject, + _: sending AnyObject, // expected-note {{should match type here}} + _: sending AnyObject, + _: sending AnyObject, + _: (sending AnyObject) -> Void, // expected-note {{should match type here}} + _: (sending AnyObject) -> Void, // expected-note {{should match type here}} + _: (sending AnyObject) -> Void + ) {} +} + +// @autoclosure should be flattened away +@abi( + func autoclosureTest( + _: @autoclosure () -> Void, + _: () -> Void, + _: @autoclosure () -> Void, + _: () -> Void + ) +) +func autoclosureTest( + _: @autoclosure () -> Void, + _: @autoclosure () -> Void, + _: () -> Void, + _: () -> Void +) {} + +// @_nonEphemeral should be flattened away +// (the diagnostic we get on these is actually part of attr checking) +@abi( + func nonEphemeralTest( + @_nonEphemeral _: UnsafeRawPointer, // expected-error {{unused '_nonEphemeral' attribute in '@abi'}} + _: UnsafeRawPointer, + @_nonEphemeral _: UnsafeRawPointer, // expected-error {{unused '_nonEphemeral' attribute in '@abi'}} + _: UnsafeRawPointer + ) +) +func nonEphemeralTest( + @_nonEphemeral _: UnsafeRawPointer, + @_nonEphemeral _: UnsafeRawPointer, + _: UnsafeRawPointer, + _: UnsafeRawPointer +) {} + +// isolated param should be flattened away +@abi(func isolatedTestMismatch(_: isolated A, _: A)) +func isolatedTestMismatch(_: A, _: isolated A) {} + +@abi(func isolatedTestMatch(_: isolated A, _: A)) +func isolatedTestMatch(_: isolated A, _: A) {} + +// _const should be flattened away +@abi( + func constTest( + _: _const () -> Void, + _: () -> Void, + _: _const () -> Void, + _: () -> Void + ) +) +func constTest( + _: _const () -> Void, + _: _const () -> Void, + _: () -> Void, + _: () -> Void +) {} + +// @noDerivative should match +@abi(func noDerivativeTest1(_: @differentiable(reverse) (@noDerivative Double, Double) -> Double)) +func noDerivativeTest1(_: @differentiable(reverse) (@noDerivative Double, Double) -> Double) {} + +@abi(func noDerivativeTest2(_: @differentiable(reverse) (Double, Double) -> Double)) // expected-error {{type '@differentiable(reverse) (Double, Double) -> Double' in '@abi' should match '@differentiable(reverse) (@noDerivative Double, Double) -> Double'}} +func noDerivativeTest2(_: @differentiable(reverse) (@noDerivative Double, Double) -> Double) {} // expected-note {{should match type here}} + +@abi(func noDerivativeTest3(_: @differentiable(reverse) (@noDerivative Double, Double) -> Double)) // expected-error {{type '@differentiable(reverse) (@noDerivative Double, Double) -> Double' in '@abi' should match '@differentiable(reverse) (Double, Double) -> Double'}} +func noDerivativeTest3(_: @differentiable(reverse) (Double, Double) -> Double) {} // expected-note {{should match type here}} + +// @_addressable should match +@abi( + func addressableTest( + _: @_addressable String, + _: String, // expected-error {{default parameter attribute in '@abi' is not compatible with '_addressable'}} + _: @_addressable String, // expected-error {{parameter attribute '_addressable' in '@abi' is not compatible with default}} + _: String + ) +) +func addressableTest( + _: @_addressable String, + _: @_addressable String, // expected-note {{should match type here}} + _: String, // expected-note {{should match type here}} + _: String +) {} + +// Flattening of function type ExtInfo +@abi( + func fnExtInfoTest( + _: @escaping () -> AnyObject, + _: @Sendable () -> AnyObject, + _: () -> sending AnyObject, + _: () -> AnyObject, + _: @MainActor () -> AnyObject, + _: (isolated MainActor) -> AnyObject, + _: @isolated(any) () -> AnyObject, // expected-error {{type '@isolated(any) () -> AnyObject' in '@abi' should match '() -> AnyObject'}} + _: @execution(caller) () async -> AnyObject, + _: () -> AnyObject, // expected-error {{type '() -> AnyObject' in '@abi' should match '@isolated(any) () -> AnyObject'}} + _: () async -> Void, + _: () -> Void, // expected-error {{type '() -> Void' in '@abi' should match '() async -> Void'}} + _: () async -> Void, // expected-error {{type '() async -> Void' in '@abi' should match '() -> Void'}} + _: () -> Void, + _: () throws -> Void, + _: () -> Void, // expected-error {{type '() -> Void' in '@abi' should match '() throws -> Void'}} + _: () throws -> Void, // expected-error {{type '() throws -> Void' in '@abi' should match '() -> Void'}} + _: () -> Void, + _: () -> Void, + _: @convention(block) () -> Void, // expected-error {{type '@convention(block) () -> Void' in '@abi' should match '() -> Void'}} + _: @convention(thin) () -> Void, // expected-error {{type '@convention(thin) () -> Void' in '@abi' should match '() -> Void'}} + _: @convention(c) () -> Void // expected-error {{type '@convention(c) () -> Void' in '@abi' should match '() -> Void'}} + ) +) +func fnExtInfoTest( + _: () -> AnyObject, + _: () -> AnyObject, + _: () -> AnyObject, + _: () -> AnyObject, + _: () -> AnyObject, + _: (MainActor) -> AnyObject, + _: () -> AnyObject, // expected-note {{should match type here}} + _: () async -> AnyObject, + _: @isolated(any) () -> AnyObject, // expected-note {{should match type here}} + _: () async -> Void, + _: () async -> Void, // expected-note {{should match type here}} + _: () -> Void, // expected-note {{should match type here}} + _: () -> Void, + _: () throws -> Void, + _: () throws -> Void, // expected-note {{should match type here}} + _: () -> Void, // expected-note {{should match type here}} + _: () -> Void, + _: () -> Void, + _: () -> Void, // expected-note {{should match type here}} + _: () -> Void, // expected-note {{should match type here}} + _: () -> Void // expected-note {{should match type here}} +) {} + +// FIXME: Not sure how to reach tryNormalizeOutermostType() generic func + +@abi( + func testMarkerProtocols( + _: A, _: B, + _: Any, _: Sendable, + _: AnyKeyPath, _: AnyKeyPath & Sendable, + _: Any, _: Sendable & BitwiseCopyable + ) +) +func testMarkerProtocols( + _: A, _: B, + _: Sendable, _: Any, + _: AnyKeyPath & Sendable, _: AnyKeyPath, + _: Sendable & BitwiseCopyable, _: Any +) {} + +@abi( + func testNormalProtocols( + _: Any, // expected-error {{type 'Any' in '@abi' should match 'any CustomStringConvertible'}} + _: CustomStringConvertible, // expected-error {{type 'any CustomStringConvertible' in '@abi' should match 'Any'}} + _: AnyKeyPath, // expected-error {{type 'AnyKeyPath' in '@abi' should match 'any AnyKeyPath & CustomStringConvertible'}} + _: AnyKeyPath & CustomStringConvertible, // expected-error {{type 'any AnyKeyPath & CustomStringConvertible' in '@abi' should match 'AnyKeyPath'}} + _: Any, // expected-error {{type 'Any' in '@abi' should match 'any CustomDebugStringConvertible & CustomStringConvertible'}} + _: CustomStringConvertible & CustomDebugStringConvertible // expected-error {{type 'any CustomDebugStringConvertible & CustomStringConvertible' in '@abi' should match 'Any'}} + ) +) +func testNormalProtocols( + _: CustomStringConvertible, // expected-note {{should match type here}} + _: Any, // expected-note {{should match type here}} + _: AnyKeyPath & CustomStringConvertible, // expected-note {{should match type here}} + _: AnyKeyPath, // expected-note {{should match type here}} + _: CustomStringConvertible & CustomDebugStringConvertible, // expected-note {{should match type here}} + _: Any // expected-note {{should match type here}} +) {} + +@abi( + func testNormalProtocolsGeneric( // expected-error {{generic signature '' in '@abi' is not compatible with ''}} + _: A, _: B + ) +) +func testNormalProtocolsGeneric( // expected-note {{should match type here}} + _: A, _: B +) {} + +enum ErsatzResult {} +extension ErsatzResult where Failure == Swift.Error { + // Similar to Swift.Result.init(__untyped_throws_catching:) + // FIXME: The where clause makes this ABI-compatible, but we can't tell that. + @abi( + init( + catching body: () throws -> Success // expected-error {{type '() throws -> Success' in '@abi' should match '() throws(Failure) -> Success'}} + ) + ) + init( + __untyped_throws_catching body: () throws(Failure) -> Success // expected-note {{should match type here}} + ) {} +} + +// +// Static/Instance and interactions with `final` +// + +class ClassStaticAndFinal { + @abi(func class00final00()) + func class00final00() {} + + @abi(class func class10final00()) // expected-error {{class method 'class10final00()' in '@abi' should be instance method to ensure ABI compatibility}} {{8-14=}} + func class10final00() {} + + @abi(static func class20final00()) // expected-error {{static method 'class20final00()' in '@abi' should be non-final instance method to ensure ABI compatibility}} {{8-15=}} + func class20final00() {} + + @abi(func class01final00()) // expected-error {{instance method 'class01final00()' in '@abi' should be class method to ensure ABI compatibility}} {{8-8=class }} + class func class01final00() {} + + @abi(func class02final00()) // expected-error {{non-final instance method 'class02final00()' in '@abi' should be static method to ensure ABI compatibility}} {{8-8=static }} + static func class02final00() {} + + @abi(class func class11final00()) + class func class11final00() {} + + @abi(class func class12final00()) // expected-error {{non-final class method 'class12final00()' in '@abi' should be static method to ensure ABI compatibility}} {{8-13=static}} + static func class12final00() {} + + @abi(static func class21final00()) // expected-error {{static method 'class21final00()' in '@abi' should be non-final class method to ensure ABI compatibility}} {{8-14=class}} + class func class21final00() {} + + @abi(static func class22final00()) + static func class22final00() {} + + @abi(final func class00final10()) // expected-error {{final instance method 'class00final10()' in '@abi' should be non-final instance method to ensure ABI compatibility}} {{8-14=}} + func class00final10() {} + + @abi(class final func class10final10()) // expected-error {{final class method 'class10final10()' in '@abi' should be non-final instance method to ensure ABI compatibility}} {{8-14=}} + func class10final10() {} + + @abi(final func class01final10()) // expected-error {{final instance method 'class01final10()' in '@abi' should be non-final class method to ensure ABI compatibility}} {{8-13=class}} + class func class01final10() {} + + @abi(final func class02final10()) // expected-error {{final instance method 'class02final10()' in '@abi' should be static method to ensure ABI compatibility}} {{8-13=static}} + static func class02final10() {} + + @abi(class final func class11final10()) // expected-error {{final class method 'class11final10()' in '@abi' should be non-final class method to ensure ABI compatibility}} {{14-20=}} + class func class11final10() {} + + @abi(class final func class12final10()) + static func class12final10() {} + + @abi(func class00final01()) // expected-error {{non-final instance method 'class00final01()' in '@abi' should be final instance method to ensure ABI compatibility}} {{8-8=final }} + final func class00final01() {} + + @abi(class func class10final01()) // expected-error {{non-final class method 'class10final01()' in '@abi' should be final instance method to ensure ABI compatibility}} {{8-13=final}} + final func class10final01() {} + + @abi(static func class20final01()) // expected-error {{static method 'class20final01()' in '@abi' should be final instance method to ensure ABI compatibility}} {{8-14=final}} + final func class20final01() {} + + @abi(func class01final01()) // expected-error {{non-final instance method 'class01final01()' in '@abi' should be final class method to ensure ABI compatibility}} {{8-8=final class }} + class final func class01final01() {} + + @abi(class func class11final01()) // expected-error {{non-final class method 'class11final01()' in '@abi' should be final class method to ensure ABI compatibility}} {{8-13=final class}} + class final func class11final01() {} + + @abi(static func class21final01()) + class final func class21final01() {} + + @abi(final func class00final11()) + final func class00final11() {} + + @abi(class final func class10final11()) //expected-error {{final class method 'class10final11()' in '@abi' should be final instance method to ensure ABI compatibility}} {{14-20=}} {{8-13=final}} + final func class10final11() {} + + @abi(final func class01final11()) // expected-error {{final instance method 'class01final11()' in '@abi' should be final class method to ensure ABI compatibility}} {{8-13=final class}} + class final func class01final11() {} + + @abi(class final func class11final11()) + class final func class11final11() {} +} + +struct StaticAndFinal { + @abi(func class00final00()) + func class00final00() {} + + @abi(static func class20final00()) // expected-error {{static method 'class20final00()' in '@abi' should be instance method to ensure ABI compatibility}} {{8-15=}} + func class20final00() {} + + @abi(func class02final00()) // expected-error {{instance method 'class02final00()' in '@abi' should be static method to ensure ABI compatibility}} {{8-8=static }} + static func class02final00() {} + + @abi(static func class22final00()) + static func class22final00() {} +} + +// +// Failable Initializers +// + +struct FailableInits { + @abi(init(i11: Void)) + init(i11: Void) {} + + @abi(init?(i21: Void)) // expected-error {{cannot give non-failable initializer 'init(i21:)' the ABI of a failable initializer}} {{12-13=}} + init(i21: Void) {} + + @abi(init!(i31: Void)) // expected-error {{cannot give non-failable initializer 'init(i31:)' the ABI of a failable initializer}} {{12-13=}} + init(i31: Void) {} + + @abi(init(i12: Void)) // expected-error {{cannot give failable initializer 'init(i12:)' the ABI of a non-failable initializer}} {{12-12=?}} + init?(i12: Void) {} + + @abi(init?(i22: Void)) + init?(i22: Void) {} + + @abi(init!(i32: Void)) + init?(i32: Void) {} + + @abi(init(i13: Void)) // expected-error {{cannot give failable initializer 'init(i13:)' the ABI of a non-failable initializer}} {{12-12=!}} + init!(i13: Void) {} + + @abi(init?(i23: Void)) + init!(i23: Void) {} + + @abi(init!(i33: Void)) + init!(i33: Void) {} +} + // // Incorrect usage // From 1bb21863777e52f773da371d99cda83d75597516 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 19 Mar 2025 14:30:49 -0700 Subject: [PATCH 08/22] Inherit @objc, dynamic, @implementation in `@abi` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ABI-only declarations now query their API counterpart for things like `isObjC()`, their ObjC name, dynamic status, etc. This means that `@objc` and friends can simply be omitted from an `@abi` attribute. No tests in this commit since attribute checking hasn’t landed yet. --- lib/AST/Decl.cpp | 48 ++++++++++++++++++++++++++++- lib/AST/SwiftNameTranslation.cpp | 25 +++++++++++++++ lib/AST/TypeCheckRequests.cpp | 3 +- lib/ClangImporter/ClangImporter.cpp | 26 ++++++++++++++++ lib/Sema/TypeCheckAttr.cpp | 4 +++ lib/Sema/TypeCheckDecl.cpp | 5 +++ lib/Sema/TypeCheckDeclObjC.cpp | 21 +++++++++++++ 7 files changed, 130 insertions(+), 2 deletions(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 6c19ffe971e14..64253946b245e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4156,6 +4156,10 @@ void ValueDecl::setIsObjC(bool value) { } Identifier ExtensionDecl::getObjCCategoryName() const { + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getObjCCategoryName(); + // If there's an @objc attribute, it's authoritative. (ClangImporter // attaches one automatically.) if (auto objcAttr = getAttrs().getAttribute(/*AllowInvalid*/true)) { @@ -4409,6 +4413,10 @@ StringRef ValueDecl::getCDeclName() const { return clangDecl->getName(); } + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getCDeclName(); + // Handle explicit cdecl attributes. if (auto cdeclAttr = getAttrs().getAttribute()) { return cdeclAttr->Name; @@ -4447,6 +4455,10 @@ ValueDecl::getObjCRuntimeName(bool skipIsObjCResolution) const { std::optional Decl::getExplicitObjCName(bool allowInvalid) const { + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getExplicitObjCName(); + auto objcAttr = getAttrs().getAttribute(allowInvalid); if (objcAttr && !objcAttr->isNameImplicit()) return objcAttr->getName(); @@ -5141,6 +5153,10 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC, static bool isObjCMemberImplementation(const ValueDecl *VD, llvm::function_ref getAccessLevel) { + auto abiRole = ABIRoleInfo(VD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return isObjCMemberImplementation(abiRole.getCounterpart(), getAccessLevel); + if (auto ED = dyn_cast(VD->getDeclContext())) if (ED->isObjCImplementation() && !isa(VD)) { auto attrDecl = isa(VD) @@ -6620,6 +6636,10 @@ static StringRef mangleObjCRuntimeName(const NominalTypeDecl *nominal, StringRef ClassDecl::getObjCRuntimeName( llvm::SmallVectorImpl &buffer) const { + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getObjCRuntimeName(buffer); + // If there is a Clang declaration, use it's runtime name. if (auto objcClass = dyn_cast_or_null(getClangDecl())) @@ -7128,6 +7148,10 @@ ProtocolDecl::getPrimaryAssociatedTypes() const { StringRef ProtocolDecl::getObjCRuntimeName( llvm::SmallVectorImpl &buffer) const { + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getObjCRuntimeName(buffer); + // If there is an 'objc' attribute with a name, use that name. if (auto objc = getAttrs().getAttribute()) { if (auto name = objc->getName()) @@ -7600,6 +7624,10 @@ getNameFromObjcAttribute(const ObjCAttr *attr, DeclName preferredName) { ObjCSelector AbstractStorageDecl::getObjCGetterSelector(Identifier preferredName) const { + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getObjCGetterSelector(preferredName); + // If the getter has an @objc attribute with a name, use that. if (auto getter = getAccessor(AccessorKind::Get)) { if (auto name = getNameFromObjcAttribute(getter->getAttrs(). @@ -7630,6 +7658,10 @@ AbstractStorageDecl::getObjCGetterSelector(Identifier preferredName) const { ObjCSelector AbstractStorageDecl::getObjCSetterSelector(Identifier preferredName) const { + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getObjCSetterSelector(preferredName); + // If the setter has an @objc attribute with a name, use that. auto setter = getAccessor(AccessorKind::Set); auto objcAttr = setter ? setter->getAttrs().getAttribute() @@ -8617,6 +8649,10 @@ Type VarDecl::getPropertyWrapperInitValueInterfaceType() const { } Identifier VarDecl::getObjCPropertyName() const { + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getObjCPropertyName(); + if (auto attr = getAttrs().getAttribute()) { if (auto name = attr->getName()) return name->getSelectorPieces()[0]; @@ -10221,6 +10257,11 @@ AbstractFunctionDecl::getBodyFingerprintIncludingLocalTypeMembers() const { ObjCSelector AbstractFunctionDecl::getObjCSelector(DeclName preferredName, bool skipIsObjCResolution) const { + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getObjCSelector(preferredName, + skipIsObjCResolution); + // FIXME: Forces computation of the Objective-C selector. if (!skipIsObjCResolution) (void)isObjC(); @@ -11652,7 +11693,12 @@ bool ClassDecl::isNonDefaultExplicitDistributedActor(ModuleDecl *M, bool ClassDecl::isNativeNSObjectSubclass() const { // @objc actors implicitly inherit from NSObject. if (isActor()) { - if (getAttrs().hasAttribute()) { + DeclAttributes attrs = getAttrs(); + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + attrs = abiRole.getCounterpart()->getAttrs(); + + if (attrs.hasAttribute()) { return true; } ClassDecl *superclass = getSuperclassDecl(); diff --git a/lib/AST/SwiftNameTranslation.cpp b/lib/AST/SwiftNameTranslation.cpp index 934c4db467dee..f675c536dcac7 100644 --- a/lib/AST/SwiftNameTranslation.cpp +++ b/lib/AST/SwiftNameTranslation.cpp @@ -37,6 +37,10 @@ getNameForObjC(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) { assert(isa(VD) || isa(VD) || isa(VD) || isa(VD) || isa(VD) || isa(VD)); + auto abiRole = ABIRoleInfo(VD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return getNameForObjC(abiRole.getCounterpart(), customNamesOnly); + if (auto objc = VD->getAttrs().getAttribute()) { if (auto name = objc->getName()) { assert(name->getNumSelectorPieces() == 1); @@ -63,6 +67,10 @@ getErrorDomainStringForObjC(const EnumDecl *ED) { // Should have already been diagnosed as diag::objc_enum_generic. assert(!ED->isGenericContext() && "Trying to bridge generic enum error to Obj-C"); + auto abiRole = ABIRoleInfo(ED); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return getErrorDomainStringForObjC(abiRole.getCounterpart()); + SmallVector outerTypes; for (const NominalTypeDecl * D = ED; D != nullptr; @@ -86,6 +94,11 @@ getErrorDomainStringForObjC(const EnumDecl *ED) { bool swift::objc_translation:: printSwiftEnumElemNameInObjC(const EnumElementDecl *EL, llvm::raw_ostream &OS, Identifier PreferredName) { + auto abiRole = ABIRoleInfo(EL); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return printSwiftEnumElemNameInObjC(abiRole.getCounterpart(), OS, + PreferredName); + StringRef ElemName = getNameForObjC(EL, CustomNamesOnly); if (!ElemName.empty()) { OS << ElemName; @@ -104,6 +117,10 @@ printSwiftEnumElemNameInObjC(const EnumElementDecl *EL, llvm::raw_ostream &OS, std::pair swift::objc_translation:: getObjCNameForSwiftDecl(const ValueDecl *VD, DeclName PreferredName){ + auto abiRole = ABIRoleInfo(VD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return getObjCNameForSwiftDecl(abiRole.getCounterpart(), PreferredName); + ASTContext &Ctx = VD->getASTContext(); Identifier BaseName; if (PreferredName) { @@ -157,6 +174,10 @@ isVisibleToObjC(const ValueDecl *VD, AccessLevel minRequiredAccess, StringRef swift::cxx_translation::getNameForCxx(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) { + auto abiRole = ABIRoleInfo(VD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return getNameForCxx(abiRole.getCounterpart(), customNamesOnly); + ASTContext& ctx = VD->getASTContext(); for (auto *EA : VD->getAttrs().getAttributes()) { @@ -214,6 +235,10 @@ swift::cxx_translation::DeclRepresentation swift::cxx_translation::getDeclRepresentation( const ValueDecl *VD, std::optional> isZeroSized) { + auto abiRole = ABIRoleInfo(VD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return getDeclRepresentation(abiRole.getCounterpart(), isZeroSized); + if (getActorIsolation(const_cast(VD)).isActorIsolated()) return {Unsupported, UnrepresentableIsolatedInActor}; if (isa(VD)) diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 124329d967f81..d20c0b72d453f 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -356,7 +356,8 @@ void IsDynamicRequest::cacheResult(bool value) const { decl->setIsDynamic(value); // Add an attribute for printing - if (value && !decl->getAttrs().hasAttribute()) + if (value && !decl->getAttrs().hasAttribute() && + ABIRoleInfo(decl).providesAPI()) decl->getAttrs().add(new (decl->getASTContext()) DynamicAttr(/*Implicit=*/true)); } diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index e694108488f63..8eadf227e0591 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -6594,6 +6594,9 @@ findFunctionInterfaceAndImplementation(AbstractFunctionDecl *func) { ObjCInterfaceAndImplementation ObjCInterfaceAndImplementationRequest:: evaluate(Evaluator &evaluator, Decl *decl) const { + ASSERT(ABIRoleInfo(decl).providesAPI() + && "@interface request for ABI-only decl?"); + // Types and extensions have direct links to their counterparts through the // `@_objcImplementation` attribute. Let's resolve that. // (Also directing nulls here, where they'll early-return.) @@ -6633,6 +6636,21 @@ llvm::TinyPtrVector Decl::getAllImplementedObjCDecls() const { // This *is* the interface, if there is one. return {}; + // ABI-only attributes don't have an `@implementation`, so query the API + // counterpart and map the results back to ABI decls. + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) { + auto interfaceDecls = + abiRole.getCounterpart()->getAllImplementedObjCDecls(); + + // Map the APIs back to their ABI counterparts (often a no-op) + for (auto &interfaceDecl : interfaceDecls) { + interfaceDecl = ABIRoleInfo(interfaceDecl).getCounterpart(); + } + + return interfaceDecls; + } + ObjCInterfaceAndImplementationRequest req{const_cast(this)}; auto result = evaluateOrDefault(getASTContext().evaluator, req, {}); return result.interfaceDecls; @@ -6650,6 +6668,14 @@ Decl *Decl::getObjCImplementationDecl() const { // This *is* the implementation, if it has one. return nullptr; + // ABI-only attributes don't have an `@implementation`, so query the API + // counterpart and map the results back to ABI decls. + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) { + auto implDecl = abiRole.getCounterpart()->getObjCImplementationDecl(); + return ABIRoleInfo(implDecl).getCounterpart(); + } + ObjCInterfaceAndImplementationRequest req{const_cast(this)}; auto result = evaluateOrDefault(getASTContext().evaluator, req, {}); return result.implementationDecl; diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index e97b72af81d9f..620fcbbd22845 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1662,6 +1662,10 @@ static SourceRange getArgListRange(ASTContext &Ctx, DeclAttribute *attr) { void AttributeChecker:: visitObjCImplementationAttr(ObjCImplementationAttr *attr) { + // If `D` is ABI-only, let ABIDeclChecker diagnose the bad attribute. + if (!ABIRoleInfo(D).providesAPI()) + return; + DeclAttribute * langAttr = D->getAttrs().getAttribute(/*AllowInvalid=*/true); if (!langAttr) diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 228c6bc6f0c74..0b6f5c360ba11 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -938,6 +938,11 @@ IsStaticRequest::evaluate(Evaluator &evaluator, FuncDecl *decl) const { bool IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { + // ABI-only decls get this from their API decl. + auto abiRole = ABIRoleInfo(decl); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->isDynamic(); + // If we can't infer dynamic here, don't. if (!DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::Dynamic, decl)) return false; diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 3bf4ff5d53f6b..ec38710102b22 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -650,6 +650,11 @@ bool swift::isRepresentableInObjC( const AbstractFunctionDecl *AFD, ObjCReason Reason, std::optional &asyncConvention, std::optional &errorConvention) { + auto abiRole = ABIRoleInfo(AFD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return isRepresentableInObjC(abiRole.getCounterpart(), Reason, + asyncConvention, errorConvention); + // Clear out the async and error conventions. They will be added later if // needed. asyncConvention = std::nullopt; @@ -1104,6 +1109,10 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { if (VD->isInvalid()) return false; + auto abiRole = ABIRoleInfo(VD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return isRepresentableInObjC(abiRole.getCounterpart(), Reason); + Type T = VD->getDeclContext()->mapTypeIntoContext(VD->getInterfaceType()); if (auto *RST = T->getAs()) { // In-memory layout of @weak and @unowned does not correspond to anything @@ -1160,6 +1169,10 @@ bool swift::isRepresentableInObjC(const VarDecl *VD, ObjCReason Reason) { bool swift::isRepresentableInObjC(const SubscriptDecl *SD, ObjCReason Reason) { // If you change this function, you must add or modify a test in PrintAsClang. + auto abiRole = ABIRoleInfo(SD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return isRepresentableInObjC(abiRole.getCounterpart(), Reason); + ASTContext &ctx = SD->getASTContext(); DiagnosticStateRAII diagState(ctx.Diags); auto behavior = behaviorLimitForObjCReason(Reason, ctx); @@ -1770,6 +1783,11 @@ static void markAsObjC(ValueDecl *D, ObjCReason reason, bool IsObjCRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const { DiagnosticStateRAII diagState(VD->getASTContext().Diags); + // ABI-only decls inherit objc-ness from their API. + auto abiRole = ABIRoleInfo(VD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->isObjC(); + // Access notes may add attributes that affect this calculus. TypeChecker::applyAccessNote(VD); @@ -4115,6 +4133,9 @@ class ObjCImplementationChecker { evaluator::SideEffect TypeCheckObjCImplementationRequest:: evaluate(Evaluator &evaluator, Decl *D) const { + if (!ABIRoleInfo(D).providesAPI()) + return evaluator::SideEffect(); + PrettyStackTraceDecl trace("checking member implementations of", D); // FIXME: Because we check extension-by-extension, candidates and requirements From 9be9b6fad6150f0caf0ef2ace45fdf02229f9df9 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 12 Mar 2025 16:48:20 -0700 Subject: [PATCH 09/22] Inherit access control in `@abi` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ABI-only declarations now inherit access control modifiers like `public` or `private(set)`, as well as `@usableFromInline` and `@_spi`, from their API counterpart. This means these attributes and modifiers don’t need to be specified in an `@abi` attribute. Very few tests because we aren’t yet enforcing the absence of these attributes. --- lib/AST/AccessRequests.cpp | 11 +++++++++++ lib/AST/Decl.cpp | 5 +++++ lib/AST/Module.cpp | 5 +++++ test/IRGen/asmname.swift | 4 ++-- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/AST/AccessRequests.cpp b/lib/AST/AccessRequests.cpp index c85aa726de7dc..996677cb07d01 100644 --- a/lib/AST/AccessRequests.cpp +++ b/lib/AST/AccessRequests.cpp @@ -43,6 +43,11 @@ AccessLevel AccessLevelRequest::evaluate(Evaluator &evaluator, ValueDecl *D) const { assert(!D->hasAccess()); + // ABI decls share the access level of their API decl. + auto abiRole = ABIRoleInfo(D); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getFormalAccess(); + // Check if the decl has an explicit access control attribute. if (auto *AA = D->getAttrs().getAttribute()) return AA->getAccess(); @@ -201,6 +206,12 @@ AccessLevel SetterAccessLevelRequest::evaluate(Evaluator &evaluator, AbstractStorageDecl *ASD) const { assert(!ASD->Accessors.getInt().hasValue()); + + // ABI decls share the access level of their API decl. + auto abiRole = ABIRoleInfo(ASD); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getSetterFormalAccess(); + if (auto *SAA = ASD->getAttrs().getAttribute()) return SAA->getAccess(); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 64253946b245e..61dc314cbeace 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4719,6 +4719,11 @@ SourceLoc Decl::getAttributeInsertionLoc(bool forModifier) const { bool ValueDecl::isUsableFromInline() const { assert(getFormalAccess() < AccessLevel::Public); + // ABI decls share the access level of their API decl. + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->isUsableFromInline(); + if (getAttrs().hasAttribute() || getAttrs().hasAttribute() || getAttrs().hasAttribute()) diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index eba98b6bbaa5c..f8ed0c23c6850 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -3165,6 +3165,11 @@ SPIGroupsRequest::evaluate(Evaluator &evaluator, const Decl *decl) const { assert (isa(decl) || isa(decl)); + // ABI decls share the SPI groups of their API decl. + auto abiRole = ABIRoleInfo(decl); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getSPIGroups(); + // First, look for local attributes. llvm::SetVector spiGroups; for (auto attr : decl->getAttrs().getAttributes()) diff --git a/test/IRGen/asmname.swift b/test/IRGen/asmname.swift index 5895977815e46..50d40110adfb7 100644 --- a/test/IRGen/asmname.swift +++ b/test/IRGen/asmname.swift @@ -88,8 +88,8 @@ extension X { // 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" +// CHECK: define internal swiftcc i64 @"$s7asmname22abi_ABIAttrPrivate_var33_{{[0-9A-F]+}}LLs5Int64Vvg" +// CHECK: define internal swiftcc void @"$s7asmname22abi_ABIAttrPrivate_var33_{{[0-9A-F]+}}LLs5Int64Vvs" @abi(func abi_ABIAttrGenericNonSendableToSendable(_ value: T) -> T) public func api_ABIAttrGenericNonSendableToSendable(_ value: T) -> T { return value } From e88b99a6ffc7e1fe2d8693981109aa430c1512f7 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 19 Mar 2025 14:29:33 -0700 Subject: [PATCH 10/22] Inherit availability in `@abi` ABI-only declarations now inherit `@available`, `@backDeployed`, etc. from their ABI counterpart. This will make it unnecessary to specify these attributes in `@abi`. Also some changes to make sure we suggest inserting `@available` in the correct place. No tests because the enforcement is not yet in. --- lib/AST/Availability.cpp | 5 +++++ lib/Sema/TypeCheckAttr.cpp | 3 ++- lib/Sema/TypeCheckAvailability.cpp | 8 +++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 5aa08cc6f99b3..af8113a89050d 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -411,6 +411,11 @@ bool Decl::isAvailableAsSPI() const { SemanticAvailableAttributes Decl::getSemanticAvailableAttrs(bool includeInactive) const { + // A decl in an @abi gets its availability from the decl it's attached to. + auto abiRole = ABIRoleInfo(this); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->getSemanticAvailableAttrs(includeInactive); + return SemanticAvailableAttributes(getAttrs(), this, includeInactive); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 620fcbbd22845..7b995582be76e 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -5138,7 +5138,8 @@ void AttributeChecker::checkBackDeployedAttrs( } if (auto *AFD = dyn_cast(D)) { - if (!AFD->hasBody()) { + // Ignore this for ABI-only decls; ABIDeclChecker will diagnose it better. + if (!AFD->hasBody() && ABIRoleInfo(AFD).providesAPI()) { diagnoseAndRemoveAttr(Attr, diag::back_deployed_requires_body, Attr, VD); continue; diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 32c323d838f86..1dabd633b9a14 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -480,6 +480,12 @@ static const Decl *relatedDeclForAvailabilityFixit(const Decl *D) { D = accessor->getStorage(); } + auto abiRole = ABIRoleInfo(D); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) { + // ABI-only decls can't have @available attributes of their own. + D = abiRole.getCounterpart(); + } + return D->getAbstractSyntaxDeclForAttributes(); } @@ -645,7 +651,7 @@ static void fixAvailabilityForDecl( // syntax to suggest the Fix-It may differ from the declaration to which // we attach availability attributes in the abstract syntax tree during // parsing. - const Decl *ConcDecl = D->getConcreteSyntaxDeclForAttributes(); + const Decl *ConcDecl = relatedDeclForAvailabilityFixit(D); // To avoid exposing the pattern binding declaration to the user, get the // descriptive kind from one of the VarDecls. From 63be84a44de6a5ed5823aa8ceb9987ac1c29315c Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 12 Mar 2025 16:54:00 -0700 Subject: [PATCH 11/22] Inherit `override` in `@abi` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ABI-only decls will now inherit `override` from their API counterpart; the keyword is unnecessary and `getOverriddenDecls()` will get the ABI counterparts of the decls the API counterpart overrides. No tests because we don’t have the enforcement mechanism in yet. --- lib/Sema/TypeCheckDeclOverride.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 4af7efdfcded1..85a113951a1e4 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -2098,7 +2098,8 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) { if (!override->getAttrs().hasAttribute() && overrideRequiresKeyword(base) != OverrideRequiresKeyword::Never && !override->isImplicit() && - override->getDeclContext()->getParentSourceFile()) { + override->getDeclContext()->getParentSourceFile() && + ABIRoleInfo(override).providesAPI()) { auto theDiag = overrideRequiresKeyword(base) == OverrideRequiresKeyword::Always ? diag::missing_override @@ -2513,6 +2514,19 @@ computeOverriddenDecls(ValueDecl *decl, bool ignoreMissingImports) { llvm::TinyPtrVector OverriddenDeclsRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const { + auto abiRole = ABIRoleInfo(decl); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) { + auto apiOverriddenDecls = abiRole.getCounterpart()->getOverriddenDecls(); + + TinyPtrVector abiOverriddenDecls; + for (auto apiOverriddenDecl : apiOverriddenDecls) { + auto abiOverriddenDecl = ABIRoleInfo(apiOverriddenDecl).getCounterpart(); + abiOverriddenDecls.push_back(abiOverriddenDecl); + } + + return abiOverriddenDecls; + } + auto &ctx = decl->getASTContext(); auto overridden = computeOverriddenDecls(decl, false); From 123447d16033981bf0ce03834566c21c5106c414 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 12 Mar 2025 16:55:48 -0700 Subject: [PATCH 12/22] =?UTF-8?q?Don=E2=80=99t=20add=20HasStorageAttr=20to?= =?UTF-8?q?=20ABI-only=20decls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s unnecessary, shouldn’t be serialized into module interfaces, and Swift doesn’t know how to compute it for an ABI-only decl since it doesn’t have accessors or an initial value. No tests because enforcement isn’t in yet. --- lib/Sema/TypeCheckStorage.cpp | 11 ++++++++++- lib/Serialization/Deserialization.cpp | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 4a6af64e8148d..ea7cb6fd2bd87 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -3733,6 +3733,11 @@ static StorageImplInfo classifyWithHasStorageAttr(VarDecl *var) { bool HasStorageRequest::evaluate(Evaluator &evaluator, AbstractStorageDecl *storage) const { + // ABI decl inherits this from API. + auto abiRole = ABIRoleInfo(storage); + if (!abiRole.providesAPI() && abiRole.getCounterpart()) + return abiRole.getCounterpart()->hasStorage(); + // Parameters are always stored. if (isa(storage)) return true; @@ -3818,7 +3823,11 @@ void HasStorageRequest::cacheResult(bool hasStorage) const { return; if (auto varDecl = dyn_cast(decl)) { - if (hasStorage && !varDecl->getAttrs().hasAttribute()) + auto abiRole = ABIRoleInfo(varDecl); + bool abiOnly = !abiRole.providesAPI() && abiRole.getCounterpart(); + + if (hasStorage && !abiOnly && + !varDecl->getAttrs().hasAttribute()) varDecl->getAttrs().add(new (varDecl->getASTContext()) HasStorageAttr(/*isImplicit=*/true)); } diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index d464f27eac5f3..11541a24eb2c1 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -4025,7 +4025,8 @@ class DeclDeserializer { AddAttribute(new (ctx) OverrideAttr(SourceLoc())); // Add the @_hasStorage attribute if this var has storage. - if (var->hasStorage()) + // (Unless it's an ABI-only decl--they shouldn't have a HasStorageAttr.) + if (var->hasStorage() && ABIDeclCounterpartID == 0) AddAttribute(new (ctx) HasStorageAttr(/*isImplicit:*/true)); { From 495d464044f9fb05695e1f0384a6d619fb2b2e51 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 12 Mar 2025 17:36:30 -0700 Subject: [PATCH 13/22] Add DeclAttribute::isEquivalent() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new method to `DeclAttribute` which can compare two attributes and see if they would be “equivalent” in light of the given decl. “Equivalent” here means that they would have the same effect on the declaration; for instance, two attrs with different source locations can be equivalent, and two attributes with the same arguments in a different order are equivalent if the ordering of those argumetns is not semantically equivalent. This capability is not yet used in this commit, but in a future commit `@abi` will check if certain attributes are equivalent or not. --- include/swift/AST/Attr.h | 216 +++++++++++++++++++++++++++++++++++++++ lib/AST/Attr.cpp | 187 +++++++++++++++++++++++++++++++++ 2 files changed, 403 insertions(+) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 6f2fbd54defba..17c9883b5891d 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -549,6 +549,11 @@ class DeclAttribute : public AttributeBase { /// Determine whether we can clone this attribute. bool canClone() const; + + /// Determine if this attribute and \p other are "the same", as in, they + /// would have the same effect on \p attachedTo were they attached to it. A + /// clone should always be equivalent to the original. + bool isEquivalent(const DeclAttribute *other, Decl *attachedTo) const; }; #define UNIMPLEMENTED_CLONE(AttrType) \ @@ -582,6 +587,11 @@ class SimpleDeclAttr : public DeclAttribute { return new (ctx) SimpleDeclAttr( AtLoc, Range.Start, isImplicit()); } + + bool isEquivalent(const SimpleDeclAttr *other, Decl *attachedTo) const { + // True by definition, since there's nothing to this other than its kind. + return true; + } }; // Declare typedefs for all of the simple declaration attributes. @@ -614,6 +624,10 @@ class SILGenNameAttr : public DeclAttribute { SILGenNameAttr *clone(ASTContext &ctx) const { return new (ctx) SILGenNameAttr(Name, Raw, AtLoc, Range, isImplicit()); } + + bool isEquivalent(const SILGenNameAttr *other, Decl *attachedTo) const { + return Name == other->Name && Raw == other->Raw; + } }; /// Defines the @_section attribute. @@ -636,6 +650,10 @@ class SectionAttr : public DeclAttribute { SectionAttr *clone(ASTContext &ctx) const { return new (ctx) SectionAttr(Name, AtLoc, Range, isImplicit()); } + + bool isEquivalent(const SectionAttr *other, Decl *attachedTo) const { + return Name == other->Name; + } }; /// Defines the @_cdecl attribute. @@ -658,6 +676,10 @@ class CDeclAttr : public DeclAttribute { CDeclAttr *clone(ASTContext &ctx) const { return new (ctx) CDeclAttr(Name, AtLoc, Range, isImplicit()); } + + bool isEquivalent(const CDeclAttr *other, Decl *attachedTo) const { + return Name == other->Name; + } }; /// Defines the @_semantics attribute. @@ -681,6 +703,10 @@ class SemanticsAttr : public DeclAttribute { SemanticsAttr *clone(ASTContext &ctx) const { return new (ctx) SemanticsAttr(Value, AtLoc, Range, isImplicit()); } + + bool isEquivalent(const SemanticsAttr *other, Decl *attachedTo) const { + return Value == other->Value; + } }; /// Defines the @_alignment attribute. @@ -701,6 +727,10 @@ class AlignmentAttr : public DeclAttribute { AlignmentAttr *clone(ASTContext &ctx) const { return new (ctx) AlignmentAttr(getValue(), AtLoc, Range, isImplicit()); } + + bool isEquivalent(const AlignmentAttr *other, Decl *attachedTo) const { + return getValue() == other->getValue(); + } }; /// Defines the @_swift_native_objc_runtime_base attribute. @@ -732,6 +762,11 @@ class SwiftNativeObjCRuntimeBaseAttr : public DeclAttribute { return new (ctx) SwiftNativeObjCRuntimeBaseAttr( BaseClassName, AtLoc, Range, isImplicit()); } + + bool isEquivalent(const SwiftNativeObjCRuntimeBaseAttr *other, + Decl *attachedTo) const { + return BaseClassName == other->BaseClassName; + } }; /// Defines the @available attribute. @@ -914,6 +949,8 @@ class AvailableAttr : public DeclAttribute { return clone(C, isImplicit()); } + bool isEquivalent(const AvailableAttr *other, Decl *attachedTo) const; + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::Available; } @@ -1081,6 +1118,16 @@ class ObjCAttr final : public DeclAttribute, /// original without source location information. ObjCAttr *clone(ASTContext &context) const; + bool isEquivalent(const ObjCAttr *other, Decl *attachedTo) const { + std::optional thisName, otherName; + if (hasName() && !isNameImplicit()) + thisName = getName(); + if (other->hasName() && !other->isNameImplicit()) + otherName = other->getName(); + + return thisName == otherName; + } + static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::ObjC; } @@ -1111,6 +1158,10 @@ class PrivateImportAttr final return new (ctx) PrivateImportAttr( AtLoc, Range, SourceFile, SourceRange(), isImplicit()); } + + bool isEquivalent(const PrivateImportAttr *other, Decl *attachedTo) const { + return getSourceFile() == other->getSourceFile(); + } }; /// The @_dynamicReplacement(for:) attribute. @@ -1190,6 +1241,10 @@ class DynamicReplacementAttr final } UNIMPLEMENTED_CLONE(DynamicReplacementAttr) + + bool isEquivalent(const DynamicReplacementAttr *other, Decl *attachedTo) const { + return getReplacedFunctionName() == other->getReplacedFunctionName(); + } }; /// The \c @_typeEraser(TypeEraserType) attribute. @@ -1247,6 +1302,8 @@ class TypeEraserAttr final : public DeclAttribute { } UNIMPLEMENTED_CLONE(TypeEraserAttr) + + bool isEquivalent(const TypeEraserAttr *other, Decl *attachedTo) const; }; /// Represents any sort of access control modifier. @@ -1286,6 +1343,10 @@ class AccessControlAttr : public AbstractAccessControlAttr { AccessControlAttr *clone(ASTContext &ctx) const { return new (ctx) AccessControlAttr(AtLoc, Range, getAccess(), isImplicit()); } + + bool isEquivalent(const AccessControlAttr *other, Decl *attachedTo) const { + return getAccess() == other->getAccess(); + } }; /// Represents a 'private', 'internal', or 'public' marker for a setter on a @@ -1305,6 +1366,10 @@ class SetterAccessAttr : public AbstractAccessControlAttr { SetterAccessAttr *clone(ASTContext &ctx) const { return new (ctx) SetterAccessAttr(AtLoc, Range, getAccess(), isImplicit()); } + + bool isEquivalent(const SetterAccessAttr *other, Decl *attachedTo) const { + return getAccess() == other->getAccess(); + } }; /// SPI attribute applied to both decls and imports. @@ -1341,6 +1406,8 @@ class SPIAccessControlAttr final : public DeclAttribute, static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::SPIAccessControl; } + + bool isEquivalent(const SPIAccessControlAttr *other, Decl *attachedTo) const; }; /// Represents an inline attribute. @@ -1363,6 +1430,10 @@ class InlineAttr : public DeclAttribute { InlineAttr *clone(ASTContext &ctx) const { return new (ctx) InlineAttr(AtLoc, Range, getKind(), isImplicit()); } + + bool isEquivalent(const InlineAttr *other, Decl *attachedTo) const { + return getKind() == other->getKind(); + } }; /// Represents the optimize attribute. @@ -1387,6 +1458,10 @@ class OptimizeAttr : public DeclAttribute { OptimizeAttr *clone(ASTContext &ctx) const { return new (ctx) OptimizeAttr(AtLoc, Range, getMode(), isImplicit()); } + + bool isEquivalent(const OptimizeAttr *other, Decl *attachedTo) const { + return getMode() == other->getMode(); + } }; /// Represents the exclusivity attribute. @@ -1418,6 +1493,10 @@ class ExclusivityAttr : public DeclAttribute { ExclusivityAttr *clone(ASTContext &ctx) const { return new (ctx) ExclusivityAttr(AtLoc, Range, getMode(), isImplicit()); } + + bool isEquivalent(const ExclusivityAttr *other, Decl *attachedTo) const { + return getMode() == other->getMode(); + } }; /// Represents the side effects attribute. @@ -1464,6 +1543,14 @@ class EffectsAttr : public DeclAttribute { } return new (ctx) EffectsAttr(getKind()); } + + bool isEquivalent(const EffectsAttr *other, Decl *attachedTo) const { + if (getKind() != other->getKind()) + return false; + if (getKind() == EffectsKind::Custom) + return getCustomString() == other->getCustomString(); + return true; + } }; @@ -1492,6 +1579,11 @@ class ReferenceOwnershipAttr : public DeclAttribute { static bool classof(const DeclAttribute *DA) { return DA->getKind() == DeclAttrKind::ReferenceOwnership; } + + bool isEquivalent(const ReferenceOwnershipAttr *other, + Decl *attachedTo) const { + return get() == other->get(); + } }; /// Defines the attribute that we use to model documentation comments. @@ -1515,6 +1607,10 @@ class RawDocCommentAttr : public DeclAttribute { RawDocCommentAttr *clone(ASTContext &ctx) const { return new (ctx) RawDocCommentAttr(CommentRange, isImplicit()); } + + bool isEquivalent(const RawDocCommentAttr *other, Decl *attachedTo) const { + return getCommentRange() == other->getCommentRange(); + } }; /// An attribute applied to a CoreFoundation class that is toll-free bridged to @@ -1542,6 +1638,10 @@ class ObjCBridgedAttr : public DeclAttribute { ObjCBridgedAttr *clone(ASTContext &ctx) const { return new (ctx) ObjCBridgedAttr(ObjCClass); } + + bool isEquivalent(const ObjCBridgedAttr *other, Decl *attachedTo) const { + return getObjCClass() == other->getObjCClass(); + } }; /// An attribute that specifies a synthesized conformance of a known @@ -1586,6 +1686,13 @@ class SynthesizedProtocolAttr : public DeclAttribute { return new (ctx) SynthesizedProtocolAttr( protocol, getLazyLoader(), isUnchecked()); } + + bool isEquivalent(const SynthesizedProtocolAttr *other, + Decl *attachedTo) const { + return isUnchecked() == other->isUnchecked() + && getProtocol() == other->getProtocol() + && getLazyLoader() == other->getLazyLoader(); + } }; /// The @_specialize attribute, which forces specialization on the specified @@ -1719,6 +1826,8 @@ class SpecializeAttr final } UNIMPLEMENTED_CLONE(SpecializeAttr) + + bool isEquivalent(const SpecializeAttr *other, Decl *attachedTo) const; }; class StorageRestrictionsAttr final @@ -1763,6 +1872,9 @@ class StorageRestrictionsAttr final } UNIMPLEMENTED_CLONE(StorageRestrictionsAttr) + + bool isEquivalent(const StorageRestrictionsAttr *other, + Decl *attachedTo) const; }; /// The @_implements attribute, which treats a decl as the implementation for @@ -1817,6 +1929,8 @@ class ImplementsAttr : public DeclAttribute { auto dc = TyROrDC.dyn_cast(); return create(dc, getProtocol(dc), getMemberName()); } + + bool isEquivalent(const ImplementsAttr *other, Decl *attachedTo) const; }; /// A limited variant of \c \@objc that's used for classes with generic ancestry. @@ -1845,6 +1959,10 @@ class ObjCRuntimeNameAttr : public DeclAttribute { ObjCRuntimeNameAttr *clone(ASTContext &ctx) const { return new (ctx) ObjCRuntimeNameAttr(Name, AtLoc, Range, isImplicit()); } + + bool isEquivalent(const ObjCRuntimeNameAttr *other, Decl *attachedTo) const { + return Name == other->Name; + } }; /// Attribute that specifies a protocol conformance that has been restated @@ -1868,6 +1986,11 @@ class RestatedObjCConformanceAttr : public DeclAttribute { RestatedObjCConformanceAttr *clone(ASTContext &ctx) const { return new (ctx) RestatedObjCConformanceAttr(Proto); } + + bool isEquivalent(const RestatedObjCConformanceAttr *other, + Decl *attachedTo) const { + return Proto == other->Proto; + } }; /// Attached to type declarations synthesized by the Clang importer. @@ -1935,6 +2058,12 @@ class ClangImporterSynthesizedTypeAttr : public DeclAttribute { return new (ctx) ClangImporterSynthesizedTypeAttr( originalTypeName, getKind()); } + + bool isEquivalent(const ClangImporterSynthesizedTypeAttr *other, + Decl *attachedTo) const { + return getKind() == other->getKind() + && originalTypeName == other->originalTypeName; + } }; /// Defines a custom attribute. @@ -2006,6 +2135,8 @@ class CustomAttr final : public DeclAttribute { bool canClone() const { return argList == nullptr; } + bool isEquivalent(const CustomAttr *other, Decl *attachedTo) const; + private: friend class CustomAttrNominalRequest; void resetTypeInformation(TypeExpr *repr); @@ -2044,6 +2175,11 @@ class ProjectedValuePropertyAttr : public DeclAttribute { return new (ctx) ProjectedValuePropertyAttr( ProjectionPropertyName, AtLoc, Range, isImplicit()); } + + bool isEquivalent(const ProjectedValuePropertyAttr *other, + Decl *attachedTo) const { + return ProjectionPropertyName == other->ProjectionPropertyName; + } }; /// Describes a symbol that was originally defined in another module. For @@ -2109,6 +2245,14 @@ class OriginallyDefinedInAttr: public DeclAttribute { AtLoc, Range, ManglingModuleName, LinkerModuleName, Platform, MovedVersion, isImplicit()); } + + bool isEquivalent(const OriginallyDefinedInAttr *other, + Decl *attachedTo) const { + return ManglingModuleName == other->ManglingModuleName + && LinkerModuleName == other->LinkerModuleName + && Platform == other->Platform + && MovedVersion == other->MovedVersion; + } }; /// Attribute that marks a function as differentiable. @@ -2258,6 +2402,11 @@ class DifferentiableAttr final } UNIMPLEMENTED_CLONE(DifferentiableAttr) + + bool isEquivalent(const DifferentiableAttr *other, Decl *attachedTo) const { + // Not properly implemented (very complex and not currently needed) + return false; + } }; /// A declaration name with location. @@ -2399,6 +2548,11 @@ class DerivativeAttr final } UNIMPLEMENTED_CLONE(DerivativeAttr) + + bool isEquivalent(const DerivativeAttr *other, Decl *attachedTo) const { + // Not properly implemented (very complex and not currently needed) + return false; + } }; /// The `@transpose(of:)` attribute registers a function as a transpose of @@ -2485,6 +2639,11 @@ class TransposeAttr final } UNIMPLEMENTED_CLONE(TransposeAttr) + + bool isEquivalent(const TransposeAttr *other, Decl *attachedTo) const { + // Not properly implemented (very complex and not currently needed) + return false; + } }; enum class NonSendableKind : uint8_t { @@ -2520,6 +2679,10 @@ class NonSendableAttr : public DeclAttribute { NonSendableAttr *clone(ASTContext &ctx) const { return new (ctx) NonSendableAttr(AtLoc, Range, Specificity, isImplicit()); } + + bool isEquivalent(const NonSendableAttr *other, Decl *attachedTo) const { + return Specificity == other->Specificity; + } }; /// The @_unavailableFromAsync attribute, used to make function declarations @@ -2547,6 +2710,11 @@ class UnavailableFromAsyncAttr : public DeclAttribute { return new (ctx) UnavailableFromAsyncAttr( Message, AtLoc, Range, isImplicit()); } + + bool isEquivalent(const UnavailableFromAsyncAttr *other, + Decl *attachedTo) const { + return Message == other->Message; + } }; /// The `@backDeployed(...)` attribute, used to make function declarations @@ -2577,6 +2745,10 @@ class BackDeployedAttr : public DeclAttribute { return new (ctx) BackDeployedAttr( AtLoc, Range, Platform, Version, isImplicit()); } + + bool isEquivalent(const BackDeployedAttr *other, Decl *attachedTo) const { + return Platform == other->Platform && Version == Version; + } }; /// Defines the `@_expose` attribute, used to expose declarations in the @@ -2610,6 +2782,10 @@ class ExposeAttr : public DeclAttribute { return new (ctx) ExposeAttr( Name, AtLoc, Range, getExposureKind(), isImplicit()); } + + bool isEquivalent(const ExposeAttr *other, Decl *attachedTo) const { + return Name == other->Name && getExposureKind() == other->getExposureKind(); + } }; /// Define the `@_extern` attribute, used to import external declarations in @@ -2665,6 +2841,11 @@ class ExternAttr : public DeclAttribute { ModuleName, Name, AtLoc, getLParenLoc(), getRParenLoc(), Range, getExternKind(), isImplicit()); } + + bool isEquivalent(const ExternAttr *other, Decl *attachedTo) const { + return getExternKind() == other->getExternKind() + && ModuleName == other->ModuleName && Name == other->Name; + } }; /// The `@_documentation(...)` attribute, used to override a symbol's visibility @@ -2693,6 +2874,10 @@ class DocumentationAttr: public DeclAttribute { return new (ctx) DocumentationAttr( AtLoc, Range, Metadata, Visibility, isImplicit()); } + + bool isEquivalent(const DocumentationAttr *other, Decl *attachedTo) const { + return Metadata == other->Metadata && Visibility == other->Visibility; + } }; class ObjCImplementationAttr final : public DeclAttribute { @@ -2747,6 +2932,12 @@ class ObjCImplementationAttr final : public DeclAttribute { CategoryName, AtLoc, Range, isEarlyAdopter(), isImplicit(), isCategoryNameInvalid()); } + + bool isEquivalent(const ObjCImplementationAttr *other, + Decl *attachedTo) const { + // Ignoring `CategoryName` as it gets copied to @objc + return isEarlyAdopter() == other->isEarlyAdopter(); + } }; /// Represents nonisolated modifier. @@ -2772,6 +2963,10 @@ class NonisolatedAttr final : public DeclAttribute { NonisolatedAttr *clone(ASTContext &ctx) const { return new (ctx) NonisolatedAttr(AtLoc, Range, isUnsafe(), isImplicit()); } + + bool isEquivalent(const NonisolatedAttr *other, Decl *attachedTo) const { + return isUnsafe() == other->isUnsafe(); + } }; /// A macro role attribute, spelled with either @attached or @freestanding, @@ -2825,6 +3020,11 @@ class MacroRoleAttr final } UNIMPLEMENTED_CLONE(MacroRoleAttr) + + bool isEquivalent(const MacroRoleAttr *other, Decl *attachedTo) const { + // Unimplemented: complex and not immediately needed. + return true; + } }; /// Specifies the raw storage used by a type. @@ -2928,6 +3128,8 @@ class RawLayoutAttr final : public DeclAttribute { } UNIMPLEMENTED_CLONE(RawLayoutAttr) + + bool isEquivalent(const RawLayoutAttr *other, Decl *attachedTo) const; }; class LifetimeAttr final : public DeclAttribute { @@ -2953,6 +3155,8 @@ class LifetimeAttr final : public DeclAttribute { LifetimeAttr *clone(ASTContext &ctx) const { return new (ctx) LifetimeAttr(AtLoc, Range, isImplicit(), entry); } + + bool isEquivalent(const LifetimeAttr *other, Decl *attachedTo) const; }; /// Predicate used to filter MatchingAttributeRange. @@ -3001,6 +3205,9 @@ class AllowFeatureSuppressionAttr final } UNIMPLEMENTED_CLONE(AllowFeatureSuppressionAttr) + + bool isEquivalent(const AllowFeatureSuppressionAttr *other, + Decl *attachedTo) const; }; /// Defines the @abi attribute. @@ -3026,6 +3233,11 @@ class ABIAttr : public DeclAttribute { } UNIMPLEMENTED_CLONE(ABIAttr) + + bool isEquivalent(const ABIAttr *other, Decl *attachedTo) const { + // Unsupported: tricky to implement and unneeded. + return true; + } }; class ExecutionAttr : public DeclAttribute { @@ -3050,6 +3262,10 @@ class ExecutionAttr : public DeclAttribute { } UNIMPLEMENTED_CLONE(ExecutionAttr) + + bool isEquivalent(const ExecutionAttr *other, Decl *attachedTo) const { + return getBehavior() == other->getBehavior(); + } }; /// Attributes that may be applied to declarations. diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index eac91aa303a4d..8f832dd20319a 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -34,6 +34,7 @@ #include "swift/Basic/Defer.h" #include "swift/Basic/QuotedString.h" #include "swift/Strings.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" @@ -392,6 +393,27 @@ bool DeclAttribute::canClone() const { } } +// Ensure that every DeclAttribute subclass implements its own isEquivalent(). +static void checkDeclAttributeIsEquivalents() { +#define DECL_ATTR(_,CLASS,...) \ + bool(CLASS##Attr::*ptr##CLASS)(const CLASS##Attr *, Decl *) const = &CLASS##Attr::isEquivalent; \ + (void)ptr##CLASS; +#include "swift/AST/DeclAttr.def" +} + +bool DeclAttribute::isEquivalent(const DeclAttribute *other, Decl *attachedTo) const { + (void)checkDeclAttributeIsEquivalents; + if (getKind() != other->getKind()) + return false; + switch (getKind()) { +#define DECL_ATTR(_,CLASS, ...) \ + case DeclAttrKind::CLASS:\ + return static_cast(this)->isEquivalent(\ + static_cast(other), attachedTo); +#include "swift/AST/DeclAttr.def" + } +} + const BackDeployedAttr * DeclAttributes::getBackDeployed(const ASTContext &ctx, bool forTargetVariant) const { @@ -2121,6 +2143,26 @@ Type TypeEraserAttr::getResolvedType(const ProtocolDecl *PD) const { ErrorType::get(ctx)); } +static bool eqTypes(Type first, Type second) { + if (first.isNull() || second.isNull()) + return false; + return first->getCanonicalType() == second->getCanonicalType(); +} + +bool TypeEraserAttr::isEquivalent(const TypeEraserAttr *other, + Decl *attachedTo) const { + // Only makes sense when attached to a protocol. + auto proto = dyn_cast(attachedTo); + if (!proto) + return true; + + Type thisType = getResolvedType(proto), + otherType = other->getResolvedType(proto); + if (thisType.isNull() || otherType.isNull()) + return false; + return thisType->getCanonicalType() == otherType->getCanonicalType(); +} + Type RawLayoutAttr::getResolvedLikeType(StructDecl *sd) const { auto &ctx = sd->getASTContext(); return evaluateOrDefault(ctx.evaluator, @@ -2139,6 +2181,34 @@ Type RawLayoutAttr::getResolvedCountType(StructDecl *sd) const { ErrorType::get(ctx)); } +bool RawLayoutAttr::isEquivalent(const RawLayoutAttr *other, + Decl *attachedTo) const { + if (shouldMoveAsLikeType() != other->shouldMoveAsLikeType()) + return false; + + if (auto thisSizeAndAlignment = getSizeAndAlignment()) + return thisSizeAndAlignment == other->getSizeAndAlignment(); + + auto SD = dyn_cast(attachedTo); + if (!SD) + return true; + + if (auto thisScalarLikeType = getResolvedScalarLikeType(SD)) { + auto otherScalarLikeType = other->getResolvedScalarLikeType(SD); + return otherScalarLikeType && eqTypes(*thisScalarLikeType, + *otherScalarLikeType); + } + + if (auto thisArrayLikeTypes = getResolvedArrayLikeTypeAndCount(SD)) { + auto otherArrayLikeTypes = other->getResolvedArrayLikeTypeAndCount(SD); + return otherArrayLikeTypes + && eqTypes(thisArrayLikeTypes->first, otherArrayLikeTypes->first) + && eqTypes(thisArrayLikeTypes->second, otherArrayLikeTypes->second); + } + + llvm_unreachable("unknown variant of RawLayoutAttr"); +} + AvailableAttr::AvailableAttr( SourceLoc AtLoc, SourceRange Range, AvailabilityDomainOrIdentifier DomainOrIdentifier, SourceLoc DomainLoc, @@ -2246,6 +2316,19 @@ AvailableAttr *AvailableAttr::clone(ASTContext &C, bool implicit) const { implicit ? SourceRange() : ObsoletedRange, implicit, isSPI()); } +bool AvailableAttr::isEquivalent(const AvailableAttr *other, + Decl *attachedTo) const { + return getRawIntroduced() == other->getRawIntroduced() + && getRawDeprecated() == other->getRawDeprecated() + && getRawObsoleted() == other->getRawObsoleted() + && getMessage() == other->getMessage() + && getRename() == other->getRename() + && isSPI() == other->isSPI() + && getKind() == other->getKind() + && attachedTo->getSemanticAvailableAttr(this)->getDomain() + == attachedTo->getSemanticAvailableAttr(other)->getDomain(); +} + static StringRef getManglingModuleName(StringRef OriginalModuleName) { auto index = OriginalModuleName.find(";"); return index == StringRef::npos @@ -2448,6 +2531,69 @@ GenericSignature SpecializeAttr::getSpecializedSignature( nullptr); } +/// Checks whether \p first and \p second have the same elements according to +/// \p eq , ignoring the order those elements are in but considering the number +/// of repetitions. +template +bool sameElements(ArrayRef first, ArrayRef second, Eq eq) { + if (first.size() != second.size()) + return false; + + llvm::SmallSet claimedSecondIndices; + + for (auto firstElem : first) { + bool found = false; + for (auto i : indices(second)) { + if (!eq(firstElem, second[i]) || claimedSecondIndices.count(i)) + continue; + + found = true; + claimedSecondIndices.insert(i); + break; + } + + if (!found) + return false; + } + + return true; +} + +/// Checks whether \p first and \p second have the same elements, ignoring the +/// order those elements are in but considering the number of repetitions. +template +bool sameElements(ArrayRef first, ArrayRef second) { + return sameElements(first, second, std::equal_to()); +} + +bool SpecializeAttr::isEquivalent(const SpecializeAttr *other, + Decl *attachedTo) const { + if (isExported() != other->isExported() || + getSpecializationKind() != other->getSpecializationKind() || + getTargetFunctionName() != other->getTargetFunctionName() || + specializedSignature.getCanonicalSignature() != + other->specializedSignature.getCanonicalSignature()) + return false; + + if (!sameElements(getSPIGroups(), other->getSPIGroups())) + return false; + + SmallVector thisTypeErasedParams, otherTypeErasedParams; + for (auto ty : getTypeErasedParams()) + thisTypeErasedParams.push_back(ty->getCanonicalType()); + for (auto ty : other->getTypeErasedParams()) + otherTypeErasedParams.push_back(ty->getCanonicalType()); + + if (!sameElements(ArrayRef(thisTypeErasedParams), + ArrayRef(otherTypeErasedParams))) + return false; + + return sameElements(getAvailableAttrs(), other->getAvailableAttrs(), + [=](AvailableAttr *thisAttr, AvailableAttr *otherAttr) { + return thisAttr->isEquivalent(otherAttr, attachedTo); + }); +} + SPIAccessControlAttr::SPIAccessControlAttr(SourceLoc atLoc, SourceRange range, ArrayRef spiGroups) : DeclAttribute(DeclAttrKind::SPIAccessControl, atLoc, range, @@ -2476,6 +2622,11 @@ SPIAccessControlAttr *SPIAccessControlAttr::clone(ASTContext &C, return attr; } +bool SPIAccessControlAttr::isEquivalent(const SPIAccessControlAttr *other, + Decl *attachedTo) const { + return sameElements(getSPIGroups(), other->getSPIGroups()); +} + DifferentiableAttr::DifferentiableAttr(bool implicit, SourceLoc atLoc, SourceRange baseRange, enum DifferentiabilityKind diffKind, @@ -2712,6 +2863,12 @@ StorageRestrictionsAttr::create( /*implicit=*/false); } +bool StorageRestrictionsAttr:: +isEquivalent(const StorageRestrictionsAttr *other, Decl *attachedTo) const { + return sameElements(getAccessesNames(), other->getAccessesNames()) + && sameElements(getInitializesNames(), other->getInitializesNames()); +} + ImplementsAttr:: ImplementsAttr(SourceLoc atLoc, SourceRange range, llvm::PointerUnion TyROrDC, @@ -2754,6 +2911,13 @@ ImplementsAttr::getCachedProtocol(DeclContext *dc) const { return std::nullopt; } +bool ImplementsAttr::isEquivalent(const ImplementsAttr *other, + Decl *attachedTo) const { + auto DC = attachedTo->getDeclContext(); + return getMemberName() == other->getMemberName() + && getProtocol(DC) == other->getProtocol(DC); +} + CustomAttr::CustomAttr(SourceLoc atLoc, SourceRange range, TypeExpr *type, CustomAttributeInitializer *initContext, ArgumentList *argList, bool implicit) @@ -2822,6 +2986,14 @@ bool CustomAttr::isArgUnsafe() const { return isArgUnsafeBit; } +bool CustomAttr::isEquivalent(const CustomAttr *other, Decl *attachedTo) const { + // For the sake of both @abi checking and implementability, we're going to + // ignore expressions in the arguments and initializer. + + return isArgUnsafe() == other->isArgUnsafe() && eqTypes(getType(), + other->getType()); +} + MacroRoleAttr::MacroRoleAttr(SourceLoc atLoc, SourceRange range, MacroSyntax syntax, SourceLoc lParenLoc, MacroRole role, @@ -2949,12 +3121,27 @@ AllowFeatureSuppressionAttr *AllowFeatureSuppressionAttr::create( AllowFeatureSuppressionAttr(atLoc, range, implicit, inverted, features); } +bool AllowFeatureSuppressionAttr:: +isEquivalent(const AllowFeatureSuppressionAttr *other, Decl *attachedTo) const { + if (getInverted() != other->getInverted()) + return false; + + return sameElements(getSuppressedFeatures(), other->getSuppressedFeatures()); +} + LifetimeAttr *LifetimeAttr::create(ASTContext &context, SourceLoc atLoc, SourceRange baseRange, bool implicit, LifetimeEntry *entry) { return new (context) LifetimeAttr(atLoc, baseRange, implicit, entry); } +bool LifetimeAttr::isEquivalent(const LifetimeAttr *other, + Decl *attachedTo) const { + // FIXME: This is kind of cheesy. + return getLifetimeEntry()->getString() + == other->getLifetimeEntry()->getString(); +} + void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) { if (attr) attr->print(out); From 32b8a11b11e9277cf827345e6c70f7be4867ad2e Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 12 Mar 2025 16:29:03 -0700 Subject: [PATCH 14/22] Add flags to specify `@abi` behavior of attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds a set of DeclAttr.def flags for specifying how a given attribute interacts with `@abi`, and declares a behavior for each existing attribute. Future attributes will be *required* to declare an `@abi` behavior lest they fail a static assert. Note that the behavior is not actually enforced in this commit—it is merely specified here. --- include/swift/AST/Attr.h | 35 ++++ include/swift/AST/DeclAttr.def | 311 +++++++++++++++++---------------- lib/AST/Attr.cpp | 4 +- 3 files changed, 194 insertions(+), 156 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 17c9883b5891d..11e80605567ea 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -364,6 +364,41 @@ class DeclAttribute : public AttributeBase { /// The opposite of ABIBreakingToRemove ABIStableToRemove = 1ull << 15, + + /// Attribute should not be used in an \c \@abi attribute. Use for + /// attributes which cannot affect mangled names, even indirectly, and + /// which either don't affect ABI or where ABI-only declarations get their + /// behavior from their API counterpart. + ForbiddenInABIAttr = 1ull << 16, + + /// Attribute can be used without restrictions in an \c \@abi attribute. + /// Use for attributes which affect mangled names but otherwise don't alter + /// the ABI, or ones where the \c ABIDeclChecker manually implements + /// special checking logic (e.g. because several different attributes + /// contribute to the same aspect of ABI in some complicated way). + UnconstrainedInABIAttr = 1ull << 17, + + /// Attribute can be used in an \c \@abi attribute, but must match + /// equivalent on API decl. Use for attributes which affect both mangled + /// names and other parts of the ABI such that the declaration can only be + /// valid if they match. + EquivalentInABIAttr = 1ull << 18, + + /// Attribute can be used in an \c \@abi attribute, but must match + /// equivalent on API decl; if omitted, API decl's attribute will be + /// cloned. Use where you would want to use \c EquivalentInABIAttr but + /// repeating the attribute is judged too burdensome. + InferredInABIAttr = 1ull << 19, + + /// Use for attributes which are \em only valid on declarations that cannot + /// have an \c @abi attribute, such as \c ImportDecl . + UnreachableInABIAttr = 1ull << 20, + }; + + enum : uint64_t { + InABIAttrMask = ForbiddenInABIAttr | UnconstrainedInABIAttr + | EquivalentInABIAttr | InferredInABIAttr + | UnreachableInABIAttr }; LLVM_READNONE diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index a7cb4d7d4eb59..f0785cce9b8ce 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -63,142 +63,142 @@ // leave an "Unused 'NNN'" comment so the unused code is obvious. DECL_ATTR(_silgen_name, SILGenName, OnAbstractFunction | OnVar, - LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 0) DECL_ATTR(available, Available, OnAbstractFunction | OnAssociatedType | OnGenericType | OnVar | OnSubscript | OnEnumElement | OnMacro | OnExtension, - AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 1) CONTEXTUAL_SIMPLE_DECL_ATTR(final, Final, OnClass | OnFunc | OnAccessor | OnVar | OnSubscript, - DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 2) DECL_ATTR(objc, ObjC, OnAbstractFunction | OnClass | OnProtocol | OnExtension | OnVar | OnSubscript | OnEnum | OnEnumElement, - ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 3) CONTEXTUAL_SIMPLE_DECL_ATTR(required, Required, OnConstructor, - DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | EquivalentInABIAttr, 4) CONTEXTUAL_SIMPLE_DECL_ATTR(optional, Optional, OnConstructor | OnFunc | OnAccessor | OnVar | OnSubscript, - DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 5) SIMPLE_DECL_ATTR(dynamicCallable, DynamicCallable, OnNominalType, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 6) SIMPLE_DECL_ATTR(main, MainType, OnClass | OnStruct | OnEnum | OnExtension, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 7) SIMPLE_DECL_ATTR(_exported, Exported, OnImport, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 8) SIMPLE_DECL_ATTR(dynamicMemberLookup, DynamicMemberLookup, OnNominalType, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 9) SIMPLE_DECL_ATTR(NSCopying, NSCopying, OnVar, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 10) SIMPLE_DECL_ATTR(IBAction, IBAction, OnFunc, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 11) SIMPLE_DECL_ATTR(IBDesignable, IBDesignable, OnClass | OnExtension, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 12) SIMPLE_DECL_ATTR(IBInspectable, IBInspectable, OnVar, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 13) SIMPLE_DECL_ATTR(IBOutlet, IBOutlet, OnVar, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 14) SIMPLE_DECL_ATTR(NSManaged, NSManaged, OnVar | OnFunc | OnAccessor, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 15) CONTEXTUAL_SIMPLE_DECL_ATTR(lazy, Lazy, OnVar, - DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, 16) SIMPLE_DECL_ATTR(LLDBDebuggerFunction, LLDBDebuggerFunction, OnFunc, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 17) SIMPLE_DECL_ATTR(UIApplicationMain, UIApplicationMain, OnClass, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 18) SIMPLE_DECL_ATTR(unsafe_no_objc_tagged_pointer, UnsafeNoObjCTaggedPointer, OnProtocol, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 19) DECL_ATTR(inline, Inline, OnVar | OnSubscript | OnAbstractFunction, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, 20) DECL_ATTR(_semantics, Semantics, OnAbstractFunction | OnSubscript | OnNominalType | OnVar, - AllowMultipleAttributes | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + AllowMultipleAttributes | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 21) CONTEXTUAL_SIMPLE_DECL_ATTR(dynamic, Dynamic, OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor, - DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 22) CONTEXTUAL_SIMPLE_DECL_ATTR(infix, Infix, OnFunc | OnOperator, - DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 23) CONTEXTUAL_SIMPLE_DECL_ATTR(prefix, Prefix, OnFunc | OnOperator, - DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 24) CONTEXTUAL_SIMPLE_DECL_ATTR(postfix, Postfix, OnFunc | OnOperator, - DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 25) SIMPLE_DECL_ATTR(_transparent, Transparent, OnFunc | OnAccessor | OnConstructor | OnVar | OnDestructor, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, 26) SIMPLE_DECL_ATTR(requires_stored_property_inits, RequiresStoredPropertyInits, OnClass, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 27) // Unused '28' @@ -206,37 +206,37 @@ SIMPLE_DECL_ATTR(requires_stored_property_inits, RequiresStoredPropertyInits, SIMPLE_DECL_ATTR(nonobjc, NonObjC, OnExtension | OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 30) SIMPLE_DECL_ATTR(_fixed_layout, FixedLayout, OnVar | OnClass | OnStruct | OnProtocol, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 31) SIMPLE_DECL_ATTR(inlinable, Inlinable, OnVar | OnSubscript | OnAbstractFunction, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, 32) DECL_ATTR(_specialize, Specialize, OnConstructor | OnFunc | OnAccessor, - AllowMultipleAttributes | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + AllowMultipleAttributes | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 33) SIMPLE_DECL_ATTR(objcMembers, ObjCMembers, OnClass, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 34) CONTEXTUAL_SIMPLE_DECL_ATTR(_compilerInitialized, CompilerInitialized, OnVar, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 35) SIMPLE_DECL_ATTR(_lexicalLifetimes, LexicalLifetimes, OnFunc, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 36) // Unused '37' @@ -245,37 +245,37 @@ SIMPLE_DECL_ATTR(_lexicalLifetimes, LexicalLifetimes, CONTEXTUAL_SIMPLE_DECL_ATTR(__consuming, LegacyConsuming, OnFunc | OnAccessor, - DeclModifier | UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 40) CONTEXTUAL_SIMPLE_DECL_ATTR(mutating, Mutating, OnFunc | OnAccessor, - DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 41) CONTEXTUAL_SIMPLE_DECL_ATTR(nonmutating, NonMutating, OnFunc | OnAccessor, - DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 42) CONTEXTUAL_SIMPLE_DECL_ATTR(convenience, Convenience, OnConstructor, - DeclModifier | NotSerialized | ABIBreakingToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | NotSerialized | ABIBreakingToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | EquivalentInABIAttr, 43) CONTEXTUAL_SIMPLE_DECL_ATTR(override, Override, OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor | OnAssociatedType, - DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 44) SIMPLE_DECL_ATTR(_hasStorage, HasStorage, OnVar, - UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 45) DECL_ATTR(private, AccessControl, OnFunc | OnAccessor | OnExtension | OnGenericType | OnVar | OnSubscript | OnConstructor | OnMacro | OnImport, - DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 46) DECL_ATTR_ALIAS(fileprivate, AccessControl) DECL_ATTR_ALIAS(internal, AccessControl) @@ -285,540 +285,541 @@ CONTEXTUAL_DECL_ATTR_ALIAS(open, AccessControl) DECL_ATTR(__setter_access, SetterAccess, OnVar | OnSubscript, - DeclModifier | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 47) DECL_ATTR(__raw_doc_comment, RawDocComment, OnAnyDecl, - UserInaccessible | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 48) CONTEXTUAL_DECL_ATTR(weak, ReferenceOwnership, OnVar, - DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 49) CONTEXTUAL_DECL_ATTR_ALIAS(unowned, ReferenceOwnership) DECL_ATTR(_effects, Effects, OnAbstractFunction, - AllowMultipleAttributes | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + AllowMultipleAttributes | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 50) DECL_ATTR(__objc_bridged, ObjCBridged, OnClass, - UserInaccessible | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 51) SIMPLE_DECL_ATTR(NSApplicationMain, NSApplicationMain, OnClass, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 52) SIMPLE_DECL_ATTR(_objc_non_lazy_realization, ObjCNonLazyRealization, OnClass, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 53) DECL_ATTR(__synthesized_protocol, SynthesizedProtocol, OnConcreteNominalType, - UserInaccessible | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 54) SIMPLE_DECL_ATTR(testable, Testable, OnImport, - UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 55) DECL_ATTR(_alignment, Alignment, OnStruct | OnEnum, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 56) SIMPLE_DECL_ATTR(rethrows, Rethrows, OnFunc | OnConstructor, - DeclModifier | RejectByParser | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + DeclModifier | RejectByParser | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 57) SIMPLE_DECL_ATTR(rethrows, AtRethrows, OnProtocol, - ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnreachableInABIAttr, 58) DECL_ATTR(_swift_native_objc_runtime_base, SwiftNativeObjCRuntimeBase, OnClass, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 59) CONTEXTUAL_SIMPLE_DECL_ATTR(indirect, Indirect, OnEnum | OnEnumElement, - DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 60) SIMPLE_DECL_ATTR(warn_unqualified_access, WarnUnqualifiedAccess, OnFunc | OnAccessor, - LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 61) SIMPLE_DECL_ATTR(_show_in_interface, ShowInInterface, OnProtocol, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 62) DECL_ATTR(_cdecl, CDecl, OnFunc | OnAccessor, - LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 63) SIMPLE_DECL_ATTR(usableFromInline, UsableFromInline, OnAbstractFunction | OnVar | OnSubscript | OnNominalType | OnTypeAlias, - LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 64) SIMPLE_DECL_ATTR(discardableResult, DiscardableResult, OnFunc | OnAccessor | OnConstructor | OnMacro, - LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 65) SIMPLE_DECL_ATTR(GKInspectable, GKInspectable, OnVar, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 66) DECL_ATTR(_implements, Implements, OnFunc | OnAccessor | OnVar | OnSubscript | OnTypeAlias, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 67) DECL_ATTR(_objcRuntimeName, ObjCRuntimeName, OnClass, - UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 68) SIMPLE_DECL_ATTR(_staticInitializeObjCMetadata, StaticInitializeObjCMetadata, OnClass, - UserInaccessible | LongAttribute | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | LongAttribute | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 69) DECL_ATTR(_restatedObjCConformance, RestatedObjCConformance, OnProtocol, - UserInaccessible | LongAttribute | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | LongAttribute | RejectByParser | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 70) // Unused '71' DECL_ATTR(implementation, ObjCImplementation, OnExtension | OnAbstractFunction, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 72) DECL_ATTR_ALIAS(_objcImplementation, ObjCImplementation) DECL_ATTR(_optimize, Optimize, OnAbstractFunction | OnSubscript | OnVar, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 73) DECL_ATTR(_clangImporterSynthesizedType, ClangImporterSynthesizedType, OnGenericType, - LongAttribute | RejectByParser | UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + LongAttribute | RejectByParser | UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 74) SIMPLE_DECL_ATTR(_weakLinked, WeakLinked, OnNominalType | OnAssociatedType | OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor | OnEnumElement | OnExtension | OnImport, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 75) SIMPLE_DECL_ATTR(frozen, Frozen, OnEnum | OnStruct, - ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToRemove | APIStableToAdd, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToRemove | APIStableToAdd | UnreachableInABIAttr, 76) - DECL_ATTR_ALIAS(_frozen, Frozen) + SIMPLE_DECL_ATTR(_forbidSerializingReference, ForbidSerializingReference, OnAnyDecl, - LongAttribute | RejectByParser | UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + LongAttribute | RejectByParser | UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 77) SIMPLE_DECL_ATTR(_hasInitialValue, HasInitialValue, OnVar, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 78) SIMPLE_DECL_ATTR(_nonoverride, NonOverride, OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor | OnAssociatedType, - UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 79) DECL_ATTR(_dynamicReplacement, DynamicReplacement, OnAbstractFunction | OnVar | OnSubscript, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 80) SIMPLE_DECL_ATTR(_borrowed, Borrowed, OnVar | OnSubscript, - UserInaccessible | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, 81) DECL_ATTR(_private, PrivateImport, OnImport, - UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 82) SIMPLE_DECL_ATTR(_alwaysEmitIntoClient, AlwaysEmitIntoClient, OnVar | OnSubscript | OnAbstractFunction, - UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | InferredInABIAttr, 83) SIMPLE_DECL_ATTR(_implementationOnly, ImplementationOnly, OnImport | OnFunc | OnConstructor | OnVar | OnSubscript, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 84) DECL_ATTR(_custom, Custom, OnAnyDecl, - RejectByParser | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + RejectByParser | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 85) SIMPLE_DECL_ATTR(propertyWrapper, PropertyWrapper, OnStruct | OnClass | OnEnum, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 86) SIMPLE_DECL_ATTR(_disfavoredOverload, DisfavoredOverload, OnAbstractFunction | OnVar | OnSubscript, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 87) SIMPLE_DECL_ATTR(resultBuilder, ResultBuilder, OnNominalType, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 88) DECL_ATTR(_projectedValueProperty, ProjectedValueProperty, OnVar, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 89) SIMPLE_DECL_ATTR(_nonEphemeral, NonEphemeral, OnParam, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr, 90) DECL_ATTR(differentiable, Differentiable, OnAccessor | OnConstructor | OnFunc | OnVar | OnSubscript, - LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 91) SIMPLE_DECL_ATTR(_hasMissingDesignatedInitializers, HasMissingDesignatedInitializers, OnClass, - UserInaccessible | NotSerialized | APIBreakingToAdd | ABIBreakingToAdd | APIStableToRemove | ABIStableToRemove, + UserInaccessible | NotSerialized | APIBreakingToAdd | ABIBreakingToAdd | APIStableToRemove | ABIStableToRemove | UnreachableInABIAttr, 92) SIMPLE_DECL_ATTR(_inheritsConvenienceInitializers, InheritsConvenienceInitializers, OnClass, - UserInaccessible | NotSerialized | APIStableToAdd | ABIStableToAdd | APIBreakingToRemove | ABIBreakingToRemove, + UserInaccessible | NotSerialized | APIStableToAdd | ABIStableToAdd | APIBreakingToRemove | ABIBreakingToRemove | UnreachableInABIAttr, 93) DECL_ATTR(_typeEraser, TypeEraser, OnProtocol, - UserInaccessible | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | AllowMultipleAttributes, + UserInaccessible | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | AllowMultipleAttributes | UnreachableInABIAttr, 94) SIMPLE_DECL_ATTR(IBSegueAction, IBSegueAction, OnFunc, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 95) DECL_ATTR(_originallyDefinedIn, OriginallyDefinedIn, OnNominalType | OnFunc | OnVar | OnExtension, - UserInaccessible | AllowMultipleAttributes | LongAttribute | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | AllowMultipleAttributes | LongAttribute | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 96) DECL_ATTR(derivative, Derivative, OnFunc, - LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 97) DECL_ATTR(_spi, SPIAccessControl, OnAbstractFunction | OnExtension | OnGenericType | OnVar | OnSubscript | OnImport | OnAccessor | OnEnumElement | OnMacro, - AllowMultipleAttributes | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + AllowMultipleAttributes | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr, 98) DECL_ATTR(transpose, Transpose, OnFunc, - LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + LongAttribute | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 99) SIMPLE_DECL_ATTR(noDerivative, NoDerivative, OnAbstractFunction | OnVar | OnSubscript, - ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 100) // Unused '101' CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor, OnClass, - DeclModifier | ConcurrencyOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + DeclModifier | ConcurrencyOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnreachableInABIAttr, 102) CONTEXTUAL_SIMPLE_DECL_ATTR(isolated, Isolated, OnDestructor, - DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnreachableInABIAttr, 103) SIMPLE_DECL_ATTR(globalActor, GlobalActor, OnClass | OnStruct | OnEnum, - ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | UnreachableInABIAttr, 104) SIMPLE_DECL_ATTR(_specializeExtension, SpecializeExtension, OnExtension, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 105) CONTEXTUAL_SIMPLE_DECL_ATTR(async, Async, OnVar | OnFunc, - DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 106) SIMPLE_DECL_ATTR(Sendable, Sendable, OnFunc | OnConstructor | OnAccessor | OnAnyClangDecl, - ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 107) + SIMPLE_DECL_ATTR(_marker, Marker, OnProtocol, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnreachableInABIAttr, 108) SIMPLE_DECL_ATTR(reasync, Reasync, OnFunc | OnConstructor, - RejectByParser | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + RejectByParser | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 109) SIMPLE_DECL_ATTR(reasync, AtReasync, OnProtocol, - ConcurrencyOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + ConcurrencyOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnreachableInABIAttr, 110) // Unused '111' CONTEXTUAL_DECL_ATTR(nonisolated, Nonisolated, OnFunc | OnConstructor | OnDestructor | OnVar | OnSubscript | OnProtocol | OnExtension | OnClass | OnStruct | OnEnum, - DeclModifier | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + DeclModifier | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | UnconstrainedInABIAttr, 112) // Unused '113' SIMPLE_DECL_ATTR(_unsafeInheritExecutor, UnsafeInheritExecutor, OnFunc, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 114) SIMPLE_DECL_ATTR(_implicitSelfCapture, ImplicitSelfCapture, OnParam, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 115) SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext, OnParam, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 116) SIMPLE_DECL_ATTR(_eagerMove, EagerMove, OnFunc | OnParam | OnVar | OnNominalType, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 117) CONTEXTUAL_SIMPLE_DECL_ATTR(distributed, DistributedActor, OnClass | OnFunc | OnAccessor | OnVar, - DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + DeclModifier | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | EquivalentInABIAttr, 118) SIMPLE_DECL_ATTR(_noEagerMove, NoEagerMove, OnFunc | OnParam | OnVar | OnNominalType, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 119) SIMPLE_DECL_ATTR(_assemblyVision, EmitAssemblyVisionRemarks, OnFunc | OnNominalType, - UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 120) DECL_ATTR(_nonSendable, NonSendable, OnNominalType, - UserInaccessible | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + UserInaccessible | AllowMultipleAttributes | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 121) SIMPLE_DECL_ATTR(_noImplicitCopy, NoImplicitCopy, OnFunc | OnParam | OnVar, - UserInaccessible | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove, + UserInaccessible | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 122) SIMPLE_DECL_ATTR(_noLocks, NoLocks, OnAbstractFunction | OnSubscript, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 123) SIMPLE_DECL_ATTR(_noAllocation, NoAllocation, OnAbstractFunction | OnSubscript, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 124) SIMPLE_DECL_ATTR(preconcurrency, Preconcurrency, OnFunc | OnConstructor | OnProtocol | OnGenericType | OnVar | OnSubscript | OnEnumElement | OnImport, - ABIStableToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + ABIStableToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 125) CONTEXTUAL_SIMPLE_DECL_ATTR(_const, CompileTimeLiteral, OnParam | OnVar, - DeclModifier | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + DeclModifier | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | UnconstrainedInABIAttr, 126) DECL_ATTR(_unavailableFromAsync, UnavailableFromAsync, OnFunc | OnConstructor | OnMacro, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | ForbiddenInABIAttr, 127) DECL_ATTR(exclusivity, Exclusivity, OnVar, - ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 128) DECL_ATTR(backDeployed, BackDeployed, OnAbstractFunction | OnAccessor | OnSubscript | OnVar, - AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove, + AllowMultipleAttributes | LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 129) DECL_ATTR_ALIAS(_backDeploy, BackDeployed) CONTEXTUAL_SIMPLE_DECL_ATTR(_local, KnownToBeLocal, OnFunc | OnParam | OnVar, - DeclModifier | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + DeclModifier | UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 130) SIMPLE_DECL_ATTR(_moveOnly, MoveOnly, OnNominalType, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnreachableInABIAttr, 131) SIMPLE_DECL_ATTR(_alwaysEmitConformanceMetadata, AlwaysEmitConformanceMetadata, OnProtocol, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 132) DECL_ATTR(_expose, Expose, OnFunc | OnNominalType | OnVar | OnConstructor, - AllowMultipleAttributes | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + AllowMultipleAttributes | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 133) // Unused '134' SIMPLE_DECL_ATTR(_spiOnly, SPIOnly, OnImport, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 135) DECL_ATTR(_documentation, Documentation, OnAnyDecl, - UserInaccessible | APIBreakingToAdd | APIStableToRemove | ABIStableToAdd | ABIStableToRemove, + UserInaccessible | APIBreakingToAdd | APIStableToRemove | ABIStableToAdd | ABIStableToRemove | ForbiddenInABIAttr, 136) // Unused '137' SIMPLE_DECL_ATTR(_noMetadata, NoMetadata, OnGenericTypeParam, - UserInaccessible | NotSerialized | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | NotSerialized | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 138) // Unused '139' CONTEXTUAL_SIMPLE_DECL_ATTR(consuming, Consuming, OnFunc | OnAccessor, - DeclModifier | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 140) CONTEXTUAL_SIMPLE_DECL_ATTR(borrowing, Borrowing, OnFunc | OnAccessor, - DeclModifier | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + DeclModifier | NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 141) DECL_ATTR(attached, MacroRole, OnMacro, - AllowMultipleAttributes | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove, + AllowMultipleAttributes | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | UnreachableInABIAttr, 142) DECL_ATTR_ALIAS(freestanding, MacroRole) SIMPLE_DECL_ATTR(_used, Used, OnAbstractFunction | OnVar, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 143) DECL_ATTR(_section, Section, OnAbstractFunction | OnVar, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | ForbiddenInABIAttr, 144) DECL_ATTR(storageRestrictions, StorageRestrictions, OnAccessor, - ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove, + ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove | UnreachableInABIAttr, 145) DECL_ATTR(_rawLayout, RawLayout, OnStruct, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 146) DECL_ATTR(_extern, Extern, OnFunc, - AllowMultipleAttributes | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + AllowMultipleAttributes | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 147) SIMPLE_DECL_ATTR(_nonescapable, NonEscapable, OnNominalType, - UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + UserInaccessible | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | UnreachableInABIAttr, 148) SIMPLE_DECL_ATTR(_unsafeNonescapableResult, UnsafeNonEscapableResult, OnAbstractFunction | OnSubscript | OnAccessor, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove | EquivalentInABIAttr, 149) // Unused '150' SIMPLE_DECL_ATTR(_staticExclusiveOnly, StaticExclusiveOnly, OnStruct, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | UnreachableInABIAttr, 151) SIMPLE_DECL_ATTR(extractConstantsFromMembers, ExtractConstantsFromMembers, OnClass | OnEnum | OnProtocol | OnStruct, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | UnreachableInABIAttr, 152) SIMPLE_DECL_ATTR(_noRuntime, NoRuntime, OnAbstractFunction | OnSubscript, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 153) SIMPLE_DECL_ATTR(_noExistentials, NoExistentials, OnAbstractFunction | OnSubscript, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 154) SIMPLE_DECL_ATTR(_noObjCBridging, NoObjCBridging, OnAbstractFunction | OnSubscript, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 155) // Unused '156': Used to be `_distributedThunkTarget` but completed implementation in Swift 6.0 does not need it after all DECL_ATTR(_allowFeatureSuppression, AllowFeatureSuppression, OnAnyDecl, - UserInaccessible | NotSerialized | ABIStableToAdd | APIStableToAdd | ABIStableToRemove | APIStableToRemove, + UserInaccessible | NotSerialized | ABIStableToAdd | APIStableToAdd | ABIStableToRemove | APIStableToRemove | ForbiddenInABIAttr, 157) DECL_ATTR_ALIAS(_disallowFeatureSuppression, AllowFeatureSuppression) SIMPLE_DECL_ATTR(_preInverseGenerics, PreInverseGenerics, OnAbstractFunction | OnSubscript | OnVar | OnExtension, - UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | UnconstrainedInABIAttr, 158) // Declares that a struct contains "sensitive" data. It enforces that the contents of such a struct value @@ -827,49 +828,49 @@ SIMPLE_DECL_ATTR(_preInverseGenerics, PreInverseGenerics, // TODO: enable @sensitive also for other nominal types than structs, e.g. for enums SIMPLE_DECL_ATTR(sensitive, Sensitive, OnStruct, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | UnreachableInABIAttr, 159) SIMPLE_DECL_ATTR(unsafe, Unsafe, OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType | OnExtension | OnTypeAlias | OnEnumElement, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 160) DECL_ATTR(lifetime, Lifetime, OnAccessor | OnConstructor | OnFunc | OnSubscript, - LongAttribute | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | AllowMultipleAttributes, + LongAttribute | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove | AllowMultipleAttributes | EquivalentInABIAttr, 161) SIMPLE_DECL_ATTR(_addressableSelf, AddressableSelf, OnAccessor | OnConstructor | OnFunc | OnSubscript, - ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UserInaccessible, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UserInaccessible | UnconstrainedInABIAttr, 162) SIMPLE_DECL_ATTR(_addressableForDependencies, AddressableForDependencies, OnNominalType, - ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UserInaccessible, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UserInaccessible | UnreachableInABIAttr, 163) SIMPLE_DECL_ATTR(safe, Safe, OnAbstractFunction | OnSubscript | OnVar | OnMacro | OnNominalType | OnExtension | OnEnumElement, - UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 164) DECL_ATTR(abi, ABI, OnAbstractFunction | OnVar, - LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 165) DECL_ATTR_FEATURE_REQUIREMENT(ABI, ABIAttribute) DECL_ATTR(execution, Execution, OnFunc | OnConstructor | OnSubscript | OnVar, - ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnconstrainedInABIAttr, 166) DECL_ATTR_FEATURE_REQUIREMENT(Execution, ExecutionAttribute) SIMPLE_DECL_ATTR(const, ConstVal, OnParam | OnVar | OnFunc, - ABIStableToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + ABIStableToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | EquivalentInABIAttr, 167) DECL_ATTR_FEATURE_REQUIREMENT(ConstVal, CompileTimeValues) SIMPLE_DECL_ATTR(constInitialized, ConstInitialized, diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 8f832dd20319a..22190f5228658 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -59,7 +59,9 @@ static_assert(IsTriviallyDestructible::value, #Name " needs to specify either APIBreakingToAdd or APIStableToAdd"); \ static_assert(DeclAttribute::hasOneBehaviorFor##Id( \ DeclAttribute::APIBreakingToRemove | DeclAttribute::APIStableToRemove), \ - #Name " needs to specify either APIBreakingToRemove or APIStableToRemove"); + #Name " needs to specify either APIBreakingToRemove or APIStableToRemove"); \ + static_assert(DeclAttribute::hasOneBehaviorFor##Id(DeclAttribute::InABIAttrMask), \ + #Name " needs to specify exactly one of ForbiddenInABIAttr, UnconstrainedInABIAttr, EquivalentInABIAttr, InferredInABIAttr, or UnreachableInABIAttr"); #include "swift/AST/DeclAttr.def" #define TYPE_ATTR(_, Id) \ From 132f49108d752205a2f012303a3615794b33039c Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 19 Mar 2025 14:28:07 -0700 Subject: [PATCH 15/22] Check attributes in `@abi` attr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit compares the attributes on the decl inside the `@abi` attribute to those in the decl it’s attached to, diagnosing ABI-incompatible differences. It also rejects many attributes that don’t need to be specified in the `@abi` attribute, such as ObjC-ness, access control, or ABI-neutral traits like `@discardableResult`, so developers know to remove them. --- include/swift/AST/DiagnosticsSema.def | 21 + include/swift/Basic/LangOptions.h | 3 + include/swift/Option/Options.td | 4 + lib/Frontend/CompilerInvocation.cpp | 2 + lib/Sema/TypeCheckAttr.cpp | 6 + lib/Sema/TypeCheckAttrABI.cpp | 217 ++++- lib/Sema/TypeCheckDeclPrimary.cpp | 4 + test/attr/Inputs/attr_abi.h | 3 + test/attr/attr_abi.swift | 1146 ++++++++++++++++++++++++- test/attr/attr_abi_objc.swift | 156 ++++ test/attr/attr_weaklinked.swift | 18 +- 11 files changed, 1574 insertions(+), 6 deletions(-) create mode 100644 test/attr/Inputs/attr_abi.h create mode 100644 test/attr/attr_abi_objc.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 1bd689560e9af..9c751a3235819 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8376,6 +8376,27 @@ ERROR(attr_abi_incompatible_with_silgen_name,none, "the same purpose", (DescriptiveDeclKind)) +ERROR(attr_abi_missing_attr,none, + "missing '%0' %select{attribute|modifier}1 in '@abi'", + (StringRef, bool)) +ERROR(attr_abi_extra_attr,none, + "extra %select{|implicit }2'%0' %select{attribute|modifier}1 in '@abi'", + (StringRef, bool, /*isImplicit=*/bool)) +ERROR(attr_abi_forbidden_attr,none, + "unused '%0' %select{attribute|modifier}1 in '@abi'", + (StringRef, bool)) +REMARK(abi_attr_inferred_attribute,none, + "inferred '%0' in '@abi' to match %select{attribute|modifier}1 on API", + (StringRef, bool)) + +ERROR(attr_abi_mismatched_attr,none, + "'%0' %select{attribute|modifier}1 in '@abi' should match '%2'", + (StringRef, bool, StringRef)) +NOTE(attr_abi_matching_attr_here,none, + "%select{should match|matches}0 %select{attribute|modifier}1 " + "%select{|implicitly added }2here", + (/*matches=*/bool, /*isModifier=*/bool, /*isImplicit=*/bool)) + ERROR(attr_abi_mismatched_type,none, "type %0 in '@abi' should match %1", (Type, Type)) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 5d9cac773637b..8a73b09290079 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -269,6 +269,9 @@ namespace swift { /// Emit a remark on early exit in explicit interface build bool EnableSkipExplicitInterfaceModuleBuildRemarks = false; + /// Emit a remark when \c \@abi infers an attribute or modifier. + bool EnableABIInferenceRemarks = false; + /// /// Support for alternate usage modes /// diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 5b85213f00ed6..73f76baea1f3f 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -468,6 +468,10 @@ def remark_module_serialization : Flag<["-"], "Rmodule-serialization">, Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, HelpText<"Emit remarks about module serialization">; +def remark_abi_inference : Flag<["-"], "Rabi-inference">, + Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>, + HelpText<"Emit a remark when an '@abi' attribute adds an attribute or modifier to the ABI declaration based on its presence in the API">; + def emit_tbd : Flag<["-"], "emit-tbd">, HelpText<"Emit a TBD file">, Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 9f3ebf072677f..a334b66bd25df 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1422,6 +1422,8 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableSkipExplicitInterfaceModuleBuildRemarks = Args.hasArg(OPT_remark_skip_explicit_interface_build); + Opts.EnableABIInferenceRemarks = Args.hasArg(OPT_remark_abi_inference); + if (Args.hasArg(OPT_experimental_skip_non_exportable_decls)) { // Only allow -experimental-skip-non-exportable-decls if either library // evolution is enabled (in which case the module's ABI is independent of diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 7b995582be76e..1a6151c727c08 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -6672,6 +6672,12 @@ static bool typeCheckDerivativeAttr(DerivativeAttr *attr) { // Note: Implementation must be idempotent because it may be called multiple // times for the same attribute. Decl *D = attr->getOriginalDeclaration(); + + // ABI-only decls can't have @derivative; bail out and let ABIDeclChecker + // diagnose this. + if (!ABIRoleInfo(D).providesAPI()) + return false; + auto &Ctx = D->getASTContext(); auto &diags = Ctx.Diags; // `@derivative` attribute requires experimental differentiable programming diff --git a/lib/Sema/TypeCheckAttrABI.cpp b/lib/Sema/TypeCheckAttrABI.cpp index b20a499a74e3d..1f1120445f9a7 100644 --- a/lib/Sema/TypeCheckAttrABI.cpp +++ b/lib/Sema/TypeCheckAttrABI.cpp @@ -20,6 +20,7 @@ #include "swift/AST/Effects.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/ASTPrinter.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #include "swift/Parse/Lexer.h" @@ -129,6 +130,40 @@ void fixItReplaceKeywords(InFlightDiagnostic &diag, return fixItReplaceKeywords(diag, charRange, newText); } +/// Returns a string representation of \p attr suitable to either replace an +/// existing attribute or be inserted as a new attribute, depending on the +/// value of \p toInsert . +StringRef printAttr(DeclAttribute *attr, + Decl *decl, + SmallVectorImpl &scratch, + bool toInsert = false) { + auto &ctx = decl->getASTContext(); + auto opts = PrintOptions::printForDiagnostics(AccessLevel::Private, + ctx.TypeCheckerOpts.PrintFullConvention); + opts.PrintLongAttrsOnSeparateLines = false; + + llvm::raw_svector_ostream os{scratch}; + StreamPrinter printer{os}; + attr->print(printer, opts, decl); + + auto str = StringRef(scratch.begin(), scratch.size()); + if (!toInsert) + str = str.trim(' '); + return str; +} + +/// Emit \c diag::attr_abi_matching_attr_here in the best available location. +void noteAttrHere(DeclAttribute *attr, Decl *decl, bool isMatch = false) { + auto &ctx = decl->getASTContext(); + SourceLoc loc = attr->getLocation(); + if (loc.isValid()) + ctx.Diags.diagnose(loc, diag::attr_abi_matching_attr_here, + isMatch, attr->isDeclModifier(), attr->isImplicit()); + else + ctx.Diags.diagnose(decl, diag::attr_abi_matching_attr_here, + isMatch, attr->isDeclModifier(), attr->isImplicit()); +} + /// Get the best available \c SourceLoc representing the type in \p storage . SourceLoc getTypeLoc(AbstractStorageDecl *storage, Decl *owner = nullptr) { auto loc = storage->getTypeSourceRangeForDiagnostics().Start; @@ -187,7 +222,11 @@ class ABIDeclChecker : public ASTComparisonVisitor { bool didDiagnose = false; auto noteShouldMatch = [&](bool isModifier) { - ctx.Diags.diagnose(apiTypeLoc, diag::attr_abi_should_match_type_here); + if (isSelfParam) + ctx.Diags.diagnose(apiTypeLoc, diag::attr_abi_matching_attr_here, + /*matches=*/false, isModifier, /*isImplicit=*/false); + else + ctx.Diags.diagnose(apiTypeLoc, diag::attr_abi_should_match_type_here); }; // These assertions represent values that should have been normalized. @@ -646,13 +685,187 @@ class ABIDeclChecker : public ASTComparisonVisitor { // MARK: @abi checking - attributes + /// Are these attributes similar enough that they should be checked against + /// one another? At minimum this means they're of the same kind, but for some + /// attrs there are additional criteria. + bool canCompareAttrs(DeclAttribute *api, DeclAttribute *abi, + Decl *apiDecl, Decl *abiDecl) { + if (api->getKind() != abi->getKind()) + return false; + + auto getAvailableDomain = [](Decl *D, DeclAttribute *A) { + return D->getSemanticAvailableAttr(cast(A))->getDomain(); + }; + + // Extra logic for specific attributes. + switch (api->getKind()) { + case DeclAttrKind::Expose: + return cast(api)->getExposureKind() + == cast(abi)->getExposureKind(); + + case DeclAttrKind::Extern: + return cast(api)->getExternKind() + == cast(abi)->getExternKind(); + + case DeclAttrKind::Available: + return getAvailableDomain(apiDecl, api) + == getAvailableDomain(abiDecl, abi); + return true; + + default: + break; + } + + return true; + } + + /// Check two attribute lists against one another. + /// + /// This pairs up attributes which are sufficiently similar (as determined by + /// \c canCompareAttrs() ) and then checks them. Attributes which + /// have no counterpart are checked individually. bool checkAttrs(DeclAttributes api, DeclAttributes abi, Decl *apiDecl, Decl *abiDecl) { bool didDiagnose = false; - // TODO: Actually check attributes + + // Collect all ABI attrs. + SmallVector remainingABIDeclAttrs; + for (auto *abiDeclAttr : abi) { + remainingABIDeclAttrs.push_back(abiDeclAttr); + } + + // Visit each API attr, pairing it with an ABI attr if possible. + // Note that this will visit even invalid attributes. + for (auto *apiDeclAttr : api) { + auto abiAttrIter = llvm::find_if(remainingABIDeclAttrs, + [&](DeclAttribute *abiDeclAttr) { + return abiDeclAttr && canCompareAttrs(apiDeclAttr, abiDeclAttr, + apiDecl, abiDecl); + }); + DeclAttribute *abiDeclAttr = nullptr; + if (abiAttrIter != remainingABIDeclAttrs.end()) { + // Found a matching ABI attr. Claim and use it. + std::swap(abiDeclAttr, *abiAttrIter); + } + didDiagnose |= checkAttr(apiDeclAttr, abiDeclAttr, apiDecl, abiDecl); + } + + // Visit leftover ABI attrs. + for (auto *abiDeclAttr : remainingABIDeclAttrs) { + if (abiDeclAttr) + didDiagnose |= checkAttr(nullptr, abiDeclAttr, apiDecl, abiDecl); + } return didDiagnose; } + /// Check a single attribute against its counterpart. If an attribute has no + /// counterpart, the counterpart may be \c nullptr ; either \p abi or \p abi + /// may be \c nullptr , but never both. + bool checkAttr(DeclAttribute *api, DeclAttribute *abi, + Decl *apiDecl, Decl *abiDecl) { + ASSERT(api || abi && "checkAttr() should have at least one attribute"); + + // If either attribute has already been diagnosed, don't check here. + if ((api && api->isInvalid()) || (abi && abi->isInvalid())) + return true; + + auto kind = api ? api->getKind() : abi->getKind(); + auto behaviors = DeclAttribute::getBehaviors(kind); + + switch (behaviors & DeclAttribute::InABIAttrMask) { + case DeclAttribute::UnreachableInABIAttr: + ASSERT(abiAttr->canAppearOnDecl(apiDecl) + && "checking @abi on decl that can't have it???"); + ASSERT(!abiAttr->canAppearOnDecl(apiDecl) + && "unreachable-in-@abi attr on reachable decl???"); + + // If the asserts are disabled, fall through to no checking. + LLVM_FALLTHROUGH; + + case DeclAttribute::UnconstrainedInABIAttr: + // No checking required. + return false; + + case DeclAttribute::ForbiddenInABIAttr: + // Diagnose if ABI has attribute. + if (abi) { + // A shorthand `@available(foo 1, bar 2, *)` attribute gets parsed into + // several separate `AvailableAttr`s, each with the full range of the + // shorthand attribute. If we've already diagnosed one of them, don't + // diagnose the rest; otherwise, record that we've diagnosed this one. + if (isa(abi) && + !diagnosedAvailableAttrSourceLocs.insert(abi->getLocation())) + return true; + + diagnoseAndRemoveAttr(abi, diag::attr_abi_forbidden_attr, + abi->getAttrName(), abi->isDeclModifier()); + return true; + } + + return false; + + case DeclAttribute::InferredInABIAttr: + if (!abi && api->canClone()) { + // Infer an identical attribute. + abi = api->clone(ctx); + abi->setImplicit(true); + abiDecl->getAttrs().add(abi); + + if (ctx.LangOpts.EnableABIInferenceRemarks) { + SmallString<64> scratch; + auto abiAttrAsString = printAttr(abi, abiDecl, scratch); + + abiDecl->diagnose(diag::abi_attr_inferred_attribute, + abiAttrAsString, api->isDeclModifier()); + noteAttrHere(api, apiDecl, /*isMatch=*/true); + } + } + + // Other than the cloning behavior, Inferred behaves like Equivalent. + LLVM_FALLTHROUGH; + + case DeclAttribute::EquivalentInABIAttr: + // Diagnose if API doesn't have attribute. + if (!api) { + diagnoseAndRemoveAttr(abi, diag::attr_abi_extra_attr, + abi->getAttrName(), abi->isDeclModifier(), + abi->isImplicit()); + return true; + } + + // Diagnose if ABI doesn't have attribute. + if (!abi) { + SmallString<64> scratch; + auto apiAttrAsString = printAttr(api, apiDecl, scratch, + /*toInsert=*/true); + + ctx.Diags.diagnose(abiDecl, diag::attr_abi_missing_attr, + api->getAttrName(), api->isDeclModifier()) + .fixItInsert(abiDecl->getAttributeInsertionLoc(api->isDeclModifier()), + apiAttrAsString); + noteAttrHere(api, apiDecl); + return true; + } + + // Diagnose if two attributes are mismatched. + if (!api->isEquivalent(abi, apiDecl)) { + SmallString<64> scratch; + auto apiAttrAsString = printAttr(api, apiDecl, scratch); + + ctx.Diags.diagnose(abi->getLocation(), diag::attr_abi_mismatched_attr, + abi->getAttrName(), abi->isDeclModifier(), + apiAttrAsString) + .fixItReplace(abi->getRangeWithAt(), apiAttrAsString); + noteAttrHere(api, apiDecl); + return true; + } + + return false; + } + + llvm_unreachable("unknown InABIAttrMask behavior"); + } + // MARK: @abi checking - types bool checkType(Type api, Type abi, SourceLoc apiLoc, SourceLoc abiLoc) { diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 5664aff3f48f3..033274012edcf 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2173,6 +2173,10 @@ void swift::diagnoseAttrsAddedByAccessNote(SourceFile &SF) { evaluator::SideEffect ApplyAccessNoteRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const { + // Access notes don't apply to ABI-only attributes. + if (!ABIRoleInfo(VD).providesAPI()) + return {}; + AccessNotesFile ¬es = VD->getModuleContext()->getAccessNotes(); if (auto note = notes.lookup(VD)) applyAccessNote(VD, *note.get(), notes); diff --git a/test/attr/Inputs/attr_abi.h b/test/attr/Inputs/attr_abi.h new file mode 100644 index 0000000000000..580cdfb6b20c2 --- /dev/null +++ b/test/attr/Inputs/attr_abi.h @@ -0,0 +1,3 @@ +void implementation1(void); +void implementation2(void); +void implementation3(void); diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index 24351743f8a4f..49c1e1ff7d7bb 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -1,7 +1,21 @@ -// RUN: %target-typecheck-verify-swift -enable-experimental-feature Extern -enable-experimental-feature ABIAttribute -parse-as-library +// RUN: %target-typecheck-verify-swift -enable-experimental-feature Extern -enable-experimental-feature ABIAttribute -enable-experimental-feature AddressableParameters -enable-experimental-feature NoImplicitCopy -enable-experimental-feature SymbolLinkageMarkers -enable-experimental-feature StrictMemorySafety -enable-experimental-feature LifetimeDependence -enable-experimental-feature CImplementation -enable-experimental-feature ExecutionAttribute -import-bridging-header %S/Inputs/attr_abi.h -parse-as-library -Rabi-inference -debugger-support // REQUIRES: swift_feature_ABIAttribute +// REQUIRES: swift_feature_AddressableParameters +// REQUIRES: swift_feature_CImplementation +// REQUIRES: swift_feature_ExecutionAttribute // REQUIRES: swift_feature_Extern +// REQUIRES: swift_feature_LifetimeDependence +// REQUIRES: swift_feature_NoImplicitCopy +// REQUIRES: swift_feature_StrictMemorySafety +// REQUIRES: swift_feature_SymbolLinkageMarkers + +import _Differentiation + +import Distributed + +@available(SwiftStdlib 5.7, *) +typealias DefaultDistributedActorSystem = LocalTestingDistributedActorSystem // // Same-kind checking @@ -1035,6 +1049,1136 @@ struct FailableInits { init!(i33: Void) {} } +// +// Attributes +// + +// @_originallyDefinedIn -- allowed to vary +@abi(@_originallyDefinedIn(module: "Other", macOS 14) func originallyDefinedIn1()) +@available(macOS 12, *) @_originallyDefinedIn(module: "Other", macOS 14) public func originallyDefinedIn1() {} + +@abi(func originallyDefinedIn2()) +@available(macOS 12, *) @_originallyDefinedIn(module: "Other", macOS 14) public func originallyDefinedIn2() {} + +@abi(@_originallyDefinedIn(module: "Other", macOS 14) func originallyDefinedIn3()) +@available(macOS 12, *) public func originallyDefinedIn3() {} + +@abi(@_originallyDefinedIn(module: "Different", macOS 12) func originallyDefinedIn4()) +@available(macOS 12, *) @_originallyDefinedIn(module: "Other", macOS 14) public func originallyDefinedIn4() {} + +// @Sendable -- allowed to vary +@abi(@Sendable func sendable1()) +@Sendable func sendable1() {} + +@abi(@Sendable func sendable2()) +func sendable2() {} + +@abi(func sendable3()) +@Sendable func sendable3() {} + +// @preconcurrency -- allowed to vary +@abi(@preconcurrency func preconcurrency1()) +@preconcurrency func preconcurrency1() {} + +@abi(@preconcurrency func preconcurrency2()) +func preconcurrency2() {} + +@abi(func preconcurrency3()) +@preconcurrency func preconcurrency3() {} + +// @_preInverseGenerics -- allowed to vary +struct PreInverseGenerics { + @abi(@_preInverseGenerics func fn1(_: consuming T)) + @_preInverseGenerics func fn1(_: consuming T) {} + + @abi(@_preInverseGenerics func fn2(_: consuming T)) + func fn2(_: consuming T) {} + + @abi(func fn3(_: consuming T)) + @_preInverseGenerics func fn3(_: consuming T) {} +} + +// 'nonisolated', 'isolated' arguments, global actors -- allowed to vary +@abi(@MainActor func isolation1()) +@MainActor func isolation1() {} + +@abi(func isolation2()) +@MainActor func isolation2() {} + +@abi(@MainActor func isolation3()) +func isolation3() {} + +@abi(nonisolated func isolation4()) +@MainActor func isolation4() {} + +@abi(@MainActor func isolation5()) +nonisolated func isolation5() {} + +@abi(func isolation6(_: isolated some Actor)) +@MainActor func isolation6(_: some Actor) {} + +@abi(func isolation7(_: some Actor)) +func isolation7(_: isolated some Actor) {} + +@abi(@execution(concurrent) func isolation8() async) +@execution(concurrent) func isolation8() async {} + +@abi(func isolation9() async) +@execution(concurrent) func isolation9() async {} + +@abi(@execution(concurrent) func isolation10() async) +func isolation10() async {} + +@abi(nonisolated func isolation11() async) +@execution(concurrent) func isolation11() async {} + +@abi(@execution(concurrent) func isolation12() async) +nonisolated func isolation12() async {} + +@abi(@execution(caller) func isolation13() async) +@execution(caller) func isolation13() async {} + +@abi(func isolation14() async) +@execution(caller) func isolation14() async {} + +@abi(@execution(caller) func isolation15() async) +func isolation15() async {} + +@abi(nonisolated func isolation16() async) +@execution(caller) func isolation16() async {} + +@abi(@execution(caller) func isolation17() async) +nonisolated func isolation17() async {} + +@abi(@execution(caller) func isolation18() async) +@execution(concurrent) func isolation18() async {} + +@abi(@execution(concurrent) func isolation19() async) +@execution(caller) func isolation19() async {} + +// NSCopying - see attr/attr_abi_objc.swift + +// @LLDBDebuggerFunction -- banned in @abi +@abi(@LLDBDebuggerFunction func lldbDebuggerFunction1()) // expected-error {{unused 'LLDBDebuggerFunction' attribute in '@abi'}} {{6-27=}} +@LLDBDebuggerFunction func lldbDebuggerFunction1() {} + +@abi(@LLDBDebuggerFunction func lldbDebuggerFunction2()) // expected-error {{unused 'LLDBDebuggerFunction' attribute in '@abi'}} {{6-27=}} +func lldbDebuggerFunction2() {} + +@abi(func lldbDebuggerFunction3()) +@LLDBDebuggerFunction func lldbDebuggerFunction3() {} + +// @_compilerInitialized -- banned in @abi +class CompilerInitialized { + @abi(@_compilerInitialized let v1: Int) // expected-error {{unused '_compilerInitialized' attribute in '@abi'}} {{8-29=}} + @_compilerInitialized let v1: Int + + @abi(@_compilerInitialized let v2: Int) // expected-error {{unused '_compilerInitialized' attribute in '@abi'}} {{8-29=}} + let v2: Int + + @abi(let v3: Int) + @_compilerInitialized let v3: Int + + init() {} +} + +// @_hasStorage -- banned in @abi +struct HasStorage { + @abi(@_hasStorage let v1: Int) // expected-error {{unused '_hasStorage' attribute in '@abi'}} {{8-20=}} + @_hasStorage let v1: Int + + @abi(@_hasStorage let v2: Int) // expected-error {{unused '_hasStorage' attribute in '@abi'}} {{8-20=}} + let v2: Int + + @abi(let v3: Int) + @_hasStorage let v3: Int + + init() {} +} + +// @discardableResult -- banned in @abi +@abi(@discardableResult func discardableResult1() -> Int) // expected-error {{unused 'discardableResult' attribute in '@abi'}} {{6-24=}} +@discardableResult func discardableResult1() -> Int { 0 } + +@abi(@discardableResult func discardableResult2() -> Int) // expected-error {{unused 'discardableResult' attribute in '@abi'}} {{6-24=}} +func discardableResult2() -> Int { 0 } + +@abi(func lldbDebuggerFunction3() -> Int) +@discardableResult func discardableResult3() -> Int { 0 } + +// @warn_unqualified_access -- banned in @abi +struct WarnUnqualifiedAccess { + @abi(@warn_unqualified_access func fn1()) // expected-error {{unused 'warn_unqualified_access' attribute in '@abi'}} {{8-32=}} + @warn_unqualified_access func fn1() {} + + @abi(@warn_unqualified_access func fn2()) // expected-error {{unused 'warn_unqualified_access' attribute in '@abi'}} {{8-32=}} + func fn2() {} + + @abi(func fn3()) + @warn_unqualified_access func fn3() {} +} + +// @_disfavoredOverload -- banned in @abi +@abi(@_disfavoredOverload func disfavoredOverload1()) // expected-error {{unused '_disfavoredOverload' attribute in '@abi'}} {{6-26=}} +@_disfavoredOverload func disfavoredOverload1() {} + +@abi(@_disfavoredOverload func disfavoredOverload2()) // expected-error {{unused '_disfavoredOverload' attribute in '@abi'}} {{6-26=}} +func disfavoredOverload2() {} + +@abi(func disfavoredOverload3()) +@_disfavoredOverload func disfavoredOverload3() {} + +// @_nonEphemeral -- banned in @abi +@abi(func nonEphemeral1(@_nonEphemeral _: UnsafeRawPointer)) // expected-error {{unused '_nonEphemeral' attribute in '@abi'}} {{25-39=}} +func nonEphemeral1(@_nonEphemeral _: UnsafeRawPointer) {} + +@abi(func nonEphemeral2(@_nonEphemeral _: UnsafeRawPointer)) // expected-error {{unused '_nonEphemeral' attribute in '@abi'}} {{25-39=}} +func nonEphemeral2(_: UnsafeRawPointer) {} + +@abi(func disfavoredOverload3(_: UnsafeRawPointer)) +func nonEphemeral3(@_nonEphemeral _: UnsafeRawPointer) {} + +// @_inheritActorContext -- banned in @abi +@abi(func inheritActorContext1(@_inheritActorContext fn: @Sendable @escaping () async -> Void)) // expected-error {{unused '_inheritActorContext' attribute in '@abi'}} {{32-53=}} +func inheritActorContext1(@_inheritActorContext fn: @Sendable @escaping () async -> Void) {} + +@abi(func inheritActorContext2(@_inheritActorContext fn: @Sendable @escaping () async -> Void)) // expected-error {{unused '_inheritActorContext' attribute in '@abi'}} {{32-53=}} +func inheritActorContext2(fn: @Sendable @escaping () async -> Void) {} + +@abi(func inheritActorContext3(fn: @Sendable @escaping () async -> Void)) +func inheritActorContext3(@_inheritActorContext fn: @Sendable @escaping () async -> Void) {} + +// @excusivity(checked/unchecked) -- banned in @abi +class Exclusivity { + @abi(var checked00: Int) + var checked00: Int = 0 + + @abi(@exclusivity(checked) var checked10: Int) // expected-error {{unused 'exclusivity(checked)' attribute in '@abi'}} {{8-29=}} + var checked10: Int = 0 + + @abi(@exclusivity(unchecked) var checked20: Int) // expected-error {{unused 'exclusivity(unchecked)' attribute in '@abi'}} {{8-31=}} + var checked20: Int = 0 + + @abi(var checked01: Int) + @exclusivity(checked) var checked01: Int = 0 + + @abi(@exclusivity(checked) var checked11: Int) // expected-error {{unused 'exclusivity(checked)' attribute in '@abi'}} {{8-29=}} + @exclusivity(checked) var checked11: Int = 0 + + @abi(@exclusivity(unchecked) var checked21: Int) // expected-error {{unused 'exclusivity(unchecked)' attribute in '@abi'}} {{8-31=}} + @exclusivity(checked) var checked21: Int = 0 + + @abi(var checked02: Int) + @exclusivity(unchecked) var checked02: Int = 0 + + @abi(@exclusivity(checked) var checked12: Int) // expected-error {{unused 'exclusivity(checked)' attribute in '@abi'}} {{8-29=}} + @exclusivity(unchecked) var checked12: Int = 0 + + @abi(@exclusivity(unchecked) var checked22: Int) // expected-error {{unused 'exclusivity(unchecked)' attribute in '@abi'}} {{8-31=}} + @exclusivity(unchecked) var checked22: Int = 0 +} + +// @_noAllocation -- banned in @abi +@abi(@_noAllocation func noAllocation1()) // expected-error {{unused '_noAllocation' attribute in '@abi'}} {{6-20=}} +@_noAllocation func noAllocation1() {} + +@abi(@_noAllocation func noAllocation2()) // expected-error {{unused '_noAllocation' attribute in '@abi'}} {{6-20=}} +func noAllocation2() {} + +@abi(func noAllocation3()) +@_noAllocation func noAllocation3() {} + +// @_noLocks -- banned in @abi +@abi(@_noLocks func noLocks1()) // expected-error {{unused '_noLocks' attribute in '@abi'}} {{6-15=}} +@_noLocks func noLocks1() {} + +@abi(@_noLocks func noLocks2()) // expected-error {{unused '_noLocks' attribute in '@abi'}} {{6-15=}} +func noLocks2() {} + +@abi(func noLocks3()) +@_noLocks func noLocks3() {} + +// @_noImplicitCopy -- banned in @abi +struct NoImplicitCopy { + @abi(@_noImplicitCopy func fn1()) // expected-error {{unused '_noImplicitCopy' attribute in '@abi'}} {{8-24=}} + @_noImplicitCopy func fn1() {} + + @abi(@_noImplicitCopy func fn2()) // expected-error {{unused '_noImplicitCopy' attribute in '@abi'}} {{8-24=}} + func fn2() {} + + @abi(func fn3()) + @_noImplicitCopy func fn3() {} + + @abi(func fn4(@_noImplicitCopy _: Int)) // expected-error {{unused '_noImplicitCopy' attribute in '@abi'}} {{17-33=}} + func fn4(@_noImplicitCopy _: Int) {} + + @abi(func fn5(@_noImplicitCopy _: Int)) // expected-error {{unused '_noImplicitCopy' attribute in '@abi'}} {{17-33=}} + func fn5(_: Int) {} + + @abi(func fn6(_: Int)) + func fn6(@_noImplicitCopy _: Int) {} +} + +// @_noObjCBridging -- banned in @abi +@abi(@_noObjCBridging func noObjCBridging1()) // expected-error {{unused '_noObjCBridging' attribute in '@abi'}} {{6-22=}} +@_noObjCBridging func noObjCBridging1() {} + +@abi(@_noObjCBridging func noObjCBridging2()) // expected-error {{unused '_noObjCBridging' attribute in '@abi'}} {{6-22=}} +func noObjCBridging2() {} + +@abi(func noObjCBridging3()) +@_noObjCBridging func noObjCBridging3() {} + +// @_noExistentials -- banned in @abi +@abi(@_noExistentials func noExistentials1()) // expected-error {{unused '_noExistentials' attribute in '@abi'}} {{6-22=}} +@_noExistentials func noExistentials1() {} + +@abi(@_noExistentials func noExistentials2()) // expected-error {{unused '_noExistentials' attribute in '@abi'}} {{6-22=}} +func noExistentials2() {} + +@abi(func noExistentials3()) +@_noExistentials func noExistentials3() {} + +// @_noRuntime -- banned in @abi +@abi(@_noRuntime func noRuntime1()) // expected-error {{unused '_noRuntime' attribute in '@abi'}} {{6-17=}} +@_noRuntime func noRuntime1() {} + +@abi(@_noRuntime func noRuntime2()) // expected-error {{unused '_noRuntime' attribute in '@abi'}} {{6-17=}} +func noRuntime2() {} + +@abi(func noRuntime3()) +@_noRuntime func noRuntime3() {} + +// @_noEagerMove -- banned in @abi +struct NoEagerMove { + @abi(@_noEagerMove func fn1()) // expected-error {{unused '_noEagerMove' attribute in '@abi'}} {{8-21=}} + @_noEagerMove func fn1() {} + + @abi(@_noEagerMove func fn2()) // expected-error {{unused '_noEagerMove' attribute in '@abi'}} {{8-21=}} + func fn2() {} + + @abi(func fn3()) + @_noEagerMove func fn3() {} + + @abi(func fn4(@_noEagerMove _: Int)) // expected-error {{unused '_noEagerMove' attribute in '@abi'}} {{17-30=}} + func fn4(@_noEagerMove _: Int) {} + + @abi(func fn5(@_noEagerMove _: Int)) // expected-error {{unused '_noEagerMove' attribute in '@abi'}} {{17-30=}} + func fn5(_: Int) {} + + @abi(func fn6(_: Int)) + func fn6(@_noEagerMove _: Int) {} +} + +// @_eagerMove -- banned in @abi +struct EagerMove { + @abi(@_eagerMove func fn1()) // expected-error {{unused '_eagerMove' attribute in '@abi'}} {{8-19=}} + @_eagerMove func fn1() {} + + @abi(@_eagerMove func fn2()) // expected-error {{unused '_eagerMove' attribute in '@abi'}} {{8-19=}} + func fn2() {} + + @abi(func fn3()) + @_eagerMove func fn3() {} + + @abi(func fn4(@_eagerMove _: Int)) // expected-error {{unused '_eagerMove' attribute in '@abi'}} {{17-28=}} + func fn4(@_eagerMove _: Int) {} + + @abi(func fn5(@_eagerMove _: Int)) // expected-error {{unused '_eagerMove' attribute in '@abi'}} {{17-28=}} + func fn5(_: Int) {} + + @abi(func fn6(_: Int)) + func fn6(@_eagerMove _: Int) {} +} + +// @_lexicalLifetimes -- banned in @abi +@abi(@_lexicalLifetimes func lexicalLifetimes1()) // expected-error {{unused '_lexicalLifetimes' attribute in '@abi'}} {{6-24=}} +@_lexicalLifetimes func lexicalLifetimes1() {} + +@abi(@_lexicalLifetimes func lexicalLifetimes2()) // expected-error {{unused '_lexicalLifetimes' attribute in '@abi'}} {{6-24=}} +func lexicalLifetimes2() {} + +@abi(func lexicalLifetimes3()) +@_lexicalLifetimes func lexicalLifetimes3() {} + +// @_assemblyVision -- banned in @abi +@abi(@_assemblyVision func assemblyVision1()) // expected-error {{unused '_assemblyVision' attribute in '@abi'}} {{6-22=}} +@_assemblyVision func assemblyVision1() {} + +@abi(@_assemblyVision func assemblyVision2()) // expected-error {{unused '_assemblyVision' attribute in '@abi'}} {{6-22=}} +func assemblyVision2() {} + +@abi(func assemblyVision3()) +@_assemblyVision func assemblyVision3() {} + +// @_extern -- banned in @abi +@abi(@_extern(c) @_extern(wasm, module: "foo", name: "bar") func extern1()) // expected-error {{unused '_extern' attribute in '@abi'}} {{18-61=}} expected-error {{unused '_extern' attribute in '@abi'}} {{6-17=}} +@_extern(c) @_extern(wasm, module: "foo", name: "bar") func extern1() + +@abi(@_extern(c) @_extern(wasm, module: "foo", name: "bar") func extern2()) // expected-error {{unused '_extern' attribute in '@abi'}} {{18-61=}} expected-error {{unused '_extern' attribute in '@abi'}} {{6-17=}} +func extern2() {} + +@abi(func extern3()) +@_extern(c) @_extern(wasm, module: "foo", name: "bar") func extern3() + +// @_used -- banned in @abi +@abi(@_used func used1()) // expected-error {{unused '_used' attribute in '@abi'}} {{6-12=}} +@_used func used1() {} + +@abi(@_used func used2()) // expected-error {{unused '_used' attribute in '@abi'}} {{6-12=}} +func used2() {} + +@abi(func used3()) +@_used func used3() {} + +// weak, unowned, unowned(unsafe) -- banned in @abi +class ReferenceOwnership { + @abi(var v00: AnyObject?) + var v00: AnyObject? = nil + + @abi(weak var v10: AnyObject?) // expected-error {{unused 'weak' modifier in '@abi'}} {{8-12=}} + var v10: AnyObject? = nil + + @abi(unowned var v20: AnyObject?) // expected-error {{unused 'unowned' modifier in '@abi'}} {{8-15=}} + var v20: AnyObject? = nil + + @abi(unowned(unsafe) var v30: AnyObject?) // expected-error {{unused 'unowned(unsafe)' modifier in '@abi'}} {{8-23=}} + var v30: AnyObject? = nil + + @abi(var v01: AnyObject?) + weak var v01: AnyObject? = nil + + @abi(weak var v11: AnyObject?) // expected-error {{unused 'weak' modifier in '@abi'}} {{8-12=}} + weak var v11: AnyObject? = nil + + @abi(unowned var v21: AnyObject?) // expected-error {{unused 'unowned' modifier in '@abi'}} {{8-15=}} + weak var v21: AnyObject? = nil + + @abi(unowned(unsafe) var v31: AnyObject?) // expected-error {{unused 'unowned(unsafe)' modifier in '@abi'}} {{8-23=}} + weak var v31: AnyObject? = nil + + @abi(var v02: AnyObject?) + unowned var v02: AnyObject? = nil + + @abi(weak var v12: AnyObject?) // expected-error {{unused 'weak' modifier in '@abi'}} {{8-12=}} + unowned var v12: AnyObject? = nil + + @abi(unowned var v22: AnyObject?) // expected-error {{unused 'unowned' modifier in '@abi'}} {{8-15=}} + unowned var v22: AnyObject? = nil + + @abi(unowned(unsafe) var v32: AnyObject?) // expected-error {{unused 'unowned(unsafe)' modifier in '@abi'}} {{8-23=}} + unowned var v32: AnyObject? = nil + + @abi(var v03: AnyObject?) + unowned(unsafe) var v03: AnyObject? = nil + + @abi(weak var v13: AnyObject?) // expected-error {{unused 'weak' modifier in '@abi'}} {{8-12=}} + unowned(unsafe) var v13: AnyObject? = nil + + @abi(unowned var v23: AnyObject?) // expected-error {{unused 'unowned' modifier in '@abi'}} {{8-15=}} + unowned(unsafe) var v23: AnyObject? = nil + + @abi(unowned(unsafe) var v33: AnyObject?) // expected-error {{unused 'unowned(unsafe)' modifier in '@abi'}} {{8-23=}} + unowned(unsafe) var v33: AnyObject? = nil +} + +// @abi -- banned in @abi (no recursion) +@abi( + @abi(func abiRecursion()) // expected-error {{unused 'abi' attribute in '@abi'}} {{3-29=}} + func abiRecursion() +) +func abiRecursion() {} + +// @unsafe -- banned in @abi +@abi(@unsafe func unsafe1()) // expected-error {{unused 'unsafe' attribute in '@abi'}} {{6-13=}} +@unsafe func unsafe1() {} + +@abi(@unsafe func unsafe2()) // expected-error {{unused 'unsafe' attribute in '@abi'}} {{6-13=}} +func unsafe2() {} + +@abi(func unsafe3()) +@unsafe func unsafe3() {} + +// @safe -- banned in @abi +@abi(@safe func safe1()) // expected-error {{unused 'safe' attribute in '@abi'}} {{6-11=}} +@safe func safe1() {} + +@abi(@safe func safe2()) // expected-error {{unused 'safe' attribute in '@abi'}} {{6-11=}} +func safe2() {} + +@abi(func safe3()) +@safe func safe3() {} + +// Access control, @usableFromInline, @_spi -- banned in @abi +// An ABI-only decl gets its access control from its counterpart. +@abi(internal func accessControl1()) // expected-error {{unused 'internal' modifier in '@abi'}} {{6-14=}} +func accessControl1() {} + +@abi(func accessControl2()) +public func accessControl2() {} + +@abi(@usableFromInline func accessControl3()) // expected-error {{'@usableFromInline' attribute can only be applied to internal or package declarations, but global function 'accessControl3()' is public}} +public func accessControl3() {} + +@abi(private(set) var setterAccess1: Int) // expected-error {{unused 'private' modifier in '@abi'}} {{6-18=}} +var setterAccess1: Int = 42 + +@abi(var setterAccess2: Int) +private(set) var setterAccess2: Int = 42 + +@abi(@usableFromInline func usableFromInline1()) // expected-error {{unused 'usableFromInline' attribute in '@abi'}} {{6-23=}} +@usableFromInline func usableFromInline1() {} + +@abi(func usableFromInline2()) +@usableFromInline func usableFromInline2() {} + +@_spi(foo) public struct SPIType {} // expected-note 2 {{struct declared here}} + +@abi(@_spi(foo) func spi1(_: SPIType)) // expected-error {{unused '_spi' attribute in '@abi'}} {{6-16=}} +@_spi(foo) public func spi1(_: SPIType) {} + +@abi(func spi2(_: SPIType)) +@_spi(foo) public func spi2(_: SPIType) {} + +@abi(func spi3(_: SPIType)) // expected-error {{cannot use struct 'SPIType' here; it is SPI}} +public func spi3(_: SPIType) {} // expected-error {{cannot use struct 'SPIType' here; it is SPI}} + +// @available, @_unavailable*, @backDeployed -- banned in @abi +// An ABI-only decl gets its availability from its counterpart. +@abi(@available(macOS 14, iOS 16, *) func available1()) // expected-error {{unused 'available' attribute in '@abi'}} {{6-37=}} +@available(macOS 14, iOS 16, *) func available1() {} + +@abi(@available(macOS 14, iOS 16, *) func available2()) // expected-error {{unused 'available' attribute in '@abi'}} {{6-37=}} +func available2() {} + +@abi(func available3()) +@available(macOS 14, iOS 16, *) func available3() {} + +@abi( + @available(macOS, unavailable) // expected-error {{unused 'available' attribute in '@abi'}} {{3-34=}} + @available(iOS, deprecated) // expected-error {{unused 'available' attribute in '@abi'}} {{3-31=}} + func available4() +) +@available(macOS 14, iOS 16, *) func available4() {} + +// Additional tests in attr/attr_abi_objc.swift + +@abi(@_unavailableFromAsync func unavailableFromAsync1()) // expected-error {{unused '_unavailableFromAsync' attribute in '@abi'}} {{6-28=}} +@_unavailableFromAsync func unavailableFromAsync1() {} + +@abi(@_unavailableFromAsync func unavailableFromAsync2()) // expected-error {{unused '_unavailableFromAsync' attribute in '@abi'}} {{6-28=}} +func unavailableFromAsync2() {} + +@abi(func unavailableFromAsync3()) +@_unavailableFromAsync func unavailableFromAsync3() {} + +// FIXME: Test @_unavailableInEmbedded (it gets rewritten in the parser) + +@abi(@backDeployed(before: macOS 14) func backDeployed1()) // expected-error {{unused 'backDeployed' attribute in '@abi'}} {{6-37=}} +@backDeployed(before: macOS 14) public func backDeployed1() {} + +@abi(@backDeployed(before: macOS 14) func backDeployed2()) // expected-error {{unused 'backDeployed' attribute in '@abi'}} {{6-37=}} +public func backDeployed2() {} + +@abi(func backDeployed3()) +@backDeployed(before: macOS 14) public func backDeployed3() {} + +// override, @_nonoverride -- banned in @abi +// An ABI-only decl gets its overrides from its counterpart; no marker modifiers +// are required. +class Overridden { + func fn1() {} + + func fn2() {} // expected-note 2 {{overridden declaration is here}} + + func fn3() {} +} + +class Override: Overridden { + @abi(override func fn1()) // expected-error {{unused 'override' modifier in '@abi'}} {{8-16=}} + override func fn1() {} + + @abi(override func fn2()) // expected-error {{unused 'override' modifier in '@abi'}} {{8-16=}} + func fn2() {} // expected-error {{overriding declaration requires an 'override' keyword}} + + @abi(func fn3()) + override func fn3() {} +} + +class NonOverride: Overridden { + @abi(@_nonoverride func fn1()) // expected-error {{unused '_nonoverride' attribute in '@abi'}} {{8-21=}} + @_nonoverride func fn1() {} + + @abi(@_nonoverride func fn2()) // expected-error {{unused '_nonoverride' attribute in '@abi'}} {{8-21=}} + func fn2() {} // expected-error {{overriding declaration requires an 'override' keyword}} + + @abi(func fn3()) + @_nonoverride func fn3() {} +} + +// @_silgen_name -- banned in @abi *and* on declarations with @abi +// Becuase of the way @_silgen_name is implemented, these would interact oddly +// if they were allowed on the same decl. +@_silgen_name("conflictingAttrsSilgenName") +@abi(func silgenName1()) +func silgenName1() {} // expected-error@-2 {{cannot use '@_silgen_name' and '@abi' on the same global function because they serve the same purpose}} {{1-44=}} + +@abi(@_silgen_name("silgenNameWithABI") func silgenName2()) // expected-error {{unused '_silgen_name' attribute in '@abi'}} {{6-40=}} +func silgenName2() {} + +// @_documentation(visibility:metadata:) -- banned in @abi +@abi(@_documentation(visibility: public) func documentation1()) // expected-error {{unused '_documentation' attribute in '@abi'}} {{6-41=}} +@_documentation(visibility: public) func documentation1() {} + +@abi(@_documentation(visibility: public) func documentation2()) // expected-error {{unused '_documentation' attribute in '@abi'}} {{6-41=}} +func documentation2() {} + +@abi(func documentation3()) +@_documentation(visibility: public) func documentation3() {} + +// @_allowFeatureSuppression -- banned in @abi +// Feature suppression should be applied to the API since we can't put `#if` +// inside `@abi`. +@abi(@_allowFeatureSuppression(IsolatedAny) func allowFeatureSuppression1()) // expected-error {{unused '_allowFeatureSuppression' attribute in '@abi'}} {{6-44=}} +@_allowFeatureSuppression(IsolatedAny) func allowFeatureSuppression1() {} + +@abi(@_allowFeatureSuppression(IsolatedAny) func allowFeatureSuppression2()) // expected-error {{unused '_allowFeatureSuppression' attribute in '@abi'}} {{6-44=}} +func allowFeatureSuppression2() {} + +@abi(func allowFeatureSuppression3()) +@_allowFeatureSuppression(IsolatedAny) func allowFeatureSuppression3() {} + +// @objc -- tested in attr/attr_abi_objc.swift +// @IBAction -- tested in attr/attr_abi_objc.swift +// @IBInspectable -- tested in attr/attr_abi_objc.swift +// @GKInspectable -- tested in attr/attr_abi_objc.swift +// @IBOutlet -- tested in attr/attr_abi_objc.swift +// @IBSegueAction -- tested in attr/attr_abi_objc.swift +// @NSManaged -- tested in attr/attr_abi_objc.swift +// @nonobjc -- tested in attr/attr_abi_objc.swift +// optional -- tested in attr/attr_abi_objc.swift +// dynamic -- tested in attr/attr_abi_objc.swift + +// @_cdecl -- banned in @abi +// ABI-only decls inherit cdecl-ness from their counterpart +@abi(@_cdecl("cdecl1") func cdecl1()) // expected-error {{unused '_cdecl' attribute in '@abi'}} {{6-23=}} +@_cdecl("cdecl1") func cdecl1() {} + +@abi(@_cdecl("cdecl2") func cdecl2()) // expected-error {{unused '_cdecl' attribute in '@abi'}} {{6-23=}} +func cdecl2() {} + +@abi(func cdecl3()) +@_cdecl("cdecl3") func cdecl3() {} + +// @implementation -- banned in @abi +// ABI-only decls inherit implementation-ness from their counterpart +@abi(@implementation func implementation1()) // expected-error {{unused 'implementation' attribute in '@abi'}} {{6-21=}} +@_cdecl("implementation1") @implementation func implementation1() {} + +@abi(@implementation func implementation2()) // expected-error {{unused 'implementation' attribute in '@abi'}} {{6-21=}} +@_cdecl("implementation2") func implementation2() {} + +@abi(func implementation3()) +@_cdecl("implementation3") @implementation func implementation3() {} + +// @_expose -- banned in @abi +// ABI-only decls inherit exposure from their counterpart +@abi(@_expose(Cxx) func expose1()) // expected-error {{unused '_expose' attribute in '@abi'}} {{6-19=}} +@_expose(Cxx) func expose1() {} + +@abi(@_expose(Cxx) func expose2()) // expected-error {{unused '_expose' attribute in '@abi'}} {{6-19=}} +func expose2() {} + +@abi(func expose3()) +@_expose(Cxx) func expose3() {} + +// @_section -- banned in @abi +@abi(@_section("fnord") func section1()) // expected-error {{unused '_section' attribute in '@abi'}} {{6-24=}} +@_section("fnord") func section1() {} + +@abi(@_section("fnord") func section2()) // expected-error {{unused '_section' attribute in '@abi'}} {{6-24=}} +func section2() {} + +@abi(func section3()) +@_section("fnord") func section3() {} + +// @inlinable -- automatically cloned into @abi +@abi(@inlinable func inlinable1()) +@inlinable func inlinable1() {} + +@abi(@inlinable func inlinable2()) // expected-error {{extra 'inlinable' attribute in '@abi'}} {{6-16=}} +func inlinable2() {} + +@abi(func inlinable3()) // expected-remark {{inferred '@inlinable' in '@abi' to match attribute on API}} +@inlinable func inlinable3() {} // expected-note {{matches attribute here}} + +// @inline -- automatically cloned into @abi +@abi(@inline(never) func inline1()) +@inline(never) func inline1() {} + +@abi(@inline(never) func inline2()) // expected-error {{extra 'inline(never)' attribute in '@abi'}} {{6-20=}} +func inline2() {} + +@abi(func inline3()) // expected-remark {{inferred '@inline(never)' in '@abi' to match attribute on API}} +@inline(never) func inline3() {} // expected-note {{matches attribute here}} + +// @_transparent -- automatically cloned into @abi +@abi(@_transparent func transparent1()) +@_transparent func transparent1() {} + +@abi(@_transparent func transparent2()) // expected-error {{extra '_transparent' attribute in '@abi'}} {{6-19=}} +func transparent2() {} + +@abi(func transparent3()) // expected-remark {{inferred '@_transparent' in '@abi' to match attribute on API}} +@_transparent func transparent3() {} // expected-note {{matches attribute here}} + +// @_alwaysEmitIntoClient -- automatically cloned into @abi +@abi(@_alwaysEmitIntoClient func alwaysEmitIntoClient1()) +@_alwaysEmitIntoClient func alwaysEmitIntoClient1() {} + +@abi(@_alwaysEmitIntoClient func alwaysEmitIntoClient2()) // expected-error {{extra '_alwaysEmitIntoClient' attribute in '@abi'}} {{6-28=}} +func alwaysEmitIntoClient2() {} + +@abi(func alwaysEmitIntoClient3()) // expected-remark {{inferred '@_alwaysEmitIntoClient' in '@abi' to match attribute on API}} +@_alwaysEmitIntoClient func alwaysEmitIntoClient3() {} // expected-note {{matches attribute here}} + +// @_optimize(none) -- banned in @abi +@abi(@_optimize(none) func optimize1()) // expected-error {{unused '_optimize(none)' attribute in '@abi'}} {{6-22=}} +@_optimize(none) func optimize1() {} + +@abi(@_optimize(none) func optimize2()) // expected-error {{unused '_optimize(none)' attribute in '@abi'}} {{6-22=}} +func optimize2() {} + +@abi(func optimize3()) +@_optimize(none) func optimize3() {} + +// convenience -- must match in @abi +// This doesn't have direct mangling impact, but a future direction where +// convenience inits could fake designated inits or vice versa might be useful. +class Convenience { + @abi(convenience init(i1: Void)) + convenience init(i1: Void) { fatalError() } + + @abi(convenience init(i2: Void)) // expected-error {{extra 'convenience' modifier in '@abi'}} {{8-19=}} + init(i2: Void) { fatalError() } + + @abi(init(i3: Void)) // expected-error {{missing 'convenience' modifier in '@abi'}} {{8-8=convenience }} + convenience init(i3: Void) { fatalError() } // expected-note {{should match modifier here}} +} + +// required -- must match in @abi +// This doesn't have direct mangling impact, but a future direction where +// required inits could fake normal inits or vice versa might be useful. +class Required { + @abi(required init(i1: Void)) + required init(i1: Void) { fatalError() } + + @abi(required init(i2: Void)) // expected-error {{extra 'required' modifier in '@abi'}} {{8-16=}} + init(i2: Void) { fatalError() } + + @abi(init(i3: Void)) // expected-error {{missing 'required' modifier in '@abi'}} {{8-8=required }} + required init(i3: Void) { fatalError() } // expected-note {{should match modifier here}} +} + +// lazy -- automatically cloned into @abi +class Lazy { + @abi(lazy var v1: Int) + lazy var v1: Int = 0 + + @abi(lazy var v2: Int) // expected-error {{extra 'lazy' modifier in '@abi'}} {{8-12=}} + var v2: Int = 0 + + @abi(var v3: Int) // expected-remark {{inferred 'lazy' in '@abi' to match modifier on API}} + lazy var v3: Int = 0 // expected-note {{matches modifier here}} +} + +// @_fixed_layout -- banned in @abi +class FixedLayoutVars { + @abi(@_fixed_layout var v1: Int) // expected-error {{unused '_fixed_layout' attribute in '@abi'}} {{8-22=}} + @_fixed_layout public var v1: Int = 0 + + @abi(@_fixed_layout var v2: Int) // expected-error {{unused '_fixed_layout' attribute in '@abi'}} {{8-22=}} + public var v2: Int = 0 + + @abi(var v3: Int) + @_fixed_layout public var v3: Int = 0 +} + +// @_specialize -- banned in @abi +// TODO: Maybe use @_specialize in @abi to tweak the ABI of specializations. +// Ban it for now, since there's nothing useful you can do with it yet. +@abi(@_specialize(where T == Int) func specialize1(_: T)) // expected-error {{unused '_specialize' attribute in '@abi'}} {{6-34=}} +@_specialize(where T == Int) func specialize1(_: T) {} + +@abi(@_specialize(where T == Int) func specialize2(_: T)) // expected-error {{unused '_specialize' attribute in '@abi'}} {{6-34=}} +func specialize2(_: T) {} + +@abi(func specialize3(_: T)) +@_specialize(where T == Int) func specialize3(_: T) {} + +// @_effects -- banned in @abi +@abi(@_effects(readonly) func effects1()) // expected-error {{unused '_effects(readonly)' attribute in '@abi'}} {{6-25=}} +@_effects(readonly) func effects1() {} + +@abi(@_effects(readonly) func effects2()) // expected-error {{unused '_effects(readonly)' attribute in '@abi'}} {{6-25=}} +func effects2() {} + +@abi(func effects3()) +@_effects(readonly) func effects3() {} + +// @_implements -- banned in @abi +protocol ImplementsProto { + func f1() + func f2() + func f3() +} + +class Implements: ImplementsProto { + @abi(@_implements(ImplementsProto, f1) func f1()) // expected-error {{unused '_implements' attribute in '@abi'}} {{8-41=}} + @_implements(ImplementsProto, f1) func f1() {} + + @abi(@_implements(ImplementsProto, f2) func f2()) // expected-error {{unused '_implements' attribute in '@abi'}} {{8-41=}} + func f2() {} + + @abi(func f3()) + @_implements(ImplementsProto, f3) func f3() {} +} + +// @_dynamicReplacement -- banned in @abi +struct DynamicReplacement { + dynamic func f1Original() {} + dynamic func f2Original() {} + dynamic func f3Original() {} +} + +extension DynamicReplacement { + @abi(@_dynamicReplacement(for: f1Original) func f1()) // expected-error {{unused '_dynamicReplacement' attribute in '@abi'}} {{8-45=}} + @_dynamicReplacement(for: f1Original) func f1() {} + + @abi(@_dynamicReplacement(for: f2Original) func f2()) // expected-error {{unused '_dynamicReplacement' attribute in '@abi'}} {{8-45=}} + func f2() {} + + @abi(func f3()) + @_dynamicReplacement(for: f3Original) func f3() {} +} + +// @_weakLinked -- tested in attr/attr_weaklinked.swift + +// @_borrowed -- automatically cloned into @abi +protocol BorrowedAttr { + @abi(@_borrowed var v1: Int) + @_borrowed var v1: Int { get set } + + @abi(var v2: Int) // expected-remark {{inferred '@_borrowed' in '@abi' to match attribute on API}} + @_borrowed var v2: Int { get set } // expected-note {{matches attribute here}} + + @abi(@_borrowed var v3: Int) // expected-error {{extra '_borrowed' attribute in '@abi'}} {{8-18=}} + var v3: Int { get set } +} + +// @lifetime -- must match in @abi +// TODO: Probably possible to make these unconstrained as long as we ensure +// that `@_addressableForDependencies` doesn't cause a calling convention +// change. +struct Lifetime: ~Escapable { + @abi(@lifetime(borrow i1) init(i1: UnsafeRawPointer)) + @lifetime(borrow i1) init(i1: UnsafeRawPointer) {} + + @abi(@lifetime(borrow i2) init(i2: UnsafeRawPointer)) // expected-error {{extra 'lifetime' attribute in '@abi'}} {{8-28=}} + init(i2: UnsafeRawPointer) {} // expected-error {{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} + + @abi(init(i3: UnsafeRawPointer)) // expected-error {{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} expected-error {{missing 'lifetime' attribute in '@abi'}} {{8-8=@lifetime(borrow i3) }} + @lifetime(borrow i3) init(i3: UnsafeRawPointer) {} // expected-note {{should match attribute here}} + + @abi(@lifetime(borrow i4) init(i4: UnsafeRawPointer, i4a: UnsafeRawPointer)) // expected-error {{'lifetime' attribute in '@abi' should match '@lifetime(borrow i4a)'}} {{8-28=@lifetime(borrow i4a)}} + @lifetime(borrow i4a) init(i4: UnsafeRawPointer, i4a: UnsafeRawPointer) {} // expected-note {{should match attribute here}} +} + +// @_unsafeNonescapableResult -- must match in @abi +// TODO: This could probably be allowed to vary in some circumstances. +struct UnsafeNonescapableResult: ~Escapable { + @abi(@_unsafeNonescapableResult init(i1: UnsafeRawPointer)) + @_unsafeNonescapableResult init(i1: UnsafeRawPointer) {} + + @abi(@_unsafeNonescapableResult init(i2: UnsafeRawPointer)) // expected-error {{extra '_unsafeNonescapableResult' attribute in '@abi'}} {{8-34=}} + init(i2: UnsafeRawPointer) {} // expected-error {{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} + + @abi(init(i3: UnsafeRawPointer)) // expected-error {{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} expected-error {{missing '_unsafeNonescapableResult' attribute in '@abi'}} {{8-8=@_unsafeNonescapableResult }} + @_unsafeNonescapableResult init(i3: UnsafeRawPointer) {} // expected-note {{should match attribute here}} +} + +// distributed -- must match in @abi +@available(SwiftStdlib 5.7, *) +distributed actor Local { + @abi(distributed func fn1()) + distributed func fn1() {} + + @abi(distributed func fn2()) // expected-error {{extra 'distributed' modifier in '@abi'}} {{8-19=}} + func fn2() {} + + @abi(func fn3()) // expected-error {{missing 'distributed' modifier in '@abi'}} {{8-8=distributed }} + distributed func fn3() {} // expected-note {{should match modifier here}} +} + +// _const -- allowed to vary +@abi(func const1(_: _const Int)) +func const1(_: _const Int) {} + +@abi(func const2(_: _const Int)) +func const2(_: Int) {} + +@abi(func const3(_: Int)) +func const3(_: _const Int) {} + +// @derivative, @differentiable, @transpose, @_noDerivative -- banned in @abi +// Too complex to infer or check +// TODO: Figure out if there's something we could do here. +@abi(@differentiable(reverse) func differentiable1(_ x: Float) -> Float) // expected-error {{unused 'differentiable' attribute in '@abi'}} {{6-30=}} +@differentiable(reverse) func differentiable1(_ x: Float) -> Float { x } + +@abi(@differentiable(reverse) func differentiable2(_ x: Float) -> Float) // expected-error {{unused 'differentiable' attribute in '@abi'}} {{6-30=}} +func differentiable2(_ x: Float) -> Float { x } + +@abi(func differentiable3(_ x: Float) -> Float) +@differentiable(reverse) func differentiable3(_ x: Float) -> Float { x } + +@abi( + @derivative(of: differentiable1(_:)) // expected-error {{unused 'derivative' attribute in '@abi'}} {{3-40=}} + func derivative1(_: Float) -> (value: Float, differential: (Float) -> (Float)) +) +@derivative(of: differentiable1(_:)) +func derivative1(_ x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +@abi( + @derivative(of: differentiable2(_:)) // expected-error {{unused 'derivative' attribute in '@abi'}} {{3-40=}} + func derivative2(_: Float) -> (value: Float, differential: (Float) -> (Float)) +) +func derivative2(_ x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +@abi( + func derivative3(_: Float) -> (value: Float, differential: (Float) -> (Float)) +) +@derivative(of: differentiable3(_:)) +func derivative3(_ x: Float) -> (value: Float, differential: (Float) -> (Float)) { + return (x, { $0 }) +} + +struct Transpose where T == T.TangentVector { + func fn1(_ x: T, _ y: T) -> T { x + y } + func fn2(_ x: T, _ y: T) -> T { x + y } + func fn3(_ x: T, _ y: T) -> T { x + y } + + @abi( + @transpose(of: fn1, wrt: (0, 1)) // expected-error {{unused 'transpose' attribute in '@abi'}} {{5-38=}} + func t_fn1(_ result: T) -> (T, T) + ) + @transpose(of: fn1, wrt: (0, 1)) + func t_fn1(_ result: T) -> (T, T) { (result, result) } + + @abi( + @transpose(of: fn2, wrt: (0, 1)) // expected-error {{unused 'transpose' attribute in '@abi'}} {{5-38=}} + func t_fn2(_ result: T) -> (T, T) + ) + func t_fn2(_ result: T) -> (T, T) { (result, result) } + + @abi( + func t_fn3(_ result: T) -> (T, T) + ) + @transpose(of: fn3, wrt: (0, 1)) + func t_fn3(_ result: T) -> (T, T) { (result, result) } +} + +struct NoDerivative { + @abi(@noDerivative func fn1()) // expected-error {{unused 'noDerivative' attribute in '@abi'}} {{8-21=}} + @noDerivative func fn1() {} + + @abi(@noDerivative func fn2()) // expected-error {{unused 'noDerivative' attribute in '@abi'}} {{8-21=}} + func fn2() {} + + @abi(func fn3()) + @noDerivative func fn3() {} +} + +// prefix, postfix -- allowed to vary +// Essentially part of the name, which is unconstrained. +prefix operator ← +prefix operator ↑ // expected-note {{prefix operator found here}} +prefix operator → // expected-note {{prefix operator found here}} +prefix operator ↓ + +struct Prefix { + @abi(static prefix func ← (value: Self) -> Self) + static prefix func ← (value: Self) -> Self { value } + + @abi(static prefix func ↑ (value: Self) -> Self) + static func ↑ (value: Self) -> Self { value } // expected-error {{prefix unary operator missing 'prefix' modifier}} + + @abi(static func → (value: Self) -> Self) // expected-error {{prefix unary operator missing 'prefix' modifier}} + static prefix func → (value: Self) -> Self { value } + + // Test ABI-preserving replacement code pattern: + + @abi(static prefix func ↓ (value: Self) -> Self) + static func __oldDownArrow(value: Self) -> Self { value } + + @abi(static func __newDownArrow(value: Self) -> Self) + static prefix func ↓ (value: Self) -> Self { value } +} + +postfix operator ←← +postfix operator ↑↑ // expected-note {{postfix operator found here}} +postfix operator →→ // expected-note {{postfix operator found here}} +postfix operator ↓↓ + +struct Postfix { + @abi(static postfix func ←← (value: Self) -> Self) + static postfix func ←← (value: Self) -> Self { value } + + @abi(static postfix func ↑↑ (value: Self) -> Self) + static func ↑↑ (value: Self) -> Self { value } // expected-error {{postfix unary operator missing 'postfix' modifier}} + + @abi(static func →→ (value: Self) -> Self) // expected-error {{postfix unary operator missing 'postfix' modifier}} + static postfix func →→ (value: Self) -> Self { value } + + // Test ABI-preserving replacement code pattern: + + @abi(static postfix func ↓↓ (value: Self) -> Self) + static func __oldDownArrow(value: Self) -> Self { value } + + @abi(static func __newDownArrow(value: Self) -> Self) + static postfix func ↓↓ (value: Self) -> Self { value } +} + +// Not testing `infix`; it's not *really* valid on funcs. + +// nonmutating, borrowing, consuming, __consuming, mutating -- allowed to vary +// Act like param modifiers; checked against each other separately +struct SelfParamOwnership { + @abi(func fn00()) + func fn00() {} + + @abi(nonmutating func fn10()) + func fn10() {} + + @abi(borrowing func fn20()) + func fn20() {} + + @abi(consuming func fn30()) // expected-error {{instance method modifier 'consuming' in '@abi' is not compatible with default}} {{none}} + func fn30() {} // expected-note {{should match modifier here}} + + @abi(__consuming func fn40()) // expected-error {{instance method modifier '__consuming' in '@abi' is not compatible with default}} {{none}} + func fn40() {} // expected-note {{should match modifier here}} + + @abi(mutating func fn50()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with default}} {{none}} + func fn50() {} // expected-note {{should match modifier here}} + + @abi(func fn01()) + nonmutating func fn01() {} + + @abi(nonmutating func fn11()) + nonmutating func fn11() {} + + @abi(borrowing func fn21()) + nonmutating func fn21() {} + + @abi(consuming func fn31()) // expected-error {{instance method modifier 'consuming' in '@abi' is not compatible with default}} {{none}} + nonmutating func fn31() {} // expected-note {{should match modifier here}} + + @abi(__consuming func fn41()) // expected-error {{instance method modifier '__consuming' in '@abi' is not compatible with default}} {{none}} + nonmutating func fn41() {} // expected-note {{should match modifier here}} + + @abi(mutating func fn51()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with default}} {{none}} + nonmutating func fn51() {} // expected-note {{should match modifier here}} + + @abi(func fn02()) + borrowing func fn02() {} + + @abi(nonmutating func fn12()) + borrowing func fn12() {} + + @abi(borrowing func fn22()) + borrowing func fn22() {} + + @abi(consuming func fn32()) // expected-error {{instance method modifier 'consuming' in '@abi' is not compatible with 'borrowing'}} {{none}} + borrowing func fn32() {} // expected-note {{should match modifier here}} + + @abi(__consuming func fn42()) // expected-error {{instance method modifier '__consuming' in '@abi' is not compatible with 'borrowing'}} {{none}} + borrowing func fn42() {} // expected-note {{should match modifier here}} + + @abi(mutating func fn52()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with 'borrowing'}} {{none}} + borrowing func fn52() {} // expected-note {{should match modifier here}} + + @abi(func fn03()) // expected-error {{default instance method modifier in '@abi' is not compatible with 'consuming'}} {{none}} + consuming func fn03() {} // expected-note {{should match modifier here}} + + @abi(nonmutating func fn13()) // expected-error {{default instance method modifier in '@abi' is not compatible with 'consuming'}} {{none}} + consuming func fn13() {} // expected-note {{should match modifier here}} + + @abi(borrowing func fn23()) // expected-error {{instance method modifier 'borrowing' in '@abi' is not compatible with 'consuming'}} {{none}} + consuming func fn23() {} // expected-note {{should match modifier here}} + + @abi(consuming func fn33()) + consuming func fn33() {} + + @abi(__consuming func fn43()) + consuming func fn43() {} + + @abi(mutating func fn53()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with 'consuming'}} {{none}} + consuming func fn53() {} // expected-note {{should match modifier here}} + + @abi(func fn04()) // expected-error {{default instance method modifier in '@abi' is not compatible with '__consuming'}} {{none}} + __consuming func fn04() {} // expected-note {{should match modifier here}} + + @abi(nonmutating func fn14()) // expected-error {{default instance method modifier in '@abi' is not compatible with '__consuming'}} {{none}} + __consuming func fn14() {} // expected-note {{should match modifier here}} + + @abi(borrowing func fn24()) // expected-error {{instance method modifier 'borrowing' in '@abi' is not compatible with '__consuming'}} {{none}} + __consuming func fn24() {} // expected-note {{should match modifier here}} + + @abi(consuming func fn34()) + __consuming func fn34() {} + + @abi(__consuming func fn44()) + __consuming func fn44() {} + + @abi(mutating func fn54()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with '__consuming'}} {{none}} + __consuming func fn54() {} // expected-note {{should match modifier here}} + + @abi(func fn05()) // expected-error {{default instance method modifier in '@abi' is not compatible with 'mutating'}} {{none}} + mutating func fn05() {} // expected-note {{should match modifier here}} + + @abi(nonmutating func fn15()) // expected-error {{default instance method modifier in '@abi' is not compatible with 'mutating'}} {{none}} + mutating func fn15() {} // expected-note {{should match modifier here}} + + @abi(borrowing func fn25()) // expected-error {{instance method modifier 'borrowing' in '@abi' is not compatible with 'mutating'}} {{none}} + mutating func fn25() {} // expected-note {{should match modifier here}} + + @abi(consuming func fn35()) // expected-error {{instance method modifier 'consuming' in '@abi' is not compatible with 'mutating'}} {{none}} + mutating func fn35() {} // expected-note {{should match modifier here}} + + @abi(__consuming func fn45()) // expected-error {{instance method modifier '__consuming' in '@abi' is not compatible with 'mutating'}} {{none}} + mutating func fn45() {} // expected-note {{should match modifier here}} + + @abi(mutating func fn55()) + mutating func fn55() {} +} + +// @_addressableSelf -- act like type attribute on `self` +struct AddressableSelf { + @abi(@_addressableSelf func fn1()) + @_addressableSelf func fn1() {} + + @abi(@_addressableSelf func fn2()) // expected-error {{instance method attribute '_addressableSelf' in '@abi' is not compatible with default}} {{none}} + func fn2() {} // expected-note {{should match attribute here}} + + @abi(func fn3()) // expected-error {{default instance method attribute in '@abi' is not compatible with '_addressableSelf'}} {{none}} + @_addressableSelf func fn3() {} // expected-note {{should match attribute here}} +} + // // Incorrect usage // diff --git a/test/attr/attr_abi_objc.swift b/test/attr/attr_abi_objc.swift new file mode 100644 index 0000000000000..581d1e5ab358a --- /dev/null +++ b/test/attr/attr_abi_objc.swift @@ -0,0 +1,156 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-feature ABIAttribute -parse-as-library -Rabi-inference + +// REQUIRES: swift_feature_ABIAttribute +// REQUIRES: objc_interop + +import Foundation + +// @NSCopying -- banned in @abi +class NSCopyingAttr: NSObject { + @abi(@NSCopying var v1: NSArray?) // expected-error {{unused 'NSCopying' attribute in '@abi'}} {{8-18=}} + @NSCopying var v1: NSArray? = nil + + @abi(var v2: NSArray?) + @NSCopying var v2: NSArray? = nil +} + +// Availability +// These tests will only work on a versioned platform. +@available(macOS 99, iOS 99, tvOS 99, watchOS 99, visionOS 99, *) +struct FutureType {} + +@abi(func available5(_: FutureType)) // expected-error {{'FutureType' is only available in }} +func available5(_: FutureType) {} // expected-error {{'FutureType' is only available in }} +// expected-note@-1 2{{add @available attribute to enclosing global function}} (fix-it not tested because it varies by target) + +@abi(func available6(_: FutureType)) +@available(macOS 99, iOS 99, tvOS 99, watchOS 99, visionOS 99, *) func available6(_: FutureType) {} + +// @objc -- banned in @abi +class ObjCAttr: NSObject { + @abi(@objc func fn1()) // expected-error {{unused 'objc' attribute in '@abi'}} {{8-13=}} + @objc func fn1() {} + + @abi(@objc func fn2()) // expected-error {{unused 'objc' attribute in '@abi'}} {{8-13=}} + func fn2() {} + + @abi(func fn3()) + @objc func fn3() {} +} + +// @IBAction -- banned in @abi +class IBActionAttr: NSObject { + @abi(@IBAction func fn1(_: Any)) // expected-error {{unused 'IBAction' attribute in '@abi'}} {{8-17=}} + @IBAction func fn1(_: Any) {} + + @abi(@IBAction func fn2(_: Any)) // expected-error {{unused 'IBAction' attribute in '@abi'}} {{8-17=}} + func fn2(_: Any) {} + + @abi(func fn3(_: Any)) + @IBAction func fn3(_: Any) {} +} + +// @IBInspectable -- banned in @abi +class IBInspectableAttr: NSObject { + @abi(@IBInspectable var v1: Double) // expected-error {{unused 'IBInspectable' attribute in '@abi'}} {{8-22=}} + @IBInspectable var v1: Double = 0.0 + + @abi(@IBInspectable var v2: Double) // expected-error {{unused 'IBInspectable' attribute in '@abi'}} {{8-22=}} + var v2: Double = 0.0 + + @abi(var v3: Double) + @IBInspectable var v3: Double = 0.0 +} + +// @GKInspectable -- banned in @abi +class GKInspectableAttr: NSObject { + @abi(@GKInspectable var v1: Double) // expected-error {{unused 'GKInspectable' attribute in '@abi'}} {{8-22=}} + @GKInspectable var v1: Double = 0.0 + + @abi(@GKInspectable var v2: Double) // expected-error {{unused 'GKInspectable' attribute in '@abi'}} {{8-22=}} + var v2: Double = 0.0 + + @abi(var v3: Double) + @GKInspectable var v3: Double = 0.0 +} + +// @IBOutlet -- banned in @abi +class IBOutletAttr: NSObject { + @abi(@IBOutlet var v1: NSObject!) // expected-error {{unused 'IBOutlet' attribute in '@abi'}} {{8-17=}} + @IBOutlet var v1: NSObject! + + @abi(@IBOutlet var v2: NSObject!) // expected-error {{unused 'IBOutlet' attribute in '@abi'}} {{8-17=}} + var v2: NSObject! + + @abi(var v3: NSObject!) + @IBOutlet var v3: NSObject! +} + +// @IBSegueAction -- banned in @abi +class IBSegueActionAttr: NSObject { + @abi(@IBSegueAction func fn1(_: Any) -> Any) // expected-error {{unused 'IBSegueAction' attribute in '@abi'}} {{8-22=}} + @IBSegueAction func fn1(_: Any) -> Any {} + + @abi(@IBSegueAction func fn2(_: Any) -> Any) // expected-error {{unused 'IBSegueAction' attribute in '@abi'}} {{8-22=}} + func fn2(_: Any) -> Any {} + + @abi(func fn3(_: Any) -> Any) + @IBSegueAction func fn3(_: Any) -> Any {} +} + +// @NSManaged -- banned in @abi +class NSManagedAttr: NSObject { + @abi(@NSManaged var v1: NSObject!) // expected-error {{unused 'NSManaged' attribute in '@abi'}} {{8-18=}} + @NSManaged var v1: NSObject! + + @abi(@NSManaged var v2: NSObject!) // expected-error {{unused 'NSManaged' attribute in '@abi'}} {{8-18=}} + var v2: NSObject! + + @abi(var v3: NSObject!) + @NSManaged var v3: NSObject! +} + +// @nonobjc -- banned in @abi +@objcMembers +class NonObjCAttr: NSObject { + @abi(@nonobjc var v1: NSObject!) // expected-error {{unused 'nonobjc' attribute in '@abi'}} {{8-16=}} + @nonobjc var v1: NSObject! + + @abi(@nonobjc var v2: NSObject!) // expected-error {{unused 'nonobjc' attribute in '@abi'}} {{8-16=}} + var v2: NSObject! + + @abi(var v3: NSObject!) + @nonobjc var v3: NSObject! +} + +// optional -- banned in @abi +@objc protocol OptionalModifier { + @abi( + @objc // expected-error {{unused 'objc' attribute in '@abi'}} {{5-11=}} + optional // expected-error {{unused 'optional' modifier in '@abi'}} {{5-14=}} + func fn1() + ) + @objc optional func fn1() + + @abi( + @objc // expected-error {{unused 'objc' attribute in '@abi'}} {{5-11=}} + optional // expected-error {{unused 'optional' modifier in '@abi'}} {{5-14=}} + func fn2() + ) + @objc func fn2() + + @abi(func fn3()) + @objc optional func fn3() +} + +// dynamic -- banned in @abi +class DynamicModifier: NSObject { + @abi(dynamic func fn1()) // expected-error {{unused 'dynamic' modifier in '@abi'}} {{8-15=}} + dynamic func fn1() {} + + @abi(dynamic func fn2()) // expected-error {{unused 'dynamic' modifier in '@abi'}} {{8-15=}} + func fn2() {} + + @abi(func fn3()) + dynamic func fn3() {} +} diff --git a/test/attr/attr_weaklinked.swift b/test/attr/attr_weaklinked.swift index 2de0a8eaddbb3..a56dbf4bf8ecb 100644 --- a/test/attr/attr_weaklinked.swift +++ b/test/attr/attr_weaklinked.swift @@ -1,6 +1,18 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-experimental-feature ABIAttribute + +// UNSUPPORTED: OS=windows-msvc +// REQUIRES: swift_feature_ABIAttribute -#if !os(Windows) @_weakLinked public func f() { } -#endif +// @_weakLinked -- banned in @abi +struct WeakLinked { + @abi(@_weakLinked func fn1()) // expected-error {{unused '_weakLinked' attribute in '@abi'}} {{8-20=}} + @_weakLinked func fn1() {} + + @abi(@_weakLinked func fn2()) // expected-error {{unused '_weakLinked' attribute in '@abi'}} {{8-20=}} + func fn2() {} + + @abi(func fn3()) + @_weakLinked func fn3() {} +} From d2276362a46a169983056dc0b23e52482fb2722d Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 4 Mar 2025 16:57:41 -0800 Subject: [PATCH 16/22] Support `@abi` on subscripts And make sure we reject it on `deinit`s and accessors. --- include/swift/AST/DeclAttr.def | 2 +- lib/AST/NameLookup.cpp | 2 +- lib/Parse/ParseDecl.cpp | 20 ++++++---- test/attr/attr_abi.swift | 69 ++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index f0785cce9b8ce..edfafd2630b93 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -857,7 +857,7 @@ SIMPLE_DECL_ATTR(safe, Safe, 164) DECL_ATTR(abi, ABI, - OnAbstractFunction | OnVar, + OnConstructor | OnFunc | OnSubscript | OnVar, LongAttribute | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 165) DECL_ATTR_FEATURE_REQUIREMENT(ABI, ABIAttribute) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index d806ce0499ec2..77c1e16fe3fce 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1546,7 +1546,7 @@ void MemberLookupTable::addMember(Decl *member) { A->getMemberName().addToLookupTable(Lookup, vd); auto abiRole = ABIRoleInfo(vd); - if (!abiRole.providesABI()) + if (!abiRole.providesABI() && abiRole.getCounterpart()) addMember(abiRole.getCounterpart()); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f4aced389a1b9..2a85b94c3779c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -9776,10 +9776,23 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, Decls.push_back(Subscript); + bool Invalid = false; + // Reject 'subscript' functions outside of type decls + if (!(Flags & PD_HasContainerType)) { + diagnose(SubscriptLoc, diag::subscript_decl_wrong_scope); + Invalid = true; + } + // '{' // Parse getter and setter. ParsedAccessors accessors; if (Tok.isNot(tok::l_brace)) { + // Subscript stubs should never have accessors, and this one doesn't, so + // we're done. + if (Flags.contains(PD_StubOnly)) { + return makeParserResult(Status, Subscript); + } + // Subscript declarations must always have at least a getter, so they need // to be followed by a {. if (!Status.isErrorOrHasCompletion()) { @@ -9796,13 +9809,6 @@ Parser::parseDeclSubscript(SourceLoc StaticLoc, Subscript); } - bool Invalid = false; - // Reject 'subscript' functions outside of type decls - if (!(Flags & PD_HasContainerType)) { - diagnose(SubscriptLoc, diag::subscript_decl_wrong_scope); - Invalid = true; - } - accessors.record(*this, Subscript, (Invalid || !Status.isSuccess() || Status.hasCodeCompletion())); diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index 49c1e1ff7d7bb..2f5c475c80197 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -33,6 +33,23 @@ var funcForVar: Int = 0 @abi(var varForFunc_abi: Int) // expected-error {{cannot give global function 'varForFunc()' the ABI of a pattern binding}} func varForFunc() {} +struct SameKind { + @abi(subscript(sub1 _: Int) -> Int) + subscript(sub1 _: Int) -> Int { 0 } + + @abi(func sub2(_: Int) -> Int) // expected-error {{cannot give subscript 'subscript(sub2:)' the ABI of a instance method}} + subscript(sub2 _: Int) -> Int { 0 } + + @abi(subscript(sub3 _: Int) -> Int) // expected-error {{cannot give instance method 'sub3' the ABI of a subscript}} + func sub3(_: Int) -> Int { 0 } + + @abi(var sub4: Int) // expected-error {{cannot give subscript 'subscript(sub4:)' the ABI of a pattern binding}} + subscript(sub4 _: Int) -> Int { 0 } + + @abi(subscript(sub4 _: Int) -> Int) // expected-error {{cannot give property 'sub4' the ABI of a subscript}} + var sub4: Int { 0 } +} + // // Function arity checking // @@ -91,6 +108,58 @@ func param01_generic11(_: Int) -> T { fatalError() } @abi(func param11_generic11(_: Int) -> T) func param11_generic11(_: Int) -> T { fatalError() } + + +struct SubscriptArity { + @abi(subscript(param11_generic00 _: Int) -> Int) + subscript(param11_generic00 _: Int) -> Int { 0 } + + @abi(subscript(param21_generic00 _: Int, _: Int) -> Int) // expected-error {{cannot give subscript 'subscript(param21_generic00:)' the ABI of a subscript with a different number of parameters}} + subscript(param21_generic00 _: Int) -> Int { 0 } + + @abi(subscript(param12_generic00 _: Int) -> Int) // expected-error {{cannot give subscript 'subscript(param12_generic00:_:)' the ABI of a subscript with a different number of parameters}} + subscript(param12_generic00 _: Int, _: Int) -> Int { 0 } + + @abi(subscript(param22_generic00 _: Int, _: Int) -> Int) + subscript(param22_generic00 _: Int, _: Int) -> Int { 0 } + + @abi(subscript(param11_generic10 _: T) -> Int) // expected-error {{declaration in '@abi' should not have generic signature because 'subscript(param11_generic10:)' is not generic}} + subscript(param11_generic10 _: Int) -> Int { 0 } + + @abi(subscript(param21_generic10 _: T, _: Int) -> Int) // expected-error {{declaration in '@abi' should not have generic signature because 'subscript(param21_generic10:)' is not generic}} + subscript(param21_generic10 _: Int) -> Int { 0 } + + @abi(subscript(param12_generic10 _: T) -> Int) // expected-error {{declaration in '@abi' should not have generic signature because 'subscript(param12_generic10:_:)' is not generic}} + subscript(param12_generic10 _: Int, _: Int) -> Int { 0 } + + @abi(subscript(param22_generic10 _: T, _: Int) -> Int) // expected-error {{declaration in '@abi' should not have generic signature because 'subscript(param22_generic10:_:)' is not generic}} + subscript(param22_generic10 _: Int, _: Int) -> Int { 0 } + + @abi(subscript(param11_generic01 _: Int) -> Int) // expected-error {{declaration in '@abi' should have generic signature compatible with ''}} + subscript(param11_generic01 _: T) -> Int { 0 } + + @abi(subscript(param21_generic01 _: Int, _: Int) -> Int) // expected-error {{declaration in '@abi' should have generic signature compatible with ''}} + subscript(param21_generic01 _: T) -> Int { 0 } + + @abi(subscript(param12_generic01 _: Int) -> Int) // expected-error {{declaration in '@abi' should have generic signature compatible with ''}} + subscript(param12_generic01 _: T, _: Int) -> Int { 0 } + + @abi(subscript(param22_generic01 _: Int, _: Int) -> Int) // expected-error {{declaration in '@abi' should have generic signature compatible with ''}} + subscript(param22_generic01 _: T, _: Int) -> Int { 0 } + + @abi(subscript(param11_generic11 _: T) -> Int) + subscript(param11_generic11 _: T) -> Int { 0 } + + @abi(subscript(param21_generic11 _: T, _: Int) -> Int) // expected-error {{cannot give subscript 'subscript(param21_generic11:)' the ABI of a subscript with a different number of parameters}} + subscript(param21_generic11 _: T) -> Int { 0 } + + @abi(subscript(param12_generic11 _: T) -> Int) // expected-error {{cannot give subscript 'subscript(param12_generic11:_:)' the ABI of a subscript with a different number of parameters}} + subscript(param12_generic11 _: T, _: Int) -> Int { 0 } + + @abi(subscript(param22_generic11 _: T, _: Int) -> Int) + subscript(param22_generic11 _: T, _: Int) -> Int { 0 } +} + // // Throws effect checking // From 7703d115dbeb7ed1ab452f9585f7520459dfbdb0 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 19 Mar 2025 14:27:02 -0700 Subject: [PATCH 17/22] Filter bad attrs out of module interface `@abi`s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When printing an `@abi` attribute’s decl, we now filter out any attrs that are not valid in that position. Fixes a broken test. --- include/swift/AST/AttrKind.h | 2 ++ lib/AST/ASTPrinter.cpp | 14 +++++++-- lib/AST/Attr.cpp | 33 ++++++++++++++++++-- test/ModuleInterface/attrs.swift | 42 ++++++++++++++++++++++--- test/ModuleInterface/attrs_objc.swift | 45 +++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 test/ModuleInterface/attrs_objc.swift diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index 1f4a784c70510..d56edbc4b6e31 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -145,6 +145,8 @@ enum class DeclAttrKind : unsigned { #include "swift/AST/DeclAttr.def" }; +StringRef getDeclAttrKindID(DeclAttrKind kind); + enum : unsigned { NumDeclAttrKinds = static_cast(DeclAttrKind::Last_DeclAttr) + 1, NumDeclAttrKindBits = countBitsUsed(NumDeclAttrKinds - 1), diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index a96e0783c5381..7beec725f5709 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -1284,6 +1284,15 @@ void PrintAST::printAttributes(const Decl *D) { // for each decl They cannot be shared across different decls. assert(Options.ExcludeCustomAttrList.empty()); + // If there is an `@abi` attr, we need to print it first so that it isn't + // affected by subsequent mutation of `Options.ExcludeAttrList`. + if (auto abiAttr = attrs.getAttribute()) { + if (Options.PrintImplicitAttrs && !Options.excludeAttr(abiAttr)) { + abiAttr->print(Printer, Options, D); + Options.ExcludeAttrList.push_back(DeclAttrKind::ABI); + } + } + if (Options.PrintSyntheticSILGenName && !D->getAttrs().hasAttribute()) { if (canPrintSyntheticSILGenName(D)) { @@ -1334,7 +1343,8 @@ void PrintAST::printAttributes(const Decl *D) { // Add SPIs to both private and package interfaces if (!Options.printPublicInterface() && DeclAttribute::canAttributeAppearOnDeclKind( - DeclAttrKind::SPIAccessControl, D->getKind())) { + DeclAttrKind::SPIAccessControl, D->getKind()) && + !Options.excludeAttrKind(DeclAttrKind::SPIAccessControl)) { interleave(D->getSPIGroups(), [&](Identifier spiName) { Printer.printAttrName("_spi", true); @@ -1358,7 +1368,7 @@ void PrintAST::printAttributes(const Decl *D) { // If the declaration is implicitly @objc, print the attribute now. if (auto VD = dyn_cast(D)) { if (VD->isObjC() && !isa(VD) && - !attrs.hasAttribute()) { + !attrs.hasAttribute() && ABIRoleInfo(D).providesAPI()) { Printer.printAttrName("@objc"); Printer << " "; } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 22190f5228658..61b992437d66c 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -68,6 +68,15 @@ static_assert(IsTriviallyDestructible::value, static_assert(TypeAttrKind::Id <= TypeAttrKind::Last_TypeAttr); #include "swift/AST/TypeAttr.def" +LLVM_ATTRIBUTE_USED StringRef swift::getDeclAttrKindID(DeclAttrKind kind) { + switch (kind) { +#define DECL_ATTR(_, CLASS, ...) \ + case DeclAttrKind::CLASS: \ + return #CLASS; +#include "swift/AST/DeclAttr.def" + } +} + StringRef swift::getAccessLevelSpelling(AccessLevel value) { switch (value) { case AccessLevel::Private: return "private"; @@ -1688,8 +1697,26 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, abiDecl = cast(abiDecl) ->getVarAtSimilarStructuralPosition( const_cast(cast(D))); - if (abiDecl) - abiDecl->print(Printer, Options); + if (abiDecl) { + auto optionsCopy = Options; + + // Don't print any attributes marked with `ForbiddenInABIAttr`. + // (Reminder: There is manual logic in `PrintAST::printAttributes()` + // to handle non-ABI attributes when `PrintImplicitAttrs` is set.) + for (auto rawAttrKind : range(0, unsigned(DeclAttrKind::Last_DeclAttr))) { + DeclAttrKind attrKind{rawAttrKind}; + if (!(DeclAttribute::getBehaviors(attrKind) + & DeclAttribute::ForbiddenInABIAttr)) + continue; + + if (attrKind == DeclAttrKind::AccessControl) + optionsCopy.PrintAccess = false; + else + optionsCopy.ExcludeAttrList.push_back(attrKind); + } + + abiDecl->print(Printer, optionsCopy); + } Printer << ")"; break; @@ -1754,7 +1781,7 @@ uint64_t DeclAttribute::getBehaviors(DeclAttrKind DK) { return BEHAVIORS; #include "swift/AST/DeclAttr.def" } - llvm_unreachable("bad DeclAttrKind"); + return 0; } std::optional DeclAttribute::getRequiredFeature(DeclAttrKind DK) { diff --git a/test/ModuleInterface/attrs.swift b/test/ModuleInterface/attrs.swift index cbda5e5ca8e01..effaa641cc9b7 100644 --- a/test/ModuleInterface/attrs.swift +++ b/test/ModuleInterface/attrs.swift @@ -1,10 +1,13 @@ // RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name attrs \ +// RUN: -emit-private-module-interface-path %t.private.swiftinterface \ // RUN: -enable-experimental-feature ABIAttribute \ // RUN: -enable-experimental-feature ExecutionAttribute // RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name attrs +// RUN: %target-swift-typecheck-module-from-interface(%t.private.swiftinterface) -module-name attrs -// RUN: %FileCheck %s --input-file %t.swiftinterface +// RUN: %FileCheck %s --check-prefixes CHECK,PUBLIC-CHECK --input-file %t.swiftinterface +// RUN: %FileCheck %s --check-prefixes CHECK,PRIVATE-CHECK --input-file %t.private.swiftinterface // REQUIRES: swift_feature_ABIAttribute // REQUIRES: swift_feature_ExecutionAttribute @@ -35,26 +38,55 @@ internal func __specialize_someGenericFunction(_ t: T) -> Int { fatalError("don't call") } -@abi(public func __abi__abiAttrOnFunction(param: Int)) +@abi(func __abi__abiAttrOnFunction(param: Int)) public func abiAttrOnFunction(param: Int) {} // CHECK: #if {{.*}} $ABIAttribute -// CHECK: @abi(public func __abi__abiAttrOnFunction(param: Swift.Int)) +// CHECK: @abi(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) +@abi(let __abi__abiAttrOnVar: Int) public var abiAttrOnVar: Int = 42 // CHECK: #if {{.*}} $ABIAttribute -// CHECK: @abi(public var __abi__abiAttrOnVar: Swift.Int) +// CHECK: @abi(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 +public struct MutatingTest { + // CHECK: #if {{.*}} $ABIAttribute + // CHECK: @abi(mutating func abiMutFunc()) + // CHECK: public mutating func abiMutFunc() + // CHECK: #else + // CHECK: @_silgen_name("$s5attrs12MutatingTestV10abiMutFuncyyF") + // CHECK: public mutating func abiMutFunc() + // CHECK: #endif + @abi(mutating func abiMutFunc()) + public mutating func abiMutFunc() {} +} + +// PUBLIC-CHECK-NOT: #if {{.*}} $ABIAttribute +// PUBLIC-CHECK-NOT: @abi(func abiSpiFunc()) +// PUBLIC-CHECK-NOT: public func abiSpiFunc() +// PUBLIC-CHECK-NOT: #else +// PUBLIC-CHECK-NOT: @_silgen_name("$s5attrs10abiSpiFuncyyF") +// PUBLIC-CHECK-NOT: public func abiSpiFunc() +// PUBLIC-CHECK-NOT: #endif +// PRIVATE-CHECK: #if {{.*}} $ABIAttribute +// PRIVATE-CHECK: @abi(func abiSpiFunc()) +// PRIVATE-CHECK: public func abiSpiFunc() +// PRIVATE-CHECK: #else +// PRIVATE-CHECK: @_silgen_name("$s5attrs10abiSpiFuncyyF") +// PRIVATE-CHECK: public func abiSpiFunc() +// PRIVATE-CHECK: #endif +@abi(func abiSpiFunc()) +@_spi(spiGroup) public func abiSpiFunc() {} + @execution(concurrent) public func testExecutionConcurrent() async {} // CHECK: @execution(concurrent) public func testExecutionConcurrent() async diff --git a/test/ModuleInterface/attrs_objc.swift b/test/ModuleInterface/attrs_objc.swift new file mode 100644 index 0000000000000..37cb78318be05 --- /dev/null +++ b/test/ModuleInterface/attrs_objc.swift @@ -0,0 +1,45 @@ +// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s \ +// RUN: -enable-objc-interop -module-name attrs_objc \ +// RUN: -enable-experimental-feature ABIAttribute + +// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name attrs_objc + +// RUN: %FileCheck %s --input-file %t.swiftinterface + +// REQUIRES: objc_interop +// REQUIRES: swift_feature_ABIAttribute + +import Foundation + +@objcMembers +public class ObjCTest: NSObject { + // CHECK: #if {{.*}} $ABIAttribute + // CHECK: @abi(func abiObjCFunc()) + // CHECK: @objc public func abiObjCFunc() + // CHECK: #else + // CHECK: @_silgen_name("$s10attrs_objc8ObjCTestC03abiC5CFuncyyF") + // CHECK: @objc public func abiObjCFunc() + // CHECK: #endif + @abi(func abiObjCFunc()) + @objc public func abiObjCFunc() {} + + // CHECK: #if {{.*}} $ABIAttribute + // CHECK: @abi(func abiImplicitObjCFunc()) + // CHECK: @objc public func abiImplicitObjCFunc() + // CHECK: #else + // CHECK: @_silgen_name("$s10attrs_objc8ObjCTestC011abiImplicitC5CFuncyyF") + // CHECK: @objc public func abiImplicitObjCFunc() + // CHECK: #endif + @abi(func abiImplicitObjCFunc()) + public func abiImplicitObjCFunc() {} + + // CHECK: #if {{.*}} $ABIAttribute + // CHECK: @abi(func abiIBActionFunc(_: Any)) + // CHECK: @objc @IBAction @_Concurrency.MainActor @preconcurrency public func abiIBActionFunc(_: Any) + // CHECK: #else + // CHECK: @_silgen_name("$s10attrs_objc8ObjCTestC15abiIBActionFuncyyypF") + // CHECK: @objc @IBAction @_Concurrency.MainActor @preconcurrency public func abiIBActionFunc(_: Any) + // CHECK: #endif + @abi(func abiIBActionFunc(_: Any)) + @IBAction public func abiIBActionFunc(_: Any) {} +} From ef738e6b5a198e04af958f6e209bd5419a0f20db Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 19 Mar 2025 15:00:24 -0700 Subject: [PATCH 18/22] Make `@abi` type diagnostics more specific Specify whether the type with the problem is a result type, parameter type (and which parameter), etc. --- include/swift/AST/DiagnosticsSema.def | 13 +- lib/Sema/TypeCheckAttrABI.cpp | 84 +++++- test/attr/attr_abi.swift | 404 +++++++++++++------------- 3 files changed, 280 insertions(+), 221 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 9c751a3235819..619d8d4ce78c4 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -8397,9 +8397,11 @@ NOTE(attr_abi_matching_attr_here,none, "%select{|implicitly added }2here", (/*matches=*/bool, /*isModifier=*/bool, /*isImplicit=*/bool)) +#define TYPE_ORIGIN(KIND_IDX, DECL_IDX) "%select{|%kind" #DECL_IDX " |" \ + "self parameter |result |thrown |%error}" #KIND_IDX ERROR(attr_abi_mismatched_type,none, - "type %0 in '@abi' should match %1", - (Type, Type)) + TYPE_ORIGIN(0, 1) "type %2 in '@abi' should match %3", + (unsigned, Decl *, Type, Type)) NOTE(attr_abi_should_match_type_here,none, "should match type here", ()) @@ -8416,9 +8418,10 @@ ERROR(attr_abi_extra_generic_signature,none, (Decl *)) ERROR(attr_abi_mismatched_param_modifier,none, - "%select{default |}0%3 %select{attribute|modifier}2 " - "%select{|'%0' }0in '@abi' is not compatible with %select{default|'%1'}1", - (StringRef, StringRef, /*isModifier=*/bool, DescriptiveDeclKind)) + "%select{default |}0%select{attribute|modifier}2 %select{|'%0' }0" + "on " TYPE_ORIGIN(3, 4) "in '@abi' is not compatible with " + "%select{default|'%1'}1", + (StringRef, StringRef, /*isModifier=*/bool, unsigned, Decl *)) ERROR(attr_abi_no_default_arguments,none, "%kind0 in '@abi' should not have a default argument; it does not " "affect the parameter's ABI", diff --git a/lib/Sema/TypeCheckAttrABI.cpp b/lib/Sema/TypeCheckAttrABI.cpp index 1f1120445f9a7..cf7b4be01c49e 100644 --- a/lib/Sema/TypeCheckAttrABI.cpp +++ b/lib/Sema/TypeCheckAttrABI.cpp @@ -90,6 +90,54 @@ struct DeclEffects { } }; +/// Describes the relationship between a given type and the declaration it +/// belongs to--e.g. is this its result type? a parameter type? etc. Used with +/// a couple of \c \@abi diagnostics. +class TypeOrigin { +public: + // Cases must be kept in sync with DiagnosticsSema TYPE_ORIGIN + enum class Kind : uint8_t { + Unspecified = 0, + Parameter = 1, + SelfParameter = 2, + Result = 3, + ThrowsEffect = 4, + }; + +private: + llvm::PointerIntPair declAndKind; + + TypeOrigin(Decl *decl, Kind kind) + : declAndKind(decl, kind) {} + +public: + static TypeOrigin forUnspecified() { + return TypeOrigin(nullptr, Kind::Unspecified); + } + + static TypeOrigin forParameter(ParamDecl *paramDecl) { + return TypeOrigin(paramDecl, + paramDecl->isSelfParameter() ? Kind::SelfParameter + : Kind::Parameter); + } + + static TypeOrigin forResult() { + return TypeOrigin(nullptr, Kind::Result); + } + + static TypeOrigin forThrowsEffect() { + return TypeOrigin(nullptr, Kind::ThrowsEffect); + } + + Kind getKind() const { + return declAndKind.getInt(); + } + + Decl *getDecl() const { + return declAndKind.getPointer(); + } +}; + /// Emit a fix-it replacing \p charRange with \p newText , inserting or /// removing whitespace after \c charRange in a way suitable for editing a /// sequence of whitespce-separated keywords. @@ -217,8 +265,10 @@ class ABIDeclChecker : public ASTComparisonVisitor { ParameterTypeFlags abiOrig, Type apiType, Type abiType, SourceLoc apiTypeLoc, SourceLoc abiTypeLoc, - DescriptiveDeclKind declKind, - bool isSelfParam) { + TypeOrigin origin) { + // Some keywords are spelled differently for a `self` parameter. + bool isSelfParam = origin.getKind() == TypeOrigin::Kind::SelfParameter; + bool didDiagnose = false; auto noteShouldMatch = [&](bool isModifier) { @@ -255,7 +305,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { ctx.Diags.diagnose(abiTypeLoc, diag::attr_abi_mismatched_param_modifier, getSpelling(abiOrig.getOwnershipSpecifier()), getSpelling(apiOrig.getOwnershipSpecifier()), - /*isModifier=*/true, declKind); + /*isModifier=*/true, unsigned(origin.getKind()), + origin.getDecl()); noteShouldMatch(/*isModifier=*/true); didDiagnose = true; } @@ -264,7 +315,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { ctx.Diags.diagnose(abiTypeLoc, diag::attr_abi_mismatched_param_modifier, abiOrig.isNoDerivative() ? "noDerivative" : "", apiOrig.isNoDerivative() ? "noDerivative" : "", - /*isModifier=*/false, declKind); + /*isModifier=*/false, unsigned(origin.getKind()), + origin.getDecl()); noteShouldMatch(/*isModifier=*/false); didDiagnose = true; } @@ -274,7 +326,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { ctx.Diags.diagnose(abiTypeLoc, diag::attr_abi_mismatched_param_modifier, abiOrig.isAddressable() ? spelling : "", apiOrig.isAddressable() ? spelling : "", - /*isModifier=*/false, declKind); + /*isModifier=*/false, unsigned(origin.getKind()), + origin.getDecl()); noteShouldMatch(/*isModifier=*/false); didDiagnose = true; } @@ -282,6 +335,7 @@ class ABIDeclChecker : public ASTComparisonVisitor { if (!didDiagnose && api != abi) { // Flag difference not otherwise diagnosed. This is a fallback diagnostic. ctx.Diags.diagnose(abiTypeLoc, diag::attr_abi_mismatched_type, + unsigned(origin.getKind()), origin.getDecl(), abiType, apiType); ctx.Diags.diagnose(apiTypeLoc, diag::attr_abi_should_match_type_here); didDiagnose = true; @@ -316,10 +370,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { SourceLoc abiTypeLoc = getTypeLoc(abi, abiDecl); didDiagnose |= checkType(apiNorm.getPlainType(), abiNorm.getPlainType(), - apiTypeLoc, abiTypeLoc); - - auto declKind = api->isSelfParameter() ? apiDecl->getDescriptiveKind() - : DescriptiveDeclKind::Param; + apiTypeLoc, abiTypeLoc, + TypeOrigin::forParameter(abi)); didDiagnose |= checkParameterFlags(apiNorm.getParameterFlags(), abiNorm.getParameterFlags(), @@ -328,7 +380,7 @@ class ABIDeclChecker : public ASTComparisonVisitor { apiNorm.getPlainType(), abiNorm.getPlainType(), apiTypeLoc, abiTypeLoc, - declKind, api->isSelfParameter()); + TypeOrigin::forParameter(abi)); didDiagnose |= checkAttrs(api->getAttrs(), abi->getAttrs(), api, abi); @@ -642,7 +694,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { return checkType(api->getResultInterfaceType(), abi->getResultInterfaceType(), api->getResultTypeSourceRange().Start, - abi->getResultTypeSourceRange().Start); + abi->getResultTypeSourceRange().Start, + TypeOrigin::forResult()); } bool visitConstructorDecl(ConstructorDecl *api, ConstructorDecl *abi) { @@ -658,7 +711,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { return true; if (checkType(api->getValueInterfaceType(), abi->getValueInterfaceType(), - getTypeLoc(api), getTypeLoc(abi))) + getTypeLoc(api), getTypeLoc(abi), + TypeOrigin::forUnspecified())) return true; return false; @@ -868,7 +922,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { // MARK: @abi checking - types - bool checkType(Type api, Type abi, SourceLoc apiLoc, SourceLoc abiLoc) { + bool checkType(Type api, Type abi, SourceLoc apiLoc, SourceLoc abiLoc, + TypeOrigin origin) { if (!api.isNull() && !abi.isNull()) { Type apiNorm = normalizeType(api); Type abiNorm = normalizeType(abi); @@ -877,7 +932,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { } } - ctx.Diags.diagnose(abiLoc, diag::attr_abi_mismatched_type, abi, api); + ctx.Diags.diagnose(abiLoc, diag::attr_abi_mismatched_type, + unsigned(origin.getKind()), origin.getDecl(), abi, api); ctx.Diags.diagnose(apiLoc, diag::attr_abi_should_match_type_here); return true; } diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index 2f5c475c80197..41288141ec637 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -374,10 +374,10 @@ func fn() { // Type differences // -@abi(func floatForIntParam(_: Float) -> Int) // expected-error @:31 {{type 'Float' in '@abi' should match 'Int'}} +@abi(func floatForIntParam(_ a: Float) -> Int) // expected-error @:33 {{parameter 'a' type 'Float' in '@abi' should match 'Int'}} func intForFloatParam(_: Int) -> Int { fatalError() } // expected-note @:26 {{should match type here}} -@abi(func floatForIntResult(_: Int) -> Float) // expected-error @:40 {{type 'Float' in '@abi' should match 'Int'}} +@abi(func floatForIntResult(_ a: Int) -> Float) // expected-error @:42 {{result type 'Float' in '@abi' should match 'Int'}} func intForFloatResult(_: Int) -> Int { fatalError() } // expected-note @:35 {{should match type here}} @abi(func labeledForUnlabeledTuple(_: (x: Int, y: Int))) @@ -404,22 +404,22 @@ func testDefaultArguments( d: Int = 2 ) {} -@abi(func arrayForVariadicParam(a: [Int], b: Set)) // expected-error @:46 {{type 'Set' in '@abi' should match 'Float...'}} +@abi(func arrayForVariadicParam(a: [Int], b: Set)) // expected-error @:46 {{parameter 'b' type 'Set' in '@abi' should match 'Float...'}} func arrayForVariadicParam(a: Int..., b: Float...) {} // expected-note @:42 {{should match type here}} struct DefaultParamOwnership { @abi( func method( - _: AnyObject, - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with default}} - _: borrowing AnyObject, - _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with default}} - _: __shared AnyObject, - _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with default}} - _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with default}} - _: (AnyObject) -> Void, - _: (borrowing AnyObject) -> Void, - _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(AnyObject) -> Void'}} + _ a: AnyObject, + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with default}} + _ c: borrowing AnyObject, + _ d: consuming AnyObject, // expected-error {{modifier 'consuming' on parameter 'd' in '@abi' is not compatible with default}} + _ e: __shared AnyObject, + _ f: __owned AnyObject, // expected-error {{modifier '__owned' on parameter 'f' in '@abi' is not compatible with default}} + _ g: sending AnyObject, // expected-error {{modifier 'sending' on parameter 'g' in '@abi' is not compatible with default}} + _ h: (AnyObject) -> Void, + _ i: (borrowing AnyObject) -> Void, + _ j: (consuming AnyObject) -> Void // expected-error {{parameter 'j' type '(consuming AnyObject) -> Void' in '@abi' should match '(AnyObject) -> Void'}} ) ) func method( @@ -437,16 +437,16 @@ struct DefaultParamOwnership { @abi( init( - _: AnyObject, - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with default}} - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with default}} - _: consuming AnyObject, - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with default}} - _: __owned AnyObject, - _: sending AnyObject, - _: (AnyObject) -> Void, - _: (borrowing AnyObject) -> Void, - _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(AnyObject) -> Void'}} + _ a: AnyObject, + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with default}} + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with default}} + _ d: consuming AnyObject, + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with default}} + _ f: __owned AnyObject, + _ g: sending AnyObject, + _ h: (AnyObject) -> Void, + _ i: (borrowing AnyObject) -> Void, + _ j: (consuming AnyObject) -> Void // expected-error {{parameter 'j' type '(consuming AnyObject) -> Void' in '@abi' should match '(AnyObject) -> Void'}} ) ) init( @@ -466,16 +466,16 @@ struct DefaultParamOwnership { struct InoutParamOwnership { @abi( func method( - _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'inout'}} - _: inout AnyObject, - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'inout'}} - _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with 'inout'}} - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'inout'}} - _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with 'inout'}} - _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with 'inout'}} - _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} - _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} - _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _ a: AnyObject, // expected-error {{default modifier on parameter 'a' in '@abi' is not compatible with 'inout'}} + _ b: inout AnyObject, + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with 'inout'}} + _ d: consuming AnyObject, // expected-error {{modifier 'consuming' on parameter 'd' in '@abi' is not compatible with 'inout'}} + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with 'inout'}} + _ f: __owned AnyObject, // expected-error {{modifier '__owned' on parameter 'f' in '@abi' is not compatible with 'inout'}} + _ g: sending AnyObject, // expected-error {{modifier 'sending' on parameter 'g' in '@abi' is not compatible with 'inout'}} + _ h: (AnyObject) -> Void, // expected-error {{parameter 'h' type '(AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _ i: (borrowing AnyObject) -> Void, // expected-error {{parameter 'i' type '(borrowing AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _ j: (consuming AnyObject) -> Void // expected-error {{parameter 'j' type '(consuming AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} ) ) func method( @@ -493,16 +493,16 @@ struct InoutParamOwnership { @abi( init( - _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'inout'}} - _: inout AnyObject, - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'inout'}} - _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with 'inout'}} - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'inout'}} - _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with 'inout'}} - _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with 'inout'}} - _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} - _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} - _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _ a: AnyObject, // expected-error {{default modifier on parameter 'a' in '@abi' is not compatible with 'inout'}} + _ b: inout AnyObject, + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with 'inout'}} + _ d: consuming AnyObject, // expected-error {{modifier 'consuming' on parameter 'd' in '@abi' is not compatible with 'inout'}} + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with 'inout'}} + _ f: __owned AnyObject, // expected-error {{modifier '__owned' on parameter 'f' in '@abi' is not compatible with 'inout'}} + _ g: sending AnyObject, // expected-error {{modifier 'sending' on parameter 'g' in '@abi' is not compatible with 'inout'}} + _ h: (AnyObject) -> Void, // expected-error {{parameter 'h' type '(AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _ i: (borrowing AnyObject) -> Void, // expected-error {{parameter 'i' type '(borrowing AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} + _ j: (consuming AnyObject) -> Void // expected-error {{parameter 'j' type '(consuming AnyObject) -> Void' in '@abi' should match '(inout AnyObject) -> Void'}} ) ) init( @@ -522,16 +522,16 @@ struct InoutParamOwnership { struct BorrowingParamOwnership { @abi( func method( - _: AnyObject, - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'borrowing'}} - _: borrowing AnyObject, - _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with 'borrowing'}} - _: __shared AnyObject, - _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with 'borrowing'}} - _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with 'borrowing'}} - _: (AnyObject) -> Void, - _: (borrowing AnyObject) -> Void, - _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(borrowing AnyObject) -> Void'}} + _ a: AnyObject, + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with 'borrowing'}} + _ c: borrowing AnyObject, + _ d: consuming AnyObject, // expected-error {{modifier 'consuming' on parameter 'd' in '@abi' is not compatible with 'borrowing'}} + _ e: __shared AnyObject, + _ f: __owned AnyObject, // expected-error {{modifier '__owned' on parameter 'f' in '@abi' is not compatible with 'borrowing'}} + _ g: sending AnyObject, // expected-error {{modifier 'sending' on parameter 'g' in '@abi' is not compatible with 'borrowing'}} + _ h: (AnyObject) -> Void, + _ i: (borrowing AnyObject) -> Void, + _ j: (consuming AnyObject) -> Void // expected-error {{parameter 'j' type '(consuming AnyObject) -> Void' in '@abi' should match '(borrowing AnyObject) -> Void'}} ) ) func method( @@ -549,16 +549,16 @@ struct BorrowingParamOwnership { @abi( init( - _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'borrowing'}} - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'borrowing'}} - _: borrowing AnyObject, - _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with 'borrowing'}} - _: __shared AnyObject, - _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with 'borrowing'}} - _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with 'borrowing'}} - _: (AnyObject) -> Void, - _: (borrowing AnyObject) -> Void, - _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(borrowing AnyObject) -> Void'}} + _ a: AnyObject, // expected-error {{default modifier on parameter 'a' in '@abi' is not compatible with 'borrowing'}} + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with 'borrowing'}} + _ c: borrowing AnyObject, + _ d: consuming AnyObject, // expected-error {{modifier 'consuming' on parameter 'd' in '@abi' is not compatible with 'borrowing'}} + _ e: __shared AnyObject, + _ f: __owned AnyObject, // expected-error {{modifier '__owned' on parameter 'f' in '@abi' is not compatible with 'borrowing'}} + _ g: sending AnyObject, // expected-error {{modifier 'sending' on parameter 'g' in '@abi' is not compatible with 'borrowing'}} + _ h: (AnyObject) -> Void, + _ i: (borrowing AnyObject) -> Void, + _ j: (consuming AnyObject) -> Void // expected-error {{parameter 'j' type '(consuming AnyObject) -> Void' in '@abi' should match '(borrowing AnyObject) -> Void'}} ) ) init( @@ -578,16 +578,16 @@ struct BorrowingParamOwnership { struct ConsumingParamOwnership { @abi( func method( - _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'consuming'}} - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'consuming'}} - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'consuming'}} - _: consuming AnyObject, - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'consuming'}} - _: __owned AnyObject, - _: sending AnyObject, - _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} - _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} - _: (consuming AnyObject) -> Void + _ a: AnyObject, // expected-error {{default modifier on parameter 'a' in '@abi' is not compatible with 'consuming'}} + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with 'consuming'}} + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with 'consuming'}} + _ d: consuming AnyObject, + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with 'consuming'}} + _ f: __owned AnyObject, + _ g: sending AnyObject, + _ h: (AnyObject) -> Void, // expected-error {{parameter 'h' type '(AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} + _ i: (borrowing AnyObject) -> Void, // expected-error {{parameter 'i' type '(borrowing AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} + _ j: (consuming AnyObject) -> Void ) ) func method( @@ -605,16 +605,16 @@ struct ConsumingParamOwnership { @abi( init( - _: AnyObject, - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'consuming'}} - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'consuming'}} - _: consuming AnyObject, - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'consuming'}} - _: __owned AnyObject, - _: sending AnyObject, - _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} - _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} - _: (consuming AnyObject) -> Void + _ a: AnyObject, + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with 'consuming'}} + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with 'consuming'}} + _ d: consuming AnyObject, + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with 'consuming'}} + _ f: __owned AnyObject, + _ g: sending AnyObject, + _ h: (AnyObject) -> Void, // expected-error {{parameter 'h' type '(AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} + _ i: (borrowing AnyObject) -> Void, // expected-error {{parameter 'i' type '(borrowing AnyObject) -> Void' in '@abi' should match '(consuming AnyObject) -> Void'}} + _ j: (consuming AnyObject) -> Void ) ) init( @@ -634,16 +634,16 @@ struct ConsumingParamOwnership { struct SharedParamOwnership { @abi( func method( - _: AnyObject, - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with '__shared'}} - _: borrowing AnyObject, - _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with '__shared'}} - _: __shared AnyObject, - _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with '__shared'}} - _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with '__shared'}} - _: (AnyObject) -> Void, - _: (borrowing AnyObject) -> Void, - _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(__shared AnyObject) -> Void'}} + _ a: AnyObject, + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with '__shared'}} + _ c: borrowing AnyObject, + _ d: consuming AnyObject, // expected-error {{modifier 'consuming' on parameter 'd' in '@abi' is not compatible with '__shared'}} + _ e: __shared AnyObject, + _ f: __owned AnyObject, // expected-error {{modifier '__owned' on parameter 'f' in '@abi' is not compatible with '__shared'}} + _ g: sending AnyObject, // expected-error {{modifier 'sending' on parameter 'g' in '@abi' is not compatible with '__shared'}} + _ h: (AnyObject) -> Void, + _ i: (borrowing AnyObject) -> Void, + _ j: (consuming AnyObject) -> Void // expected-error {{parameter 'j' type '(consuming AnyObject) -> Void' in '@abi' should match '(__shared AnyObject) -> Void'}} ) ) func method( @@ -661,16 +661,16 @@ struct SharedParamOwnership { @abi( init( - _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with '__shared'}} - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with '__shared'}} - _: borrowing AnyObject, - _: consuming AnyObject, // expected-error {{parameter modifier 'consuming' in '@abi' is not compatible with '__shared'}} - _: __shared AnyObject, - _: __owned AnyObject, // expected-error {{parameter modifier '__owned' in '@abi' is not compatible with '__shared'}} - _: sending AnyObject, // expected-error {{parameter modifier 'sending' in '@abi' is not compatible with '__shared'}} - _: (AnyObject) -> Void, - _: (borrowing AnyObject) -> Void, - _: (consuming AnyObject) -> Void // expected-error {{type '(consuming AnyObject) -> Void' in '@abi' should match '(__shared AnyObject) -> Void'}} + _ a: AnyObject, // expected-error {{default modifier on parameter 'a' in '@abi' is not compatible with '__shared'}} + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with '__shared'}} + _ c: borrowing AnyObject, + _ d: consuming AnyObject, // expected-error {{modifier 'consuming' on parameter 'd' in '@abi' is not compatible with '__shared'}} + _ e: __shared AnyObject, + _ f: __owned AnyObject, // expected-error {{modifier '__owned' on parameter 'f' in '@abi' is not compatible with '__shared'}} + _ g: sending AnyObject, // expected-error {{modifier 'sending' on parameter 'g' in '@abi' is not compatible with '__shared'}} + _ h: (AnyObject) -> Void, + _ i: (borrowing AnyObject) -> Void, + _ j: (consuming AnyObject) -> Void // expected-error {{parameter 'j' type '(consuming AnyObject) -> Void' in '@abi' should match '(__shared AnyObject) -> Void'}} ) ) init( @@ -690,16 +690,16 @@ struct SharedParamOwnership { struct OwnedParamOwnership { @abi( func method( - _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with '__owned'}} - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with '__owned'}} - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with '__owned'}} - _: consuming AnyObject, - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with '__owned'}} - _: __owned AnyObject, - _: sending AnyObject, - _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} - _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} - _: (consuming AnyObject) -> Void + _ a: AnyObject, // expected-error {{default modifier on parameter 'a' in '@abi' is not compatible with '__owned'}} + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with '__owned'}} + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with '__owned'}} + _ d: consuming AnyObject, + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with '__owned'}} + _ f: __owned AnyObject, + _ g: sending AnyObject, + _ h: (AnyObject) -> Void, // expected-error {{parameter 'h' type '(AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} + _ i: (borrowing AnyObject) -> Void, // expected-error {{parameter 'i' type '(borrowing AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} + _ j: (consuming AnyObject) -> Void ) ) func method( @@ -717,16 +717,16 @@ struct OwnedParamOwnership { @abi( init( - _: AnyObject, - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with '__owned'}} - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with '__owned'}} - _: consuming AnyObject, - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with '__owned'}} - _: __owned AnyObject, - _: sending AnyObject, - _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} - _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} - _: (consuming AnyObject) -> Void + _ a: AnyObject, + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with '__owned'}} + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with '__owned'}} + _ d: consuming AnyObject, + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with '__owned'}} + _ f: __owned AnyObject, + _ g: sending AnyObject, + _ h: (AnyObject) -> Void, // expected-error {{parameter 'h' type '(AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} + _ i: (borrowing AnyObject) -> Void, // expected-error {{parameter 'i' type '(borrowing AnyObject) -> Void' in '@abi' should match '(__owned AnyObject) -> Void'}} + _ j: (consuming AnyObject) -> Void ) ) init( @@ -746,16 +746,16 @@ struct OwnedParamOwnership { struct SendingParamOwnership { @abi( func method( - _: AnyObject, // expected-error {{default parameter modifier in '@abi' is not compatible with 'sending'}} - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'sending'}} - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'sending'}} - _: consuming AnyObject, - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'sending'}} - _: __owned AnyObject, - _: sending AnyObject, - _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} - _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} - _: (consuming AnyObject) -> Void + _ a: AnyObject, // expected-error {{default modifier on parameter 'a' in '@abi' is not compatible with 'sending'}} + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with 'sending'}} + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with 'sending'}} + _ d: consuming AnyObject, + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with 'sending'}} + _ f: __owned AnyObject, + _ g: sending AnyObject, + _ h: (AnyObject) -> Void, // expected-error {{parameter 'h' type '(AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} + _ i: (borrowing AnyObject) -> Void, // expected-error {{parameter 'i' type '(borrowing AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} + _ j: (consuming AnyObject) -> Void ) ) func method( @@ -773,16 +773,16 @@ struct SendingParamOwnership { @abi( init( - _: AnyObject, - _: inout AnyObject, // expected-error {{parameter modifier 'inout' in '@abi' is not compatible with 'sending'}} - _: borrowing AnyObject, // expected-error {{parameter modifier 'borrowing' in '@abi' is not compatible with 'sending'}} - _: consuming AnyObject, - _: __shared AnyObject, // expected-error {{parameter modifier '__shared' in '@abi' is not compatible with 'sending'}} - _: __owned AnyObject, - _: sending AnyObject, - _: (AnyObject) -> Void, // expected-error {{type '(AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} - _: (borrowing AnyObject) -> Void, // expected-error {{type '(borrowing AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} - _: (consuming AnyObject) -> Void + _ a: AnyObject, + _ b: inout AnyObject, // expected-error {{modifier 'inout' on parameter 'b' in '@abi' is not compatible with 'sending'}} + _ c: borrowing AnyObject, // expected-error {{modifier 'borrowing' on parameter 'c' in '@abi' is not compatible with 'sending'}} + _ d: consuming AnyObject, + _ e: __shared AnyObject, // expected-error {{modifier '__shared' on parameter 'e' in '@abi' is not compatible with 'sending'}} + _ f: __owned AnyObject, + _ g: sending AnyObject, + _ h: (AnyObject) -> Void, // expected-error {{parameter 'h' type '(AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} + _ i: (borrowing AnyObject) -> Void, // expected-error {{parameter 'i' type '(borrowing AnyObject) -> Void' in '@abi' should match '(sending AnyObject) -> Void'}} + _ j: (consuming AnyObject) -> Void ) ) init( @@ -856,22 +856,22 @@ func constTest( ) {} // @noDerivative should match -@abi(func noDerivativeTest1(_: @differentiable(reverse) (@noDerivative Double, Double) -> Double)) +@abi(func noDerivativeTest1(_ a: @differentiable(reverse) (@noDerivative Double, Double) -> Double)) func noDerivativeTest1(_: @differentiable(reverse) (@noDerivative Double, Double) -> Double) {} -@abi(func noDerivativeTest2(_: @differentiable(reverse) (Double, Double) -> Double)) // expected-error {{type '@differentiable(reverse) (Double, Double) -> Double' in '@abi' should match '@differentiable(reverse) (@noDerivative Double, Double) -> Double'}} +@abi(func noDerivativeTest2(_ a: @differentiable(reverse) (Double, Double) -> Double)) // expected-error {{parameter 'a' type '@differentiable(reverse) (Double, Double) -> Double' in '@abi' should match '@differentiable(reverse) (@noDerivative Double, Double) -> Double'}} func noDerivativeTest2(_: @differentiable(reverse) (@noDerivative Double, Double) -> Double) {} // expected-note {{should match type here}} -@abi(func noDerivativeTest3(_: @differentiable(reverse) (@noDerivative Double, Double) -> Double)) // expected-error {{type '@differentiable(reverse) (@noDerivative Double, Double) -> Double' in '@abi' should match '@differentiable(reverse) (Double, Double) -> Double'}} +@abi(func noDerivativeTest3(_ a: @differentiable(reverse) (@noDerivative Double, Double) -> Double)) // expected-error {{parameter 'a' type '@differentiable(reverse) (@noDerivative Double, Double) -> Double' in '@abi' should match '@differentiable(reverse) (Double, Double) -> Double'}} func noDerivativeTest3(_: @differentiable(reverse) (Double, Double) -> Double) {} // expected-note {{should match type here}} // @_addressable should match @abi( func addressableTest( - _: @_addressable String, - _: String, // expected-error {{default parameter attribute in '@abi' is not compatible with '_addressable'}} - _: @_addressable String, // expected-error {{parameter attribute '_addressable' in '@abi' is not compatible with default}} - _: String + _ a: @_addressable String, + _ b: String, // expected-error {{default attribute on parameter 'b' in '@abi' is not compatible with '_addressable'}} + _ c: @_addressable String, // expected-error {{attribute '_addressable' on parameter 'c' in '@abi' is not compatible with default}} + _ d: String ) ) func addressableTest( @@ -884,27 +884,27 @@ func addressableTest( // Flattening of function type ExtInfo @abi( func fnExtInfoTest( - _: @escaping () -> AnyObject, - _: @Sendable () -> AnyObject, - _: () -> sending AnyObject, - _: () -> AnyObject, - _: @MainActor () -> AnyObject, - _: (isolated MainActor) -> AnyObject, - _: @isolated(any) () -> AnyObject, // expected-error {{type '@isolated(any) () -> AnyObject' in '@abi' should match '() -> AnyObject'}} - _: @execution(caller) () async -> AnyObject, - _: () -> AnyObject, // expected-error {{type '() -> AnyObject' in '@abi' should match '@isolated(any) () -> AnyObject'}} - _: () async -> Void, - _: () -> Void, // expected-error {{type '() -> Void' in '@abi' should match '() async -> Void'}} - _: () async -> Void, // expected-error {{type '() async -> Void' in '@abi' should match '() -> Void'}} - _: () -> Void, - _: () throws -> Void, - _: () -> Void, // expected-error {{type '() -> Void' in '@abi' should match '() throws -> Void'}} - _: () throws -> Void, // expected-error {{type '() throws -> Void' in '@abi' should match '() -> Void'}} - _: () -> Void, - _: () -> Void, - _: @convention(block) () -> Void, // expected-error {{type '@convention(block) () -> Void' in '@abi' should match '() -> Void'}} - _: @convention(thin) () -> Void, // expected-error {{type '@convention(thin) () -> Void' in '@abi' should match '() -> Void'}} - _: @convention(c) () -> Void // expected-error {{type '@convention(c) () -> Void' in '@abi' should match '() -> Void'}} + _ a: @escaping () -> AnyObject, + _ b: @Sendable () -> AnyObject, + _ c: () -> sending AnyObject, + _ d: () -> AnyObject, + _ e: @MainActor () -> AnyObject, + _ f: (isolated MainActor) -> AnyObject, + _ g: @isolated(any) () -> AnyObject, // expected-error {{parameter 'g' type '@isolated(any) () -> AnyObject' in '@abi' should match '() -> AnyObject'}} + _ h: @execution(caller) () async -> AnyObject, + _ i: () -> AnyObject, // expected-error {{parameter 'i' type '() -> AnyObject' in '@abi' should match '@isolated(any) () -> AnyObject'}} + _ j: () async -> Void, + _ k: () -> Void, // expected-error {{parameter 'k' type '() -> Void' in '@abi' should match '() async -> Void'}} + _ l: () async -> Void, // expected-error {{parameter 'l' type '() async -> Void' in '@abi' should match '() -> Void'}} + _ m: () -> Void, + _ n: () throws -> Void, + _ o: () -> Void, // expected-error {{parameter 'o' type '() -> Void' in '@abi' should match '() throws -> Void'}} + _ p: () throws -> Void, // expected-error {{parameter 'p' type '() throws -> Void' in '@abi' should match '() -> Void'}} + _ q: () -> Void, + _ r: () -> Void, + _ s: @convention(block) () -> Void, // expected-error {{parameter 's' type '@convention(block) () -> Void' in '@abi' should match '() -> Void'}} + _ t: @convention(thin) () -> Void, // expected-error {{parameter 't' type '@convention(thin) () -> Void' in '@abi' should match '() -> Void'}} + _ u: @convention(c) () -> Void // expected-error {{parameter 'u' type '@convention(c) () -> Void' in '@abi' should match '() -> Void'}} ) ) func fnExtInfoTest( @@ -950,12 +950,12 @@ func testMarkerProtocols( @abi( func testNormalProtocols( - _: Any, // expected-error {{type 'Any' in '@abi' should match 'any CustomStringConvertible'}} - _: CustomStringConvertible, // expected-error {{type 'any CustomStringConvertible' in '@abi' should match 'Any'}} - _: AnyKeyPath, // expected-error {{type 'AnyKeyPath' in '@abi' should match 'any AnyKeyPath & CustomStringConvertible'}} - _: AnyKeyPath & CustomStringConvertible, // expected-error {{type 'any AnyKeyPath & CustomStringConvertible' in '@abi' should match 'AnyKeyPath'}} - _: Any, // expected-error {{type 'Any' in '@abi' should match 'any CustomDebugStringConvertible & CustomStringConvertible'}} - _: CustomStringConvertible & CustomDebugStringConvertible // expected-error {{type 'any CustomDebugStringConvertible & CustomStringConvertible' in '@abi' should match 'Any'}} + _ a: Any, // expected-error {{parameter 'a' type 'Any' in '@abi' should match 'any CustomStringConvertible'}} + _ b: CustomStringConvertible, // expected-error {{parameter 'b' type 'any CustomStringConvertible' in '@abi' should match 'Any'}} + _ c: AnyKeyPath, // expected-error {{parameter 'c' type 'AnyKeyPath' in '@abi' should match 'any AnyKeyPath & CustomStringConvertible'}} + _ d: AnyKeyPath & CustomStringConvertible, // expected-error {{parameter 'd' type 'any AnyKeyPath & CustomStringConvertible' in '@abi' should match 'AnyKeyPath'}} + _ e: Any, // expected-error {{parameter 'e' type 'Any' in '@abi' should match 'any CustomDebugStringConvertible & CustomStringConvertible'}} + _ f: CustomStringConvertible & CustomDebugStringConvertible // expected-error {{parameter 'f' type 'any CustomDebugStringConvertible & CustomStringConvertible' in '@abi' should match 'Any'}} ) ) func testNormalProtocols( @@ -982,7 +982,7 @@ extension ErsatzResult where Failure == Swift.Error { // FIXME: The where clause makes this ABI-compatible, but we can't tell that. @abi( init( - catching body: () throws -> Success // expected-error {{type '() throws -> Success' in '@abi' should match '() throws(Failure) -> Success'}} + catching body: () throws -> Success // expected-error {{parameter 'body' type '() throws -> Success' in '@abi' should match '() throws(Failure) -> Success'}} ) ) init( @@ -2136,13 +2136,13 @@ struct SelfParamOwnership { @abi(borrowing func fn20()) func fn20() {} - @abi(consuming func fn30()) // expected-error {{instance method modifier 'consuming' in '@abi' is not compatible with default}} {{none}} + @abi(consuming func fn30()) // expected-error {{modifier 'consuming' on self parameter in '@abi' is not compatible with default}} {{none}} func fn30() {} // expected-note {{should match modifier here}} - @abi(__consuming func fn40()) // expected-error {{instance method modifier '__consuming' in '@abi' is not compatible with default}} {{none}} + @abi(__consuming func fn40()) // expected-error {{modifier '__consuming' on self parameter in '@abi' is not compatible with default}} {{none}} func fn40() {} // expected-note {{should match modifier here}} - @abi(mutating func fn50()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with default}} {{none}} + @abi(mutating func fn50()) // expected-error {{modifier 'mutating' on self parameter in '@abi' is not compatible with default}} {{none}} func fn50() {} // expected-note {{should match modifier here}} @abi(func fn01()) @@ -2154,13 +2154,13 @@ struct SelfParamOwnership { @abi(borrowing func fn21()) nonmutating func fn21() {} - @abi(consuming func fn31()) // expected-error {{instance method modifier 'consuming' in '@abi' is not compatible with default}} {{none}} + @abi(consuming func fn31()) // expected-error {{modifier 'consuming' on self parameter in '@abi' is not compatible with default}} {{none}} nonmutating func fn31() {} // expected-note {{should match modifier here}} - @abi(__consuming func fn41()) // expected-error {{instance method modifier '__consuming' in '@abi' is not compatible with default}} {{none}} + @abi(__consuming func fn41()) // expected-error {{modifier '__consuming' on self parameter in '@abi' is not compatible with default}} {{none}} nonmutating func fn41() {} // expected-note {{should match modifier here}} - @abi(mutating func fn51()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with default}} {{none}} + @abi(mutating func fn51()) // expected-error {{modifier 'mutating' on self parameter in '@abi' is not compatible with default}} {{none}} nonmutating func fn51() {} // expected-note {{should match modifier here}} @abi(func fn02()) @@ -2172,22 +2172,22 @@ struct SelfParamOwnership { @abi(borrowing func fn22()) borrowing func fn22() {} - @abi(consuming func fn32()) // expected-error {{instance method modifier 'consuming' in '@abi' is not compatible with 'borrowing'}} {{none}} + @abi(consuming func fn32()) // expected-error {{modifier 'consuming' on self parameter in '@abi' is not compatible with 'borrowing'}} {{none}} borrowing func fn32() {} // expected-note {{should match modifier here}} - @abi(__consuming func fn42()) // expected-error {{instance method modifier '__consuming' in '@abi' is not compatible with 'borrowing'}} {{none}} + @abi(__consuming func fn42()) // expected-error {{modifier '__consuming' on self parameter in '@abi' is not compatible with 'borrowing'}} {{none}} borrowing func fn42() {} // expected-note {{should match modifier here}} - @abi(mutating func fn52()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with 'borrowing'}} {{none}} + @abi(mutating func fn52()) // expected-error {{modifier 'mutating' on self parameter in '@abi' is not compatible with 'borrowing'}} {{none}} borrowing func fn52() {} // expected-note {{should match modifier here}} - @abi(func fn03()) // expected-error {{default instance method modifier in '@abi' is not compatible with 'consuming'}} {{none}} + @abi(func fn03()) // expected-error {{default modifier on self parameter in '@abi' is not compatible with 'consuming'}} {{none}} consuming func fn03() {} // expected-note {{should match modifier here}} - @abi(nonmutating func fn13()) // expected-error {{default instance method modifier in '@abi' is not compatible with 'consuming'}} {{none}} + @abi(nonmutating func fn13()) // expected-error {{default modifier on self parameter in '@abi' is not compatible with 'consuming'}} {{none}} consuming func fn13() {} // expected-note {{should match modifier here}} - @abi(borrowing func fn23()) // expected-error {{instance method modifier 'borrowing' in '@abi' is not compatible with 'consuming'}} {{none}} + @abi(borrowing func fn23()) // expected-error {{modifier 'borrowing' on self parameter in '@abi' is not compatible with 'consuming'}} {{none}} consuming func fn23() {} // expected-note {{should match modifier here}} @abi(consuming func fn33()) @@ -2196,16 +2196,16 @@ struct SelfParamOwnership { @abi(__consuming func fn43()) consuming func fn43() {} - @abi(mutating func fn53()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with 'consuming'}} {{none}} + @abi(mutating func fn53()) // expected-error {{modifier 'mutating' on self parameter in '@abi' is not compatible with 'consuming'}} {{none}} consuming func fn53() {} // expected-note {{should match modifier here}} - @abi(func fn04()) // expected-error {{default instance method modifier in '@abi' is not compatible with '__consuming'}} {{none}} + @abi(func fn04()) // expected-error {{default modifier on self parameter in '@abi' is not compatible with '__consuming'}} {{none}} __consuming func fn04() {} // expected-note {{should match modifier here}} - @abi(nonmutating func fn14()) // expected-error {{default instance method modifier in '@abi' is not compatible with '__consuming'}} {{none}} + @abi(nonmutating func fn14()) // expected-error {{default modifier on self parameter in '@abi' is not compatible with '__consuming'}} {{none}} __consuming func fn14() {} // expected-note {{should match modifier here}} - @abi(borrowing func fn24()) // expected-error {{instance method modifier 'borrowing' in '@abi' is not compatible with '__consuming'}} {{none}} + @abi(borrowing func fn24()) // expected-error {{modifier 'borrowing' on self parameter in '@abi' is not compatible with '__consuming'}} {{none}} __consuming func fn24() {} // expected-note {{should match modifier here}} @abi(consuming func fn34()) @@ -2214,22 +2214,22 @@ struct SelfParamOwnership { @abi(__consuming func fn44()) __consuming func fn44() {} - @abi(mutating func fn54()) // expected-error {{instance method modifier 'mutating' in '@abi' is not compatible with '__consuming'}} {{none}} + @abi(mutating func fn54()) // expected-error {{modifier 'mutating' on self parameter in '@abi' is not compatible with '__consuming'}} {{none}} __consuming func fn54() {} // expected-note {{should match modifier here}} - @abi(func fn05()) // expected-error {{default instance method modifier in '@abi' is not compatible with 'mutating'}} {{none}} + @abi(func fn05()) // expected-error {{default modifier on self parameter in '@abi' is not compatible with 'mutating'}} {{none}} mutating func fn05() {} // expected-note {{should match modifier here}} - @abi(nonmutating func fn15()) // expected-error {{default instance method modifier in '@abi' is not compatible with 'mutating'}} {{none}} + @abi(nonmutating func fn15()) // expected-error {{default modifier on self parameter in '@abi' is not compatible with 'mutating'}} {{none}} mutating func fn15() {} // expected-note {{should match modifier here}} - @abi(borrowing func fn25()) // expected-error {{instance method modifier 'borrowing' in '@abi' is not compatible with 'mutating'}} {{none}} + @abi(borrowing func fn25()) // expected-error {{modifier 'borrowing' on self parameter in '@abi' is not compatible with 'mutating'}} {{none}} mutating func fn25() {} // expected-note {{should match modifier here}} - @abi(consuming func fn35()) // expected-error {{instance method modifier 'consuming' in '@abi' is not compatible with 'mutating'}} {{none}} + @abi(consuming func fn35()) // expected-error {{modifier 'consuming' on self parameter in '@abi' is not compatible with 'mutating'}} {{none}} mutating func fn35() {} // expected-note {{should match modifier here}} - @abi(__consuming func fn45()) // expected-error {{instance method modifier '__consuming' in '@abi' is not compatible with 'mutating'}} {{none}} + @abi(__consuming func fn45()) // expected-error {{modifier '__consuming' on self parameter in '@abi' is not compatible with 'mutating'}} {{none}} mutating func fn45() {} // expected-note {{should match modifier here}} @abi(mutating func fn55()) @@ -2241,10 +2241,10 @@ struct AddressableSelf { @abi(@_addressableSelf func fn1()) @_addressableSelf func fn1() {} - @abi(@_addressableSelf func fn2()) // expected-error {{instance method attribute '_addressableSelf' in '@abi' is not compatible with default}} {{none}} + @abi(@_addressableSelf func fn2()) // expected-error {{attribute '_addressableSelf' on self parameter in '@abi' is not compatible with default}} {{none}} func fn2() {} // expected-note {{should match attribute here}} - @abi(func fn3()) // expected-error {{default instance method attribute in '@abi' is not compatible with '_addressableSelf'}} {{none}} + @abi(func fn3()) // expected-error {{default attribute on self parameter in '@abi' is not compatible with '_addressableSelf'}} {{none}} @_addressableSelf func fn3() {} // expected-note {{should match attribute here}} } From 446dc9d609eb6c3a705029ea995f1a5e49aebe44 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Fri, 14 Mar 2025 18:54:33 -0700 Subject: [PATCH 19/22] Diagnose mismatched typed throws in `@abi` --- lib/Sema/TypeCheckAttrABI.cpp | 17 ++++++++++++++--- test/attr/attr_abi.swift | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/Sema/TypeCheckAttrABI.cpp b/lib/Sema/TypeCheckAttrABI.cpp index cf7b4be01c49e..35a25736fe154 100644 --- a/lib/Sema/TypeCheckAttrABI.cpp +++ b/lib/Sema/TypeCheckAttrABI.cpp @@ -453,14 +453,25 @@ class ABIDeclChecker : public ASTComparisonVisitor { bool checkEffects(DeclEffects api, DeclEffects abi, Decl *apiDecl, Decl *abiDecl) { bool didDiagnose = false; + // 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. - if (api.anyContains(EffectKind::Throws) != abi.anyContains(EffectKind::Throws)) { + bool apiThrows = api.anyContains(EffectKind::Throws); + bool abiThrows = abi.anyContains(EffectKind::Throws); + + if (apiThrows != abiThrows) { diagnoseAndRemoveAttr(abiAttr, diag::attr_abi_mismatched_throws, - apiDecl, /*abiCanThrow=*/abi.anyContains(EffectKind::Throws)); + apiDecl, /*abiCanThrow=*/abiThrows); didDiagnose = true; - // FIXME: Typed throws? + } else if (apiThrows && abiThrows) { + // If both throw, make sure the throw types are compatible. + auto apiThrowType = api.effectiveThrownType.value_or(ctx.getNeverType()); + auto abiThrowType = abi.effectiveThrownType.value_or(ctx.getNeverType()); + + didDiagnose |= checkType(apiThrowType, abiThrowType, + api.throwsLoc, abi.throwsLoc, + TypeOrigin::forThrowsEffect()); } // Do the declarations match in async-ness? diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index 41288141ec637..1b7b5f351136e 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -164,6 +164,8 @@ struct SubscriptArity { // Throws effect checking // +enum MyError: Error {} + @abi(func throws00(_: () throws -> Void)) func throws00(_: () throws -> Void) {} @@ -173,6 +175,9 @@ 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 throws30(_: () throws -> Void) throws(MyError)) // expected-error {{cannot give 'throws30' the ABI of a global function which can throw}} +func throws30(_: () 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 {} @@ -182,6 +187,9 @@ func throws11(_: () throws -> Void) throws {} @abi(func throws21(_: () throws -> Void) rethrows) func throws21(_: () throws -> Void) throws {} +@abi(func throws31(_: () throws -> Void) throws(MyError)) // expected-error {{thrown type 'MyError' in '@abi' should match 'any Error'}} +func throws31(_: () throws -> Void) throws {} // expected-note@:37 {{should match type here}} + @abi(func throws02(_: () throws -> Void)) // expected-error {{cannot give 'throws02' the ABI of a global function which cannot throw}} func throws02(_: () throws -> Void) rethrows {} @@ -191,6 +199,21 @@ func throws12(_: () throws -> Void) rethrows {} @abi(func throws22(_: () throws -> Void) rethrows) func throws22(_: () throws -> Void) rethrows {} +@abi(func throws32(_: () throws -> Void) throws(MyError)) // expected-error {{thrown type 'MyError' in '@abi' should match 'any Error'}} +func throws32(_: () throws -> Void) rethrows {} // expected-note@:37 {{should match type here}} + +@abi(func throws03(_: () throws -> Void)) // expected-error {{cannot give 'throws03' the ABI of a global function which cannot throw}} +func throws03(_: () throws -> Void) throws(MyError) {} + +@abi(func throws13(_: () throws -> Void) throws) // expected-error {{thrown type 'any Error' in '@abi' should match 'MyError'}} +func throws13(_: () throws -> Void) throws(MyError) {} // expected-note@:37 {{should match type here}} + +@abi(func throws23(_: () throws -> Void) rethrows) // expected-error {{thrown type 'any Error' in '@abi' should match 'MyError'}} +func throws23(_: () throws -> Void) throws(MyError) {} // expected-note@:37 {{should match type here}} + +@abi(func throws33(_: () throws -> Void) throws(MyError)) +func throws33(_: () throws -> Void) throws(MyError) {} + @abi(var throws00Var: Int) var throws00Var: Int { get { fatalError() } } From 715911142df3c5199271226fee8d07a45c5b8432 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Fri, 14 Mar 2025 19:03:28 -0700 Subject: [PATCH 20/22] Improve diagnosis of generic types in `@abi` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A same-type constraint in an enclosing `where` clause will eliminate a generic parameter’s ABI impact. Teach `ABIDeclChecker::checkType()` about this so it can handle a known-unsupported case for `Swift.Result.init(catching:)`. --- lib/Sema/TypeCheckAttrABI.cpp | 25 +++++++++++++----- test/attr/attr_abi.swift | 48 +++++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 20 deletions(-) diff --git a/lib/Sema/TypeCheckAttrABI.cpp b/lib/Sema/TypeCheckAttrABI.cpp index 35a25736fe154..d81c018ed7faa 100644 --- a/lib/Sema/TypeCheckAttrABI.cpp +++ b/lib/Sema/TypeCheckAttrABI.cpp @@ -222,6 +222,13 @@ SourceLoc getTypeLoc(AbstractStorageDecl *storage, Decl *owner = nullptr) { return loc; } +/// Get a decl's generic signature, if it has one. +GenericSignature getGenericSignature(Decl *decl) { + if (auto genericCtx = decl->getAsGenericContext()) + return genericCtx->getGenericSignature(); + return GenericSignature(); +} + class ABIDeclChecker : public ASTComparisonVisitor { ASTContext &ctx; Decl *diagnoseOnDecl; @@ -371,6 +378,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { didDiagnose |= checkType(apiNorm.getPlainType(), abiNorm.getPlainType(), apiTypeLoc, abiTypeLoc, + getGenericSignature(apiDecl), + getGenericSignature(abiDecl), TypeOrigin::forParameter(abi)); didDiagnose |= checkParameterFlags(apiNorm.getParameterFlags(), @@ -471,6 +480,8 @@ class ABIDeclChecker : public ASTComparisonVisitor { didDiagnose |= checkType(apiThrowType, abiThrowType, api.throwsLoc, abi.throwsLoc, + getGenericSignature(apiDecl), + getGenericSignature(abiDecl), TypeOrigin::forThrowsEffect()); } @@ -621,10 +632,9 @@ class ABIDeclChecker : public ASTComparisonVisitor { bool visitDecl(Decl *api, Decl *abi) { bool didDiagnose = checkAttrs(api->getAttrs(), abi->getAttrs(), api, abi); - if (auto apiGenericCtx = api->getAsGenericContext()) { - auto abiGenericCtx = abi->getAsGenericContext(); - didDiagnose |= checkGenericSignature(apiGenericCtx->getGenericSignature(), - abiGenericCtx->getGenericSignature(), + if (api->getAsGenericContext()) { + didDiagnose |= checkGenericSignature(getGenericSignature(api), + getGenericSignature(abi), api, abi); } @@ -706,6 +716,7 @@ class ABIDeclChecker : public ASTComparisonVisitor { abi->getResultInterfaceType(), api->getResultTypeSourceRange().Start, abi->getResultTypeSourceRange().Start, + getGenericSignature(api), getGenericSignature(abi), TypeOrigin::forResult()); } @@ -723,6 +734,7 @@ class ABIDeclChecker : public ASTComparisonVisitor { if (checkType(api->getValueInterfaceType(), abi->getValueInterfaceType(), getTypeLoc(api), getTypeLoc(abi), + getGenericSignature(api), getGenericSignature(abi), TypeOrigin::forUnspecified())) return true; @@ -934,10 +946,11 @@ class ABIDeclChecker : public ASTComparisonVisitor { // MARK: @abi checking - types bool checkType(Type api, Type abi, SourceLoc apiLoc, SourceLoc abiLoc, + GenericSignature apiSig, GenericSignature abiSig, TypeOrigin origin) { if (!api.isNull() && !abi.isNull()) { - Type apiNorm = normalizeType(api); - Type abiNorm = normalizeType(abi); + Type apiNorm = normalizeType(api->getReducedType(apiSig)); + Type abiNorm = normalizeType(abi->getReducedType(abiSig)); if (apiNorm->isEqual(abiNorm)) { return false; } diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index 1b7b5f351136e..2496713887972 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -220,6 +220,40 @@ var throws00Var: Int { get { fatalError() } } @abi(var throws11Var: Int) var throws11Var: Int { get throws { fatalError() } } +enum ErsatzResult {} + +extension ErsatzResult where Failure == Swift.Error { + // The `where` clause makes `throws(Failure)` equivalent to `throws`. + + // Similar to Swift.Result.init(__untyped_throws_catching:) + @abi( + init( + catching body: () throws -> Success + ) + ) + init( + __untyped_throws_catching body: () throws(Failure) -> Success + ) {} + + @abi(func get() throws -> Success) + func __untyped_throws_get() throws(Failure) -> Success { fatalError() } +} + +extension ErsatzResult { + // Should not be allowed, as `Failure` is still generic + @abi( + init( + unconstrainedCatching body: () throws -> Success // expected-error {{parameter 'body' type '() throws -> Success' in '@abi' should match '() throws(Failure) -> Success'}} + ) + ) + init( + __untyped_throws_catching_bad body: () throws(Failure) -> Success // expected-note {{should match type here}} + ) {} + + @abi(func unconstrainedGet() throws -> Success) // expected-error @:32 {{thrown type 'any Error' in '@abi' should match 'Failure'}} + func __untyped_throws_get_bad() throws(Failure) -> Success { fatalError() } // expected-note {{should match type here}} +} + // // Async effect checking // @@ -999,20 +1033,6 @@ func testNormalProtocolsGeneric( // expected-note _: A, _: B ) {} -enum ErsatzResult {} -extension ErsatzResult where Failure == Swift.Error { - // Similar to Swift.Result.init(__untyped_throws_catching:) - // FIXME: The where clause makes this ABI-compatible, but we can't tell that. - @abi( - init( - catching body: () throws -> Success // expected-error {{parameter 'body' type '() throws -> Success' in '@abi' should match '() throws(Failure) -> Success'}} - ) - ) - init( - __untyped_throws_catching body: () throws(Failure) -> Success // expected-note {{should match type here}} - ) {} -} - // // Static/Instance and interactions with `final` // From d5fac9f2ed83874358fd8eda32a02f1f57f8413e Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Mon, 24 Mar 2025 16:31:12 -0700 Subject: [PATCH 21/22] Tweak `@abi` `@lifetime` test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove a diagnostic that isn’t occurring anymore. --- test/attr/attr_abi.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index 2496713887972..1409d75bf0fdf 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -1997,9 +1997,9 @@ struct Lifetime: ~Escapable { @lifetime(borrow i1) init(i1: UnsafeRawPointer) {} @abi(@lifetime(borrow i2) init(i2: UnsafeRawPointer)) // expected-error {{extra 'lifetime' attribute in '@abi'}} {{8-28=}} - init(i2: UnsafeRawPointer) {} // expected-error {{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} + init(i2: UnsafeRawPointer) {} - @abi(init(i3: UnsafeRawPointer)) // expected-error {{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} expected-error {{missing 'lifetime' attribute in '@abi'}} {{8-8=@lifetime(borrow i3) }} + @abi(init(i3: UnsafeRawPointer)) // expected-error {{missing 'lifetime' attribute in '@abi'}} {{8-8=@lifetime(borrow i3) }} @lifetime(borrow i3) init(i3: UnsafeRawPointer) {} // expected-note {{should match attribute here}} @abi(@lifetime(borrow i4) init(i4: UnsafeRawPointer, i4a: UnsafeRawPointer)) // expected-error {{'lifetime' attribute in '@abi' should match '@lifetime(borrow i4a)'}} {{8-28=@lifetime(borrow i4a)}} @@ -2013,9 +2013,9 @@ struct UnsafeNonescapableResult: ~Escapable { @_unsafeNonescapableResult init(i1: UnsafeRawPointer) {} @abi(@_unsafeNonescapableResult init(i2: UnsafeRawPointer)) // expected-error {{extra '_unsafeNonescapableResult' attribute in '@abi'}} {{8-34=}} - init(i2: UnsafeRawPointer) {} // expected-error {{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} + init(i2: UnsafeRawPointer) {} - @abi(init(i3: UnsafeRawPointer)) // expected-error {{cannot infer lifetime dependence, no parameters found that are either ~Escapable or Escapable with a borrowing ownership}} expected-error {{missing '_unsafeNonescapableResult' attribute in '@abi'}} {{8-8=@_unsafeNonescapableResult }} + @abi(init(i3: UnsafeRawPointer)) // expected-error {{missing '_unsafeNonescapableResult' attribute in '@abi'}} {{8-8=@_unsafeNonescapableResult }} @_unsafeNonescapableResult init(i3: UnsafeRawPointer) {} // expected-note {{should match attribute here}} } From 6759ad59d1f78ae29fcc7fdcf85cfeda50c583e4 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 25 Mar 2025 15:24:18 -0700 Subject: [PATCH 22/22] Add `@abi` behavior for `@constInitialized` --- include/swift/AST/DeclAttr.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index edfafd2630b93..654d9cf221688 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -875,7 +875,7 @@ SIMPLE_DECL_ATTR(const, ConstVal, DECL_ATTR_FEATURE_REQUIREMENT(ConstVal, CompileTimeValues) SIMPLE_DECL_ATTR(constInitialized, ConstInitialized, OnVar, - ABIStableToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove, + ABIStableToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | EquivalentInABIAttr, 168) DECL_ATTR_FEATURE_REQUIREMENT(ConstInitialized, CompileTimeValues)