From b636b904c229f02d574a27b3f6f2a41c39a1d541 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 17 Feb 2016 22:05:48 +0000 Subject: [PATCH] Add 'nopartial' qualifier for availability attributes. An optional nopartial can be placed after the platform name. int bar() __attribute__((availability(macosx,nopartial,introduced=10.12)) When deploying back to a platform version prior to when the declaration was introduced, with 'nopartial', Clang emits an error specifying that the function is not introduced yet; without 'nopartial', the behavior stays the same: the declaration is `weakly linked`. A member is added to the end of AttributeList to save the location of the 'nopartial' keyword. A bool member is added to AvailabilityAttr. The diagnostics for 'nopartial' not-yet-introduced is handled in the same way as we handle unavailable cases. Reviewed by Doug Gregor and Jordan Rose. rdar://23791325 llvm-svn: 261163 --- clang/include/clang/Basic/Attr.td | 3 +- clang/include/clang/Basic/AttrDocs.td | 11 ++++++-- clang/include/clang/Basic/DiagnosticGroups.td | 1 + clang/include/clang/Basic/DiagnosticSemaKinds.td | 9 ++++-- clang/include/clang/Parse/Parser.h | 3 ++ clang/include/clang/Sema/AttributeList.h | 32 ++++++++++++++++++---- clang/include/clang/Sema/DelayedDiagnostic.h | 9 ++++-- clang/include/clang/Sema/Sema.h | 4 ++- clang/lib/Parse/ParseDecl.cpp | 24 ++++++++++++---- clang/lib/Parse/Parser.cpp | 1 + clang/lib/Sema/DelayedDiagnostic.cpp | 4 +++ clang/lib/Sema/SemaDecl.cpp | 2 +- clang/lib/Sema/SemaDeclAttr.cpp | 35 ++++++++++++++++++++---- clang/lib/Sema/SemaExpr.cpp | 13 +++++++-- clang/test/Sema/attr-availability-macosx.c | 2 ++ 15 files changed, 124 insertions(+), 29 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 534370b..bf5cbb1 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -450,7 +450,8 @@ def Availability : InheritableAttr { let Spellings = [GNU<"availability">]; let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">, VersionArgument<"deprecated">, VersionArgument<"obsoleted">, - BoolArgument<"unavailable">, StringArgument<"message">]; + BoolArgument<"unavailable">, StringArgument<"message">, + BoolArgument<"nopartial">]; let AdditionalMembers = [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) { return llvm::StringSwitch(Platform) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 8f24db0..d4a9fb8 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -685,9 +685,14 @@ are: Apple's watchOS operating system. The minimum deployment target is specified by the ``-mwatchos-version-min=*version*`` command-line argument. -A declaration can be used even when deploying back to a platform version prior -to when the declaration was introduced. When this happens, the declaration is -`weakly linked +An optional nopartial can be placed after the platform name. +With the optional nopartial, when deploying back to a platform version prior to +when the declaration was introduced, Clang emits an error specifying that the +function is not introduced yet. + +Without the optional nopartial, a declaration can be used even when deploying back +to a platform version prior to when the declaration was introduced. When this +happens, the declaration is `weakly linked `_, as if the ``weak_import`` attribute were added to the declaration. A weakly-linked declaration may or may not be present a run-time, and a program diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 969050d..875f5ce 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -87,6 +87,7 @@ def DeprecatedAttributes : DiagGroup<"deprecated-attributes">; def DeprecatedDeclarations : DiagGroup<"deprecated-declarations">; def UnavailableDeclarations : DiagGroup<"unavailable-declarations">; def PartialAvailability : DiagGroup<"partial-availability">; +def NotYetIntroducedDeclarations : DiagGroup<"not-yet-introduced-declarations">; def DeprecatedImplementations :DiagGroup<"deprecated-implementations">; def DeprecatedIncrementBool : DiagGroup<"deprecated-increment-bool">; def DeprecatedRegister : DiagGroup<"deprecated-register">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a41f1c9..434f06d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -837,7 +837,7 @@ def warn_accessor_property_type_mismatch : Warning< def not_conv_function_declared_at : Note<"type conversion function declared here">; def note_method_declared_at : Note<"method %0 declared here">; def note_property_attribute : Note<"property %0 is declared " - "%select{deprecated|unavailable|partial}1 here">; + "%select{deprecated|unavailable|partial|not-yet-introduced}1 here">; def err_setter_type_void : Error<"type of setter must be void">; def err_duplicate_method_decl : Error<"duplicate declaration of method %0">; def warn_duplicate_method_decl : @@ -4120,9 +4120,14 @@ def err_unavailable_message : Error<"%0 is unavailable: %1">; def warn_unavailable_fwdclass_message : Warning< "%0 may be unavailable because the receiver type is unknown">, InGroup; +def err_notyetintroduced : Error<"%0 is not introduced yet">; +def err_notyetintroduced_message : Error<"%0 is not introduced yet: %1">; +def warn_notyetintroduced_fwdclass_message : Warning< + "%0 may not be introduced because the receiver type is unknown">, + InGroup; def note_availability_specified_here : Note< "%0 has been explicitly marked " - "%select{unavailable|deleted|deprecated|partial}1 here">; + "%select{unavailable|deleted|deprecated|partial|not-yet-introduced}1 here">; def note_implicitly_deleted : Note< "explicitly defaulted function was implicitly deleted here">; def note_inherited_deleted_here : Note< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 00885a5..10d6fa1 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -134,6 +134,9 @@ class Parser : public CodeCompletionHandler { /// \brief Identifier for "message". IdentifierInfo *Ident_message; + /// \brief Identifier for "nopartial". + IdentifierInfo *Ident_nopartial; + /// C++0x contextual keywords. mutable IdentifierInfo *Ident_final; mutable IdentifierInfo *Ident_override; diff --git a/clang/include/clang/Sema/AttributeList.h b/clang/include/clang/Sema/AttributeList.h index 3450680..bfb85d0 100644 --- a/clang/include/clang/Sema/AttributeList.h +++ b/clang/include/clang/Sema/AttributeList.h @@ -157,6 +157,17 @@ private: + NumArgs)[index]; } + /// The location of the 'nopartial' keyword in an availability attribute. + SourceLocation *getNopartialSlot() { + return reinterpret_cast( + &getAvailabilitySlot(ObsoletedSlot) + 1); + } + + SourceLocation const *getNopartialSlot() const { + return reinterpret_cast( + &getAvailabilitySlot(ObsoletedSlot) + 1); + } + public: struct TypeTagForDatatypeData { ParsedType *MatchingCType; @@ -233,7 +244,7 @@ private: const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *messageExpr, - Syntax syntaxUsed) + Syntax syntaxUsed, SourceLocation nopartial) : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), @@ -245,6 +256,7 @@ private: new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated); new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted); + memcpy(getNopartialSlot(), &nopartial, sizeof(SourceLocation)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -412,6 +424,11 @@ public: return getAvailabilitySlot(ObsoletedSlot); } + SourceLocation getNopartialLoc() const { + assert(getKind() == AT_Availability && "Not an availability attribute"); + return *getNopartialSlot(); + } + SourceLocation getUnavailableLoc() const { assert(getKind() == AT_Availability && "Not an availability attribute"); return UnavailableLoc; @@ -488,7 +505,7 @@ public: AvailabilityAllocSize = sizeof(AttributeList) + ((3 * sizeof(AvailabilityChange) + sizeof(void*) + - sizeof(ArgsUnion) - 1) + sizeof(ArgsUnion) + sizeof(SourceLocation) - 1) / sizeof(void*) * sizeof(void*)), TypeTagForDatatypeAllocSize = sizeof(AttributeList) @@ -606,13 +623,14 @@ public: const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *MessageExpr, - AttributeList::Syntax syntax) { + AttributeList::Syntax syntax, + SourceLocation nopartial) { void *memory = allocate(AttributeFactory::AvailabilityAllocSize); return add(new (memory) AttributeList(attrName, attrRange, scopeName, scopeLoc, Param, introduced, deprecated, obsoleted, unavailable, MessageExpr, - syntax)); + syntax, nopartial)); } AttributeList *create(IdentifierInfo *attrName, SourceRange attrRange, @@ -741,10 +759,12 @@ public: const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *MessageExpr, - AttributeList::Syntax syntax) { + AttributeList::Syntax syntax, + SourceLocation nopartial) { AttributeList *attr = pool.create(attrName, attrRange, scopeName, scopeLoc, Param, introduced, - deprecated, obsoleted, unavailable, MessageExpr, syntax); + deprecated, obsoleted, unavailable, MessageExpr, syntax, + nopartial); add(attr); return attr; } diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h index 155b3aa..43d6764 100644 --- a/clang/include/clang/Sema/DelayedDiagnostic.h +++ b/clang/include/clang/Sema/DelayedDiagnostic.h @@ -113,7 +113,8 @@ private: /// the complete parsing of the current declaration. class DelayedDiagnostic { public: - enum DDKind { Deprecation, Unavailable, Access, ForbiddenType }; + enum DDKind { Deprecation, Unavailable, Access, ForbiddenType, + NotYetIntroduced }; unsigned char Kind; // actually a DDKind bool Triggered; @@ -165,13 +166,15 @@ public: } const NamedDecl *getDeprecationDecl() const { - assert((Kind == Deprecation || Kind == Unavailable) && + assert((Kind == Deprecation || Kind == Unavailable || + Kind == NotYetIntroduced) && "Not a deprecation diagnostic."); return DeprecationData.Decl; } StringRef getDeprecationMessage() const { - assert((Kind == Deprecation || Kind == Unavailable) && + assert((Kind == Deprecation || Kind == Unavailable || + Kind == NotYetIntroduced) && "Not a deprecation diagnostic."); return StringRef(DeprecationData.Message, DeprecationData.MessageLen); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index be97334..daea39b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2108,6 +2108,7 @@ public: VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, + bool IsNopartial, AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex); TypeVisibilityAttr *mergeTypeVisibilityAttr(Decl *D, SourceRange Range, @@ -3535,7 +3536,8 @@ public: void redelayDiagnostics(sema::DelayedDiagnosticPool &pool); - enum AvailabilityDiagnostic { AD_Deprecation, AD_Unavailable, AD_Partial }; + enum AvailabilityDiagnostic { AD_Deprecation, AD_Unavailable, AD_Partial, + AD_NotYetIntroduced }; void EmitAvailabilityWarning(AvailabilityDiagnostic AD, NamedDecl *D, StringRef Message, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 900c33b..c766ef3 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -833,11 +833,14 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { /// \brief Parse the contents of the "availability" attribute. /// /// availability-attribute: -/// 'availability' '(' platform ',' version-arg-list, opt-message')' +/// 'availability' '(' platform ',' opt-nopartial version-arg-list, opt-message')' /// /// platform: /// identifier /// +/// opt-nopartial: +/// 'nopartial' ',' +/// /// version-arg-list: /// version-arg /// version-arg ',' version-arg-list @@ -867,7 +870,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, return; } - // Parse the platform name, + // Parse the platform name. if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_availability_expected_platform); SkipUntil(tok::r_paren, StopAtSemi); @@ -889,10 +892,12 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Ident_obsoleted = PP.getIdentifierInfo("obsoleted"); Ident_unavailable = PP.getIdentifierInfo("unavailable"); Ident_message = PP.getIdentifierInfo("message"); + Ident_nopartial = PP.getIdentifierInfo("nopartial"); } - // Parse the set of introductions/deprecations/removals. - SourceLocation UnavailableLoc; + // Parse the optional "nopartial" and the set of + // introductions/deprecations/removals. + SourceLocation UnavailableLoc, NopartialLoc; do { if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_availability_expected_change); @@ -902,6 +907,15 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, IdentifierInfo *Keyword = Tok.getIdentifierInfo(); SourceLocation KeywordLoc = ConsumeToken(); + if (Keyword == Ident_nopartial) { + if (NopartialLoc.isValid()) { + Diag(KeywordLoc, diag::err_availability_redundant) + << Keyword << SourceRange(NopartialLoc); + } + NopartialLoc = KeywordLoc; + continue; + } + if (Keyword == Ident_unavailable) { if (UnavailableLoc.isValid()) { Diag(KeywordLoc, diag::err_availability_redundant) @@ -1023,7 +1037,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Changes[Deprecated], Changes[Obsoleted], UnavailableLoc, MessageExpr.get(), - Syntax); + Syntax, NopartialLoc); } /// \brief Parse the contents of the "objc_bridge_related" attribute. diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index e33586c..cfc02e0 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -491,6 +491,7 @@ void Parser::Initialize() { Ident_deprecated = nullptr; Ident_obsoleted = nullptr; Ident_unavailable = nullptr; + Ident_nopartial = nullptr; Ident__except = nullptr; diff --git a/clang/lib/Sema/DelayedDiagnostic.cpp b/clang/lib/Sema/DelayedDiagnostic.cpp index ceea04f..853f598 100644 --- a/clang/lib/Sema/DelayedDiagnostic.cpp +++ b/clang/lib/Sema/DelayedDiagnostic.cpp @@ -35,6 +35,9 @@ DelayedDiagnostic::makeAvailability(Sema::AvailabilityDiagnostic AD, case Sema::AD_Unavailable: DD.Kind = Unavailable; break; + case Sema::AD_NotYetIntroduced: + DD.Kind = NotYetIntroduced; + break; case Sema::AD_Partial: llvm_unreachable("AD_Partial diags should not be delayed"); } @@ -63,6 +66,7 @@ void DelayedDiagnostic::Destroy() { case Deprecation: case Unavailable: + case NotYetIntroduced: delete [] DeprecationData.Message; break; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 3345a1b..d79e2e2 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2196,7 +2196,7 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), AA->getIntroduced(), AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(), - AA->getMessage(), AMK, + AA->getMessage(), AA->getNopartial(), AMK, AttrSpellingListIndex); else if (const auto *VA = dyn_cast(Attr)) NewAttr = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9351c52..bfb82ac 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1916,6 +1916,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, + bool IsNopartial, AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex) { VersionTuple MergedIntroduced = Introduced; @@ -2062,7 +2063,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, return ::new (Context) AvailabilityAttr(Range, Context, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable, Message, - AttrSpellingListIndex); + IsNopartial, AttrSpellingListIndex); } return nullptr; } @@ -2089,6 +2090,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); bool IsUnavailable = Attr.getUnavailableLoc().isValid(); + bool IsNopartial = Attr.getNopartialLoc().isValid(); StringRef Str; if (const StringLiteral *SE = dyn_cast_or_null(Attr.getMessageExpr())) @@ -2099,6 +2101,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, + IsNopartial, Sema::AMK_None, Index); if (NewAttr) @@ -2143,6 +2146,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, NewDeprecated, NewObsoleted, IsUnavailable, Str, + IsNopartial, Sema::AMK_None, Index); if (NewAttr) @@ -2165,6 +2169,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, + IsNopartial, Sema::AMK_None, Index); if (NewAttr) @@ -5957,6 +5962,14 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K, property_note_select = /* partial */ 2; available_here_select_kind = /* partial */ 3; break; + + case Sema::AD_NotYetIntroduced: + diag = diag::err_notyetintroduced; + diag_message = diag::err_notyetintroduced_message; + diag_fwdclass_message = diag::warn_notyetintroduced_fwdclass_message; + property_note_select = /* deprecated */ 3; + available_here_select_kind = /* notyetintroduced */ 4; + break; } if (!Message.empty()) { @@ -5983,10 +5996,22 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K, static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, Decl *Ctx) { assert(DD.Kind == DelayedDiagnostic::Deprecation || - DD.Kind == DelayedDiagnostic::Unavailable); - Sema::AvailabilityDiagnostic AD = DD.Kind == DelayedDiagnostic::Deprecation - ? Sema::AD_Deprecation - : Sema::AD_Unavailable; + DD.Kind == DelayedDiagnostic::Unavailable || + DD.Kind == DelayedDiagnostic::NotYetIntroduced); + Sema::AvailabilityDiagnostic AD; + switch (DD.Kind) { + case DelayedDiagnostic::Deprecation: + AD = Sema::AD_Deprecation; + break; + case DelayedDiagnostic::Unavailable: + AD = Sema::AD_Unavailable; + break; + case DelayedDiagnostic::NotYetIntroduced: + AD = Sema::AD_NotYetIntroduced; + break; + default: + llvm_unreachable("Expecting: deprecated, unavailable, not-yet-introduced"); + } DD.Triggered = true; DoEmitAvailabilityWarning( S, AD, Ctx, DD.getDeprecationDecl(), DD.getDeprecationMessage(), DD.Loc, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 5976c2c..cbac36b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -137,7 +137,7 @@ DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, const ObjCPropertyDecl *ObjCPDecl = nullptr; if (Result == AR_Deprecated || Result == AR_Unavailable || - AR_NotYetIntroduced) { + Result == AR_NotYetIntroduced) { if (const ObjCMethodDecl *MD = dyn_cast(D)) { if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { AvailabilityResult PDeclResult = PD->getAvailability(nullptr); @@ -159,11 +159,20 @@ DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, break; case AR_NotYetIntroduced: { + // With nopartial, the compiler will emit delayed error just like how + // "deprecated, unavailable" are handled. + AvailabilityAttr *AA = D->getAttr(); + if (AA && AA->getNopartial() && + S.getCurContextAvailability() != AR_NotYetIntroduced) + S.EmitAvailabilityWarning(Sema::AD_NotYetIntroduced, + D, Message, Loc, UnknownObjCClass, ObjCPDecl, + ObjCPropertyAccess); + // Don't do this for enums, they can't be redeclared. if (isa(D) || isa(D)) break; - bool Warn = !D->getAttr()->isInherited(); + bool Warn = !AA->isInherited(); // Objective-C method declarations in categories are not modelled as // redeclarations, so manually look for a redeclaration in a category // if necessary. diff --git a/clang/test/Sema/attr-availability-macosx.c b/clang/test/Sema/attr-availability-macosx.c index 38f149b..8f5550e 100644 --- a/clang/test/Sema/attr-availability-macosx.c +++ b/clang/test/Sema/attr-availability-macosx.c @@ -6,6 +6,7 @@ void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5) void f3(int) __attribute__((availability(macosx,introduced=10.6))); void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=3.0))); // expected-note{{explicitly marked unavailable}} void f5(int) __attribute__((availability(ios,introduced=3.2), availability(macosx,unavailable))); // expected-note{{'f5' has been explicitly marked unavailable here}} +void f6(int) __attribute__((availability(macosx,nopartial,introduced=10.6))); //expected-note{{'f6' has been explicitly marked not-yet-introduced here}} void test() { f0(0); @@ -14,6 +15,7 @@ void test() { f3(0); f4(0); // expected-error{{f4' is unavailable: obsoleted in OS X 10.5}} f5(0); // expected-error{{'f5' is unavailable: not available on OS X}} + f6(0); // expected-error{{'f6' is not introduced yet: introduced in OS X 10.6}} } // rdar://10535640 -- 2.7.4