diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index bbe320c28a3..58566db60b2 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -24,6 +24,7 @@ #include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/VersionTuple.h" +#include "llvm/ADT/PointerEmbeddedInt.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 2416ed6ac04..a4a88d0fc59 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -195,6 +195,9 @@ class VariadicEnumArgument values, list Enums = enums; } +// Represents an attribute wrapped by another attribute. +class AttrArgument : Argument; + // This handles one spelling of an attribute. class Spelling { string Name = name; @@ -1500,6 +1503,29 @@ def SwiftImportPropertyAsAccessors : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftVersioned : Attr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">]; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + +def SwiftVersionedRemoval : Attr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">]; + let SemaHandler = 0; + let Documentation = [Undocumented]; + let AdditionalMembers = [{ + attr::Kind getAttrKindToRemove() const { + return static_cast(getRawKind()); + } + }]; +} + def ReqdWorkGroupSize : InheritableAttr { let Spellings = [GNU<"reqd_work_group_size">]; let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">, diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 6173661d4ec..078445e48ad 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -17,6 +17,21 @@ using namespace clang; using clang::api_notes::VersionedInfoRole; +namespace { + struct VersionedInfoMetadata { + VersionTuple Version; + VersionedInfoRole Role; + + /*implicit*/ VersionedInfoMetadata(VersionedInfoRole role) : Role(role) { + assert(role != VersionedInfoRole::Versioned && + "explicit version required"); + } + + /*implicit*/ VersionedInfoMetadata(VersionTuple version) + : Version(version), Role(VersionedInfoRole::Versioned) {} + }; +} // end anonymous namespace + /// Determine whether this is a multi-level pointer type. static bool isMultiLevelPointerType(QualType type) { QualType pointee = type->getPointeeType(); @@ -29,9 +44,9 @@ static bool isMultiLevelPointerType(QualType type) { // Apply nullability to the given declaration. static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { bool overrideExisting; - switch (role) { + switch (metadata.Role) { case VersionedInfoRole::AugmentSource: overrideExisting = false; break; @@ -119,20 +134,27 @@ static StringRef CopyString(ASTContext &ctx, StringRef string) { } namespace { + template + struct AttrKindFor {}; + +#define ATTR(X) \ + template <> struct AttrKindFor { \ + static const attr::Kind value = attr::X; \ + }; +#include "clang/Basic/AttrList.inc" + /// Handle an attribute introduced by API notes. /// /// \param shouldAddAttribute Whether we should add a new attribute /// (otherwise, we might remove an existing attribute). /// \param createAttr Create the new attribute to be added. - /// \param getExistingAttr Get an existing, matching attribute on the given - /// declaration. template void handleAPINotedAttribute( Sema &S, Decl *D, bool shouldAddAttribute, - VersionedInfoRole role, + VersionedInfoMetadata metadata, llvm::function_ref createAttr, - llvm::function_ref(Decl *)> getExistingAttr) { - switch (role) { + llvm::function_ref(Decl*)> getExistingAttr) { + switch (metadata.Role) { case VersionedInfoRole::AugmentSource: // If we're not adding an attribute, there's nothing to do. if (!shouldAddAttribute) return; @@ -149,8 +171,14 @@ namespace { auto end = D->specific_attr_end(); auto existing = getExistingAttr(D); if (existing != end) { - // Remove the existing attribute. + // Remove the existing attribute, and treat it as a superseded + // non-versioned attribute. + auto *versioned = + SwiftVersionedAttr::CreateImplicit(S.Context, clang::VersionTuple(), + *existing); + D->getAttrs().erase(existing.getCurrent()); + D->addAttr(versioned); } // If we're supposed to add a new attribute, do so. @@ -163,7 +191,24 @@ namespace { } case VersionedInfoRole::Versioned: - // FIXME: Retain versioned attributes separately. + // FIXME: Include the actual version instead of making one up. + if (shouldAddAttribute) { + if (auto attr = createAttr()) { + auto *versioned = + SwiftVersionedAttr::CreateImplicit(S.Context, metadata.Version, + attr); + D->addAttr(versioned); + } + } else { + // FIXME: This isn't preserving enough information for things like + // availability, where we're trying to remove a /specific/ kind of + // attribute. + auto *versioned = + SwiftVersionedRemovalAttr::CreateImplicit(S.Context, + metadata.Version, + AttrKindFor::value); + D->addAttr(versioned); + } break; } } @@ -171,9 +216,9 @@ namespace { template void handleAPINotedAttribute( Sema &S, Decl *D, bool shouldAddAttribute, - VersionedInfoRole role, + VersionedInfoMetadata metadata, llvm::function_ref createAttr) { - handleAPINotedAttribute(S, D, shouldAddAttribute, role, createAttr, + handleAPINotedAttribute(S, D, shouldAddAttribute, metadata, createAttr, [](Decl *decl) { return decl->specific_attr_begin(); }); @@ -182,10 +227,10 @@ namespace { static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonEntityInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Availability if (info.Unavailable) { - handleAPINotedAttribute(S, D, true, role, + handleAPINotedAttribute(S, D, true, metadata, [&] { return UnavailableAttr::CreateImplicit(S.Context, CopyString(S.Context, @@ -194,7 +239,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, } if (info.UnavailableInSwift) { - handleAPINotedAttribute(S, D, true, role, [&] { + handleAPINotedAttribute(S, D, true, metadata, [&] { return AvailabilityAttr::CreateImplicit( S.Context, &S.Context.Idents.get("swift"), @@ -224,14 +269,15 @@ static void ProcessAPINotes(Sema &S, Decl *D, // swift_private if (auto swiftPrivate = info.isSwiftPrivate()) { - handleAPINotedAttribute(S, D, *swiftPrivate, role, [&] { + handleAPINotedAttribute(S, D, *swiftPrivate, metadata, + [&] { return SwiftPrivateAttr::CreateImplicit(S.Context); }); } // swift_name if (!info.SwiftName.empty()) { - handleAPINotedAttribute(S, D, true, role, + handleAPINotedAttribute(S, D, true, metadata, [&]() -> SwiftNameAttr * { auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note"); @@ -249,11 +295,11 @@ static void ProcessAPINotes(Sema &S, Decl *D, static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonTypeInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // swift_bridge if (auto swiftBridge = info.getSwiftBridge()) { - handleAPINotedAttribute(S, D, !swiftBridge->empty(), role, - [&] { + handleAPINotedAttribute(S, D, !swiftBridge->empty(), + metadata, [&] { return SwiftBridgeAttr::CreateImplicit(S.Context, CopyString(S.Context, *swiftBridge)); @@ -263,7 +309,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, // ns_error_domain if (auto nsErrorDomain = info.getNSErrorDomain()) { handleAPINotedAttribute(S, D, !nsErrorDomain->empty(), - role, [&] { + metadata, [&] { return NSErrorDomainAttr::CreateImplicit( S.Context, &S.Context.Idents.get(*nsErrorDomain)); @@ -271,7 +317,7 @@ static void ProcessAPINotes(Sema &S, Decl *D, } ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Check that the replacement type provided by API notes is reasonable. @@ -293,9 +339,9 @@ static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc, /// Process API notes for a variable or property. static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::VariableInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Type override. - if (role != VersionedInfoRole::Versioned && + if (metadata.Role != VersionedInfoRole::Versioned && !info.getType().empty() && S.ParseTypeFromStringCallback) { auto parsedType = S.ParseTypeFromStringCallback(info.getType(), "", @@ -332,50 +378,50 @@ static void ProcessAPINotes(Sema &S, Decl *D, // Nullability. if (auto Nullability = info.getNullability()) { - applyNullability(S, D, *Nullability, role); + applyNullability(S, D, *Nullability, metadata); } // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Process API notes for a parameter. static void ProcessAPINotes(Sema &S, ParmVarDecl *D, const api_notes::ParamInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // noescape if (auto noescape = info.isNoEscape()) { - handleAPINotedAttribute(S, D, *noescape, role, [&] { + handleAPINotedAttribute(S, D, *noescape, metadata, [&] { return NoEscapeAttr::CreateImplicit(S.Context); }); } // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Process API notes for a global variable. static void ProcessAPINotes(Sema &S, VarDecl *D, const api_notes::GlobalVariableInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Process API notes for an Objective-C property. static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, const api_notes::ObjCPropertyInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); if (auto asAccessors = info.getSwiftImportAsAccessors()) { handleAPINotedAttribute(S, D, *asAccessors, - role, [&] { + metadata, [&] { return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context); }); } @@ -388,7 +434,7 @@ namespace { /// Process API notes for a function or method. static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, const api_notes::FunctionInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Find the declaration itself. FunctionDecl *FD = AnyFunc.dyn_cast(); Decl *D = FD; @@ -400,7 +446,7 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, // Nullability of return type. if (info.NullabilityAudited) { - applyNullability(S, D, info.getReturnTypeInfo(), role); + applyNullability(S, D, info.getReturnTypeInfo(), metadata); } // Parameters. @@ -421,12 +467,12 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, QualType paramTypeBefore = Param->getType(); if (I < info.Params.size()) { - ProcessAPINotes(S, Param, info.Params[I], role); + ProcessAPINotes(S, Param, info.Params[I], metadata); } // Nullability. if (info.NullabilityAudited) - applyNullability(S, Param, info.getParamTypeInfo(I), role); + applyNullability(S, Param, info.getParamTypeInfo(I), metadata); if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr()) anyTypeChanged = true; @@ -434,8 +480,8 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, // Result type override. QualType overriddenResultType; - if (role != VersionedInfoRole::Versioned && !info.ResultType.empty() && - S.ParseTypeFromStringCallback) { + if (metadata.Role != VersionedInfoRole::Versioned && + !info.ResultType.empty() && S.ParseTypeFromStringCallback) { auto parsedType = S.ParseTypeFromStringCallback(info.ResultType, "", D->getLocation()); @@ -483,37 +529,38 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, // Handle common entity information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Process API notes for a global function. static void ProcessAPINotes(Sema &S, FunctionDecl *D, const api_notes::GlobalFunctionInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), - static_cast(info), role); + static_cast(info), metadata); } /// Process API notes for an enumerator. static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, const api_notes::EnumConstantInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Process API notes for an Objective-C method. static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, const api_notes::ObjCMethodInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Designated initializers. if (info.DesignatedInit) { - handleAPINotedAttribute(S, D, true, role, [&] { + handleAPINotedAttribute(S, D, true, metadata, + [&] { if (ObjCInterfaceDecl *IFace = D->getClassInterface()) { IFace->setHasDesignatedInitializers(); } @@ -522,7 +569,7 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, } // FIXME: This doesn't work well with versioned API notes. - if (role == VersionedInfoRole::AugmentSource && + if (metadata.Role == VersionedInfoRole::AugmentSource && info.getFactoryAsInitKind() == api_notes::FactoryAsInitKind::AsClassMethod && !D->getAttr()) { @@ -531,28 +578,28 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), - static_cast(info), role); + static_cast(info), metadata); } /// Process API notes for a tag. static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common type information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Process API notes for a typedef. static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, const api_notes::TypedefInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // swift_wrapper using SwiftWrapperKind = api_notes::SwiftWrapperKind; if (auto swiftWrapper = info.SwiftWrapper) { handleAPINotedAttribute(S, D, - *swiftWrapper != SwiftWrapperKind::None, role, + *swiftWrapper != SwiftWrapperKind::None, metadata, [&] { SwiftNewtypeAttr::NewtypeKind kind; switch (*swiftWrapper) { @@ -576,25 +623,47 @@ static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, // Handle common type information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Process API notes for an Objective-C class or protocol. static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, const api_notes::ObjCContextInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle common type information. ProcessAPINotes(S, D, static_cast(info), - role); + metadata); } /// Process API notes for an Objective-C class. static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, const api_notes::ObjCContextInfo &info, - VersionedInfoRole role) { + VersionedInfoMetadata metadata) { // Handle information common to Objective-C classes and protocols. - ProcessAPINotes(S, static_cast(D), info, role); + ProcessAPINotes(S, static_cast(D), info, + metadata); +} + +/// Processes all versions of versioned API notes. +/// +/// Just dispatches to the various ProcessAPINotes functions in this file. +template +static void ProcessVersionedAPINotes( + Sema &S, SpecificDecl *D, + const api_notes::APINotesReader::VersionedInfo Info) { + unsigned Selected = Info.getSelected().getValueOr(Info.size()); + + VersionTuple Version; + SpecificInfo InfoSlice; + for (unsigned i = 0, e = Info.size(); i != e; ++i) { + std::tie(Version, InfoSlice) = Info[i]; + if (i != Selected) { + ProcessAPINotes(S, D, InfoSlice, Version); + } else { + ProcessAPINotes(S, D, InfoSlice, Info.getSelectedRole()); + } + } } /// Process API notes that are associated with this declaration, mapping them @@ -608,9 +677,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Global variables. if (auto VD = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupGlobalVariable(VD->getName())) { - ::ProcessAPINotes(*this, VD, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupGlobalVariable(VD->getName()); + ProcessVersionedAPINotes(*this, VD, Info); } return; @@ -620,9 +688,8 @@ void Sema::ProcessAPINotes(Decl *D) { if (auto FD = dyn_cast(D)) { if (FD->getDeclName().isIdentifier()) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupGlobalFunction(FD->getName())) { - ::ProcessAPINotes(*this, FD, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupGlobalFunction(FD->getName()); + ProcessVersionedAPINotes(*this, FD, Info); } } @@ -632,9 +699,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C classes. if (auto Class = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCClassInfo(Class->getName())) { - ::ProcessAPINotes(*this, Class, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupObjCClassInfo(Class->getName()); + ProcessVersionedAPINotes(*this, Class, Info); } return; @@ -643,9 +709,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Objective-C protocols. if (auto Protocol = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName())) { - ::ProcessAPINotes(*this, Protocol, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName()); + ProcessVersionedAPINotes(*this, Protocol, Info); } return; @@ -654,9 +719,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Tags if (auto Tag = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupTag(Tag->getName())) { - ::ProcessAPINotes(*this, Tag, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupTag(Tag->getName()); + ProcessVersionedAPINotes(*this, Tag, Info); } return; @@ -665,9 +729,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Typedefs if (auto Typedef = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupTypedef(Typedef->getName())) { - ::ProcessAPINotes(*this, Typedef, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupTypedef(Typedef->getName()); + ProcessVersionedAPINotes(*this, Typedef, Info); } return; @@ -680,9 +743,8 @@ void Sema::ProcessAPINotes(Decl *D) { if (D->getDeclContext()->getRedeclContext()->isFileContext()) { if (auto EnumConstant = dyn_cast(D)) { for (auto Reader : APINotes.findAPINotes(D->getLocation())) { - if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) { - ::ProcessAPINotes(*this, EnumConstant, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupEnumConstant(EnumConstant->getName()); + ProcessVersionedAPINotes(*this, EnumConstant, Info); } return; @@ -750,10 +812,9 @@ void Sema::ProcessAPINotes(Decl *D) { SelectorRef.NumPieces = Sel.getNumArgs(); SelectorRef.Identifiers = SelPieces; - if (auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, - Method->isInstanceMethod())){ - ::ProcessAPINotes(*this, Method, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, + Method->isInstanceMethod()); + ProcessVersionedAPINotes(*this, Method, Info); } } } @@ -765,11 +826,9 @@ void Sema::ProcessAPINotes(Decl *D) { bool isInstanceProperty = (Property->getPropertyAttributesAsWritten() & ObjCPropertyDecl::OBJC_PR_class) == 0; - if (auto Info = Reader->lookupObjCProperty(*Context, - Property->getName(), - isInstanceProperty)) { - ::ProcessAPINotes(*this, Property, *Info, Info.getSelectedRole()); - } + auto Info = Reader->lookupObjCProperty(*Context, Property->getName(), + isInstanceProperty); + ProcessVersionedAPINotes(*this, Property, Info); } } diff --git a/test/APINotes/properties.m b/test/APINotes/properties.m index a4c925b6744..b1559b9002c 100644 --- a/test/APINotes/properties.m +++ b/test/APINotes/properties.m @@ -10,22 +10,36 @@ // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnly 'id' // CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClass 'id' // CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id' // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit -// CHECK-4-NEXT: {{^$}} +// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0 +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id' // CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit -// CHECK-4-NEXT: {{^$}} +// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0 +// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id' -// CHECK-3-NEXT: {{^$}} +// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} 0{{$}} +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} +// CHECK-NOT: Attr // CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassExceptInVersion3 'id' -// CHECK-3-NEXT: {{^$}} +// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} 0{{$}} +// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit // CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit +// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} +// CHECK-NOT: Attr + +// CHECK-LABEL: Decl diff --git a/test/APINotes/versioned.m b/test/APINotes/versioned.m index d761620b80e..38690fe18be 100644 --- a/test/APINotes/versioned.m +++ b/test/APINotes/versioned.m @@ -3,16 +3,27 @@ // Build and check the unversioned module file. // RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s // Build and check the versioned module file. -// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s // RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'moveToPoint' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s #import // CHECK-UNVERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(x:y:)"))); // CHECK-VERSIONED: void moveToPoint(double x, double y) __attribute__((swift_name("moveTo(a:b:)"))); +// CHECK-DUMP-LABEL: Dumping moveToPoint +// CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 0 +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)" +// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" +// CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)" +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 +// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)" +// CHECK-DUMP-NOT: Dumping + // CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape))); // CHECK-VERSIONED: void acceptClosure(void (^block)(void)); diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 27ab34c1309..8ba2ebc487f 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -1160,6 +1160,32 @@ namespace { } }; + class AttrArgument : public SimpleArgument { + public: + AttrArgument(const Record &Arg, StringRef Attr) + : SimpleArgument(Arg, Attr, "Attr *") + {} + + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " AttrVec vec;\n" + " ReadAttributes(F, vec, Record, Idx);\n" + " assert(vec.size() == 1);\n" + " Attr *" << getLowerName() << " = vec.front();"; + } + + void writePCHWrite(raw_ostream &OS) const override { + OS << " AddAttributes(SA->get" << getUpperName() << "());"; + } + + void writeDump(raw_ostream &OS) const override {} + + void writeDumpChildren(raw_ostream &OS) const override { + OS << " dumpAttr(SA->get" << getUpperName() << "());\n"; + } + + void writeHasChildren(raw_ostream &OS) const override { OS << "true"; } + }; + } // end anonymous namespace static std::unique_ptr @@ -1207,6 +1233,8 @@ createArgument(const Record &Arg, StringRef Attr, Ptr = llvm::make_unique(Arg, Attr); else if (ArgName == "VersionArgument") Ptr = llvm::make_unique(Arg, Attr); + else if (ArgName == "AttrArgument") + Ptr = llvm::make_unique(Arg, Attr); if (!Ptr) { // Search in reverse order so that the most-derived type is handled first.