Index: cfe/trunk/include/clang/Basic/Attr.td =================================================================== --- cfe/trunk/include/clang/Basic/Attr.td +++ cfe/trunk/include/clang/Basic/Attr.td @@ -717,7 +717,8 @@ let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">, VersionArgument<"deprecated">, VersionArgument<"obsoleted">, BoolArgument<"unavailable">, StringArgument<"message">, - BoolArgument<"strict">, StringArgument<"replacement">]; + BoolArgument<"strict">, StringArgument<"replacement">, + IntArgument<"priority">]; let AdditionalMembers = [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) { return llvm::StringSwitch(Platform) Index: cfe/trunk/include/clang/Basic/AttrDocs.td =================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td +++ cfe/trunk/include/clang/Basic/AttrDocs.td @@ -1152,11 +1152,14 @@ the deprecated declaration with the new declaration specified. Multiple availability attributes can be placed on a declaration, which may -correspond to different platforms. Only the availability attribute with the -platform corresponding to the target platform will be used; any others will be -ignored. If no availability attribute specifies availability for the current -target platform, the availability attributes are ignored. Supported platforms -are: +correspond to different platforms. For most platforms, the availability +attribute with the platform corresponding to the target platform will be used; +any others will be ignored. However, the availability for ``watchOS`` and +``tvOS`` can be implicitly inferred from an ``iOS`` availability attribute. +Any explicit availability attributes for those platforms are still prefered over +the implicitly inferred availability attributes. If no availability attribute +specifies availability for the current target platform, the availability +attributes are ignored. Supported platforms are: ``ios`` Apple's iOS operating system. The minimum deployment target is specified by @@ -1229,6 +1232,63 @@ - (id)otherMethod API_AVAILABLE(macos(10.11), ios(11.0)); @end +Availability attributes can also be applied using a ``#pragma clang attribute``. +Any explicit availability attribute whose platform corresponds to the target +platform is applied to a declaration regardless of the availability attributes +specified in the pragma. For example, in the code below, +``hasExplicitAvailabilityAttribute`` will use the ``macOS`` availability +attribute that is specified with the declaration, whereas +``getsThePragmaAvailabilityAttribute`` will use the ``macOS`` availability +attribute that is applied by the pragma. + +.. code-block:: c + + #pragma clang attribute push (__attribute__((availability(macOS, introduced=10.12))), apply_to=function) + void getsThePragmaAvailabilityAttribute(void); + void hasExplicitAvailabilityAttribute(void) __attribute__((availability(macos,introduced=10.4))); + #pragma clang attribute pop + +For platforms like ``watchOS`` and ``tvOS``, whose availability attributes can +be implicitly inferred from an ``iOS`` availability attribute, the logic is +slightly more complex. The explicit and the pragma-applied availability +attributes whose platform corresponds to the target platform are applied as +described in the previous paragraph. However, the implicitly inferred attributes +are applied to a declaration only when there is no explicit or pragma-applied +availability attribute whose platform corresponds to the target platform. For +example, the function below will receive the ``tvOS`` availability from the +pragma rather than using the inferred ``iOS`` availability from the declaration: + +.. code-block:: c + + #pragma clang attribute push (__attribute__((availability(tvOS, introduced=12.0))), apply_to=function) + void getsThePragmaTVOSAvailabilityAttribute(void) __attribute__((availability(iOS,introduced=11.0))); + #pragma clang attribute pop + +The compiler is also able to apply implicly inferred attributes from a pragma +as well. For example, when targeting ``tvOS``, the function below will receive +a ``tvOS`` availability attribute that is implicitly inferred from the ``iOS`` +availability attribute applied by the pragma: + +.. code-block:: c + + #pragma clang attribute push (__attribute__((availability(iOS, introduced=12.0))), apply_to=function) + void infersTVOSAvailabilityFromPragma(void); + #pragma clang attribute pop + +The implicit attributes that are inferred from explicitly specified attributes +whose platform corresponds to the target platform are applied to the declaration +even if there is an availability attribute that can be inferred from a pragma. +For example, the function below will receive the ``tvOS, introduced=11.0`` +availability that is inferred from the attribute on the declaration rather than +inferring availability from the pragma: + +.. code-block:: c + + #pragma clang attribute push (__attribute__((availability(iOS, unavailable))), apply_to=function) + void infersTVOSAvailabilityFromAttributeNextToDeclaration(void) + __attribute__((availability(iOS,introduced=11.0))); + #pragma clang attribute pop + Also see the documentation for `@available `_ }]; Index: cfe/trunk/include/clang/Sema/ParsedAttr.h =================================================================== --- cfe/trunk/include/clang/Sema/ParsedAttr.h +++ cfe/trunk/include/clang/Sema/ParsedAttr.h @@ -207,6 +207,9 @@ /// A cached value. mutable unsigned ProcessingCache : 8; + /// True if the attribute is specified using '#pragma clang attribute'. + mutable unsigned IsPragmaClangAttribute : 1; + /// The location of the 'unavailable' keyword in an /// availability attribute. SourceLocation UnavailableLoc; @@ -238,7 +241,8 @@ ScopeLoc(scopeLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), - HasParsedType(false), HasProcessingCache(false) { + HasParsedType(false), HasProcessingCache(false), + IsPragmaClangAttribute(false) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -255,8 +259,8 @@ ScopeLoc(scopeLoc), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - HasProcessingCache(false), UnavailableLoc(unavailable), - MessageExpr(messageExpr) { + HasProcessingCache(false), IsPragmaClangAttribute(false), + UnavailableLoc(unavailable), MessageExpr(messageExpr) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (getAvailabilityData()) detail::AvailabilityData( @@ -273,7 +277,7 @@ ScopeLoc(scopeLoc), NumArgs(3), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - HasProcessingCache(false) { + HasProcessingCache(false), IsPragmaClangAttribute(false) { ArgsUnion *Args = getArgsBuffer(); Args[0] = Parm1; Args[1] = Parm2; @@ -290,7 +294,7 @@ ScopeLoc(scopeLoc), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), - HasProcessingCache(false) { + HasProcessingCache(false), IsPragmaClangAttribute(false) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); detail::TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -308,7 +312,7 @@ ScopeLoc(scopeLoc), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), - HasProcessingCache(false) { + HasProcessingCache(false), IsPragmaClangAttribute(false) { new (&getTypeBuffer()) ParsedType(typeArg); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -322,7 +326,7 @@ ScopeLoc(scopeLoc), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), - HasProcessingCache(false) { + HasProcessingCache(false), IsPragmaClangAttribute(false) { new (&getPropertyDataBuffer()) detail::PropertyData(getterId, setterId); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -436,6 +440,11 @@ bool isUsedAsTypeAttr() const { return UsedAsTypeAttr; } void setUsedAsTypeAttr() { UsedAsTypeAttr = true; } + /// True if the attribute is specified using '#pragma clang attribute'. + bool isPragmaClangAttribute() const { return IsPragmaClangAttribute; } + + void setIsPragmaClangAttribute() { IsPragmaClangAttribute = true; } + bool isPackExpansion() const { return EllipsisLoc.isValid(); } SourceLocation getEllipsisLoc() const { return EllipsisLoc; } Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -2459,18 +2459,38 @@ AMK_ProtocolImplementation, }; + /// Describes the kind of priority given to an availability attribute. + /// + /// The sum of priorities deteremines the final priority of the attribute. + /// The final priority determines how the attribute will be merged. + /// An attribute with a lower priority will always remove higher priority + /// attributes for the specified platform when it is being applied. An + /// attribute with a higher priority will not be applied if the declaration + /// already has an availability attribute with a lower priority for the + /// specified platform. The final prirority values are not expected to match + /// the values in this enumeration, but instead should be treated as a plain + /// integer value. This enumeration just names the priority weights that are + /// used to calculate that final vaue. + enum AvailabilityPriority : int { + /// The availability attribute was specified explicitly next to the + /// declaration. + AP_Explicit = 0, + + /// The availability attribute was applied using '#pragma clang attribute'. + AP_PragmaClangAttribute = 1, + + /// The availability attribute for a specific platform was inferred from + /// an availability attribute for another platform. + AP_InferredFromOtherPlatform = 2 + }; + /// Attribute merging methods. Return true if a new attribute was added. - AvailabilityAttr *mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, - IdentifierInfo *Platform, - bool Implicit, - VersionTuple Introduced, - VersionTuple Deprecated, - VersionTuple Obsoleted, - bool IsUnavailable, - StringRef Message, - bool IsStrict, StringRef Replacement, - AvailabilityMergeKind AMK, - unsigned AttrSpellingListIndex); + AvailabilityAttr *mergeAvailabilityAttr( + NamedDecl *D, SourceRange Range, IdentifierInfo *Platform, bool Implicit, + VersionTuple Introduced, VersionTuple Deprecated, VersionTuple Obsoleted, + bool IsUnavailable, StringRef Message, bool IsStrict, + StringRef Replacement, AvailabilityMergeKind AMK, int Priority, + unsigned AttrSpellingListIndex); TypeVisibilityAttr *mergeTypeVisibilityAttr(Decl *D, SourceRange Range, TypeVisibilityAttr::VisibilityType Vis, unsigned AttrSpellingListIndex); Index: cfe/trunk/lib/Sema/SemaAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaAttr.cpp +++ cfe/trunk/lib/Sema/SemaAttr.cpp @@ -522,6 +522,7 @@ void Sema::ActOnPragmaAttributeAttribute( ParsedAttr &Attribute, SourceLocation PragmaLoc, attr::ParsedSubjectMatchRuleSet Rules) { + Attribute.setIsPragmaClangAttribute(); SmallVector SubjectMatchRules; // Gather the subject match rules that are supported by the attribute. SmallVector, 4> @@ -679,6 +680,8 @@ for (auto &Entry : Group.Entries) { ParsedAttr *Attribute = Entry.Attribute; assert(Attribute && "Expected an attribute"); + assert(Attribute->isPragmaClangAttribute() && + "expected #pragma clang attribute"); // Ensure that the attribute can be applied to the given declaration. bool Applies = false; Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -2430,13 +2430,11 @@ InheritableAttr *NewAttr = nullptr; unsigned AttrSpellingListIndex = Attr->getSpellingListIndex(); if (const auto *AA = dyn_cast(Attr)) - NewAttr = S.mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), - AA->isImplicit(), AA->getIntroduced(), - AA->getDeprecated(), - AA->getObsoleted(), AA->getUnavailable(), - AA->getMessage(), AA->getStrict(), - AA->getReplacement(), AMK, - AttrSpellingListIndex); + NewAttr = S.mergeAvailabilityAttr( + D, AA->getRange(), AA->getPlatform(), AA->isImplicit(), + AA->getIntroduced(), AA->getDeprecated(), AA->getObsoleted(), + AA->getUnavailable(), AA->getMessage(), AA->getStrict(), + AA->getReplacement(), AMK, AA->getPriority(), AttrSpellingListIndex); else if (const auto *VA = dyn_cast(Attr)) NewAttr = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), AttrSpellingListIndex); Index: cfe/trunk/lib/Sema/SemaDeclAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp @@ -2283,18 +2283,11 @@ return false; } -AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, - IdentifierInfo *Platform, - bool Implicit, - VersionTuple Introduced, - VersionTuple Deprecated, - VersionTuple Obsoleted, - bool IsUnavailable, - StringRef Message, - bool IsStrict, - StringRef Replacement, - AvailabilityMergeKind AMK, - unsigned AttrSpellingListIndex) { +AvailabilityAttr *Sema::mergeAvailabilityAttr( + NamedDecl *D, SourceRange Range, IdentifierInfo *Platform, bool Implicit, + VersionTuple Introduced, VersionTuple Deprecated, VersionTuple Obsoleted, + bool IsUnavailable, StringRef Message, bool IsStrict, StringRef Replacement, + AvailabilityMergeKind AMK, int Priority, unsigned AttrSpellingListIndex) { VersionTuple MergedIntroduced = Introduced; VersionTuple MergedDeprecated = Deprecated; VersionTuple MergedObsoleted = Obsoleted; @@ -2328,16 +2321,15 @@ } // If there is an existing availability attribute for this platform that - // is explicit and the new one is implicit use the explicit one and - // discard the new implicit attribute. - if (!OldAA->isImplicit() && Implicit) { + // has a lower priority use the existing one and discard the new + // attribute. + if (OldAA->getPriority() < Priority) return nullptr; - } - // If there is an existing attribute for this platform that is implicit - // and the new attribute is explicit then erase the old one and - // continue processing the attributes. - if (!Implicit && OldAA->isImplicit()) { + // If there is an existing attribute for this platform that has a higher + // priority than the new attribute then erase the old one and continue + // processing the attributes. + if (OldAA->getPriority() > Priority) { Attrs.erase(Attrs.begin() + i); --e; continue; @@ -2436,11 +2428,10 @@ if (!checkAvailabilityAttr(*this, Range, Platform, MergedIntroduced, MergedDeprecated, MergedObsoleted) && !OverrideOrImpl) { - auto *Avail = ::new (Context) AvailabilityAttr(Range, Context, Platform, - Introduced, Deprecated, - Obsoleted, IsUnavailable, Message, - IsStrict, Replacement, - AttrSpellingListIndex); + auto *Avail = ::new (Context) + AvailabilityAttr(Range, Context, Platform, Introduced, Deprecated, + Obsoleted, IsUnavailable, Message, IsStrict, + Replacement, Priority, AttrSpellingListIndex); Avail->setImplicit(Implicit); return Avail; } @@ -2483,15 +2474,13 @@ } } - AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, AL.getRange(), II, - false/*Implicit*/, - Introduced.Version, - Deprecated.Version, - Obsoleted.Version, - IsUnavailable, Str, - IsStrict, Replacement, - Sema::AMK_None, - Index); + int PriorityModifier = AL.isPragmaClangAttribute() + ? Sema::AP_PragmaClangAttribute + : Sema::AP_Explicit; + AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( + ND, AL.getRange(), II, false /*Implicit*/, Introduced.Version, + Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, IsStrict, + Replacement, Sema::AMK_None, PriorityModifier, Index); if (NewAttr) D->addAttr(NewAttr); @@ -2527,18 +2516,11 @@ auto NewDeprecated = adjustWatchOSVersion(Deprecated.Version); auto NewObsoleted = adjustWatchOSVersion(Obsoleted.Version); - AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, - AL.getRange(), - NewII, - true/*Implicit*/, - NewIntroduced, - NewDeprecated, - NewObsoleted, - IsUnavailable, Str, - IsStrict, - Replacement, - Sema::AMK_None, - Index); + AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( + ND, AL.getRange(), NewII, true /*Implicit*/, NewIntroduced, + NewDeprecated, NewObsoleted, IsUnavailable, Str, IsStrict, + Replacement, Sema::AMK_None, + PriorityModifier + Sema::AP_InferredFromOtherPlatform, Index); if (NewAttr) D->addAttr(NewAttr); } @@ -2552,20 +2534,13 @@ NewII = &S.Context.Idents.get("tvos_app_extension"); if (NewII) { - AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, - AL.getRange(), - NewII, - true/*Implicit*/, - Introduced.Version, - Deprecated.Version, - Obsoleted.Version, - IsUnavailable, Str, - IsStrict, - Replacement, - Sema::AMK_None, - Index); - if (NewAttr) - D->addAttr(NewAttr); + AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( + ND, AL.getRange(), NewII, true /*Implicit*/, Introduced.Version, + Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, IsStrict, + Replacement, Sema::AMK_None, + PriorityModifier + Sema::AP_InferredFromOtherPlatform, Index); + if (NewAttr) + D->addAttr(NewAttr); } } } Index: cfe/trunk/test/SemaObjC/attr-availability-priority.m =================================================================== --- cfe/trunk/test/SemaObjC/attr-availability-priority.m +++ cfe/trunk/test/SemaObjC/attr-availability-priority.m @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -triple arm64-apple-tvos12.0 -fsyntax-only -verify %s + +void explicit() __attribute__((availability(tvos, introduced=11.0, deprecated=12.0))); // expected-note {{marked deprecated here}} +void inferred() __attribute__((availability(ios, introduced=11.0, deprecated=12.0))); // expected-note {{marked deprecated here}} +void explicitOverInferred() +__attribute__((availability(ios, introduced=11.0, deprecated=12.0))) +__attribute__((availability(tvos, introduced=11.0))); +void explicitOverInferred2() +__attribute__((availability(tvos, introduced=11.0))) +__attribute__((availability(ios, introduced=11.0, deprecated=12.0))); + +void simpleUsage() { + explicit(); // expected-warning{{'explicit' is deprecated: first deprecated in tvOS 12.0}} + inferred(); // expected-warning{{'inferred' is deprecated: first deprecated in tvOS 12.0}} + // ok, not deprecated for tvOS. + explicitOverInferred(); + explicitOverInferred2(); +} + +#pragma clang attribute push (__attribute__((availability(tvos, introduced=11.0, deprecated=12.0))), apply_to=function) + +void explicitFromPragma(); // expected-note {{marked deprecated here}} +void explicitWinsOverExplicitFromPragma() __attribute__((availability(tvos, introduced=11.0))); +void implicitLosesOverExplicitFromPragma() __attribute__((availability(ios, introduced=11.0))); // expected-note {{marked deprecated here}} + +#pragma clang attribute pop + +#pragma clang attribute push (__attribute__((availability(ios, introduced=11.0, deprecated=12.0))), apply_to=function) + +void implicitFromPragma(); // expected-note {{marked deprecated here}} +void explicitWinsOverImplicitFromPragma() __attribute__((availability(tvos, introduced=11.0))); +void implicitWinsOverImplicitFromPragma() __attribute__((availability(ios, introduced=11.0))); + +#pragma clang attribute pop + +#pragma clang attribute push (__attribute__((availability(tvos, introduced=11.0, deprecated=12.0))), apply_to=function) +#pragma clang attribute push (__attribute__((availability(ios, introduced=11.0, deprecated=11.3))), apply_to=function) + +void pragmaExplicitWinsOverPragmaImplicit(); // expected-note {{marked deprecated here}} + +#pragma clang attribute pop +#pragma clang attribute pop + +void pragmaUsage() { + explicitFromPragma(); // expected-warning {{'explicitFromPragma' is deprecated: first deprecated in tvOS 12.0}} + explicitWinsOverExplicitFromPragma(); // ok + implicitLosesOverExplicitFromPragma(); // expected-warning {{'implicitLosesOverExplicitFromPragma' is deprecated: first deprecated in tvOS 12.0}} + + implicitFromPragma(); // expected-warning {{'implicitFromPragma' is deprecated: first deprecated in tvOS 12.0}} + explicitWinsOverImplicitFromPragma(); // ok + implicitWinsOverImplicitFromPragma(); // ok + pragmaExplicitWinsOverPragmaImplicit(); // expected-warning {{'pragmaExplicitWinsOverPragmaImplicit' is deprecated: first deprecated in tvOS 12.0}} +}