From: Douglas Gregor Date: Wed, 30 Sep 2015 21:27:42 +0000 (+0000) Subject: Don't inherit availability information when implementing a protocol requirement. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bd3ba161658a0472f3fee6b84684809d529b7f38;p=clang Don't inherit availability information when implementing a protocol requirement. When an Objective-C method implements a protocol requirement, do not inherit any availability information from the protocol requirement. Rather, check that the implementation is not less available than the protocol requirement, as we do when overriding a method that has availability. Fixes rdar://problem/22734745. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@248949 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index cac97ce05f..fa1a6ff8fc 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2410,15 +2410,19 @@ def warn_availability_version_ordering : Warning< def warn_mismatched_availability: Warning< "availability does not match previous declaration">, InGroup; def warn_mismatched_availability_override : Warning< - "overriding method %select{introduced after|" - "deprecated before|obsoleted before}0 overridden method on %1 (%2 vs. %3)">, - InGroup; + "%select{|overriding }4method %select{introduced after|" + "deprecated before|obsoleted before}0 " + "%select{the protocol method it implements|overridden method}4 " + "on %1 (%2 vs. %3)">, InGroup; def warn_mismatched_availability_override_unavail : Warning< - "overriding method cannot be unavailable on %0 when its overridden method is " + "%select{|overriding }1method cannot be unavailable on %0 when " + "%select{the protocol method it implements|its overridden method}1 is " "available">, InGroup; def note_overridden_method : Note< "overridden method is here">; +def note_protocol_method : Note< + "protocol method is here">; // Thread Safety Attributes def warn_invalid_capability_name : Warning< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 09b46ce77b..1cf57ca36d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2062,6 +2062,22 @@ public: TypeSourceInfo *TInfo); bool isIncompatibleTypedef(TypeDecl *Old, TypedefNameDecl *New); + /// \brief Describes the kind of merge to perform for availability + /// attributes (including "deprecated", "unavailable", and "availability"). + enum AvailabilityMergeKind { + /// \brief Don't merge availability attributes at all. + AMK_None, + /// \brief Merge availability attributes for a redeclaration, which requires + /// an exact match. + AMK_Redeclaration, + /// \brief Merge availability attributes for an override, which requires + /// an exact match or a weakening of constraints. + AMK_Override, + /// \brief Merge availability attributes for an implementation of + /// a protocol requirement. + AMK_ProtocolImplementation, + }; + /// Attribute merging methods. Return true if a new attribute was added. AvailabilityAttr *mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, IdentifierInfo *Platform, @@ -2070,7 +2086,7 @@ public: VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, - bool Override, + AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex); TypeVisibilityAttr *mergeTypeVisibilityAttr(Decl *D, SourceRange Range, TypeVisibilityAttr::VisibilityType Vis, @@ -2099,19 +2115,6 @@ public: OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, SourceRange Range, unsigned AttrSpellingListIndex); - /// \brief Describes the kind of merge to perform for availability - /// attributes (including "deprecated", "unavailable", and "availability"). - enum AvailabilityMergeKind { - /// \brief Don't merge availability attributes at all. - AMK_None, - /// \brief Merge availability attributes for a redeclaration, which requires - /// an exact match. - AMK_Redeclaration, - /// \brief Merge availability attributes for an override, which requires - /// an exact match or a weakening of constraints. - AMK_Override - }; - void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); void MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 571c9baa04..a359ef4931 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2175,14 +2175,15 @@ static bool mergeAlignedAttrs(Sema &S, NamedDecl *New, Decl *Old) { } static bool mergeDeclAttribute(Sema &S, NamedDecl *D, - const InheritableAttr *Attr, bool Override) { + const InheritableAttr *Attr, + Sema::AvailabilityMergeKind AMK) { InheritableAttr *NewAttr = nullptr; unsigned AttrSpellingListIndex = Attr->getSpellingListIndex(); if (const auto *AA = dyn_cast(Attr)) NewAttr = S.mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), AA->getIntroduced(), AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(), - AA->getMessage(), Override, + AA->getMessage(), AMK, AttrSpellingListIndex); else if (const auto *VA = dyn_cast(Attr)) NewAttr = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), @@ -2219,7 +2220,11 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, // AlignedAttrs are handled separately, because we need to handle all // such attributes on a declaration at the same time. NewAttr = nullptr; - else if (isa(Attr) && Override) + else if (isa(Attr) && + (AMK == Sema::AMK_Override || + AMK == Sema::AMK_ProtocolImplementation)) + NewAttr = nullptr; + else if (isa(Attr) && AMK == Sema::AMK_ProtocolImplementation) NewAttr = nullptr; else if (Attr->duplicatesAllowed() || !DeclHasAttr(D, Attr)) NewAttr = cast(Attr->clone(S.Context)); @@ -2366,8 +2371,8 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, if (!foundAny) New->setAttrs(AttrVec()); for (auto *I : Old->specific_attrs()) { - bool Override = false; // Ignore deprecated/unavailable/availability attributes if requested. + AvailabilityMergeKind LocalAMK = AMK_None; if (isa(I) || isa(I) || isa(I)) { @@ -2376,10 +2381,9 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, continue; case AMK_Redeclaration: - break; - case AMK_Override: - Override = true; + case AMK_ProtocolImplementation: + LocalAMK = AMK; break; } } @@ -2388,7 +2392,7 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, if (isa(I)) continue; - if (mergeDeclAttribute(*this, New, I, Override)) + if (mergeDeclAttribute(*this, New, I, LocalAMK)) foundAny = true; } @@ -3156,8 +3160,11 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, // Merge the attributes, including deprecated/unavailable AvailabilityMergeKind MergeKind = - isa(newMethod->getDeclContext()) ? AMK_Redeclaration - : AMK_Override; + isa(oldMethod->getDeclContext()) + ? AMK_ProtocolImplementation + : isa(newMethod->getDeclContext()) ? AMK_Redeclaration + : AMK_Override; + mergeDeclAttributes(newMethod, oldMethod, MergeKind); // Merge attributes from the parameters. diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 8218c20696..3cf9567889 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1821,12 +1821,24 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, - bool Override, + AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex) { VersionTuple MergedIntroduced = Introduced; VersionTuple MergedDeprecated = Deprecated; VersionTuple MergedObsoleted = Obsoleted; bool FoundAny = false; + bool OverrideOrImpl = false; + switch (AMK) { + case AMK_None: + case AMK_Redeclaration: + OverrideOrImpl = false; + break; + + case AMK_Override: + case AMK_ProtocolImplementation: + OverrideOrImpl = true; + break; + } if (D->hasAttrs()) { AttrVec &Attrs = D->getAttrs(); @@ -1849,24 +1861,24 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, VersionTuple OldObsoleted = OldAA->getObsoleted(); bool OldIsUnavailable = OldAA->getUnavailable(); - if (!versionsMatch(OldIntroduced, Introduced, Override) || - !versionsMatch(Deprecated, OldDeprecated, Override) || - !versionsMatch(Obsoleted, OldObsoleted, Override) || + if (!versionsMatch(OldIntroduced, Introduced, OverrideOrImpl) || + !versionsMatch(Deprecated, OldDeprecated, OverrideOrImpl) || + !versionsMatch(Obsoleted, OldObsoleted, OverrideOrImpl) || !(OldIsUnavailable == IsUnavailable || - (Override && !OldIsUnavailable && IsUnavailable))) { - if (Override) { + (OverrideOrImpl && !OldIsUnavailable && IsUnavailable))) { + if (OverrideOrImpl) { int Which = -1; VersionTuple FirstVersion; VersionTuple SecondVersion; - if (!versionsMatch(OldIntroduced, Introduced, Override)) { + if (!versionsMatch(OldIntroduced, Introduced, OverrideOrImpl)) { Which = 0; FirstVersion = OldIntroduced; SecondVersion = Introduced; - } else if (!versionsMatch(Deprecated, OldDeprecated, Override)) { + } else if (!versionsMatch(Deprecated, OldDeprecated, OverrideOrImpl)) { Which = 1; FirstVersion = Deprecated; SecondVersion = OldDeprecated; - } else if (!versionsMatch(Obsoleted, OldObsoleted, Override)) { + } else if (!versionsMatch(Obsoleted, OldObsoleted, OverrideOrImpl)) { Which = 2; FirstVersion = Obsoleted; SecondVersion = OldObsoleted; @@ -1875,15 +1887,20 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, if (Which == -1) { Diag(OldAA->getLocation(), diag::warn_mismatched_availability_override_unavail) - << AvailabilityAttr::getPrettyPlatformName(Platform->getName()); + << AvailabilityAttr::getPrettyPlatformName(Platform->getName()) + << (AMK == AMK_Override); } else { Diag(OldAA->getLocation(), diag::warn_mismatched_availability_override) << Which << AvailabilityAttr::getPrettyPlatformName(Platform->getName()) - << FirstVersion.getAsString() << SecondVersion.getAsString(); + << FirstVersion.getAsString() << SecondVersion.getAsString() + << (AMK == AMK_Override); } - Diag(Range.getBegin(), diag::note_overridden_method); + if (AMK == AMK_Override) + Diag(Range.getBegin(), diag::note_overridden_method); + else + Diag(Range.getBegin(), diag::note_protocol_method); } else { Diag(OldAA->getLocation(), diag::warn_mismatched_availability); Diag(Range.getBegin(), diag::note_previous_attribute); @@ -1926,11 +1943,11 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, MergedObsoleted == Obsoleted) return nullptr; - // Only create a new attribute if !Override, but we want to do + // Only create a new attribute if !OverrideOrImpl, but we want to do // the checking. if (!checkAvailabilityAttr(*this, Range, Platform, MergedIntroduced, MergedDeprecated, MergedObsoleted) && - !Override) { + !OverrideOrImpl) { return ::new (Context) AvailabilityAttr(Range, Context, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable, Message, @@ -1971,7 +1988,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, - /*Override=*/false, + Sema::AMK_None, Index); if (NewAttr) D->addAttr(NewAttr); diff --git a/test/ARCMT/checking.m b/test/ARCMT/checking.m index 6a7cf76c38..11a57538d7 100644 --- a/test/ARCMT/checking.m +++ b/test/ARCMT/checking.m @@ -44,9 +44,9 @@ struct UnsafeS { }; @interface A : NSObject -- (id)retain; // expected-note {{'retain' has been explicitly marked unavailable here}} -- (id)retainCount; // expected-note {{'retainCount' has been explicitly marked unavailable here}} -- (id)autorelease; // expected-note 2 {{'autorelease' has been explicitly marked unavailable here}} +- (id)retain __attribute__((unavailable)); // expected-note {{'retain' has been explicitly marked unavailable here}} +- (id)retainCount __attribute__((unavailable)); // expected-note {{'retainCount' has been explicitly marked unavailable here}} +- (id)autorelease __attribute__((unavailable)); // expected-note 2 {{'autorelease' has been explicitly marked unavailable here}} - (id)init; - (oneway void)release; - (void)dealloc; diff --git a/test/SemaObjC/attr-availability.m b/test/SemaObjC/attr-availability.m index ea97e0e472..8323139e59 100644 --- a/test/SemaObjC/attr-availability.m +++ b/test/SemaObjC/attr-availability.m @@ -23,6 +23,7 @@ - (void)overridden4 __attribute__((availability(macosx,deprecated=10.3))); // expected-note{{overridden method is here}} - (void)overridden5 __attribute__((availability(macosx,unavailable))); - (void)overridden6 __attribute__((availability(macosx,introduced=10.3))); // expected-note{{overridden method is here}} +- (void)unavailableMethod __attribute__((unavailable)); @end // rdar://11475360 @@ -35,6 +36,7 @@ - (void)overridden4 __attribute__((availability(macosx,deprecated=10.2))); // expected-warning{{overriding method deprecated before overridden method on OS X (10.3 vs. 10.2)}} - (void)overridden5 __attribute__((availability(macosx,introduced=10.3))); - (void)overridden6 __attribute__((availability(macosx,unavailable))); // expected-warning{{overriding method cannot be unavailable on OS X when its overridden method is available}} +- (void)unavailableMethod; // does *not* inherit unavailability @end void f(A *a, B *b) { @@ -206,3 +208,73 @@ void use_myEnum() { // expected-error@+1 {{MyEnum_Blah' is unavailable: not available}} MyEnum e = MyEnum_Blah; } + +// Test that the availability of (optional) protocol methods is not +// inherited be implementations of those protocol methods. +@protocol AvailabilityP2 +@optional +-(void)methodA __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 4{{'methodA' has been explicitly marked deprecated here}} \ +// expected-note 2{{protocol method is here}} +-(void)methodB __attribute__((unavailable)); // expected-note 4{{'methodB' has been explicitly marked unavailable here}} +-(void)methodC; +@end + +void testAvailabilityP2(id obj) { + [obj methodA]; // expected-warning{{'methodA' is deprecated: first deprecated in OS X 10.2}} + [obj methodB]; // expected-error{{'methodB' is unavailable}} +} + +@interface ImplementsAvailabilityP2a +-(void)methodA; +-(void)methodB; +@end + +void testImplementsAvailabilityP2a(ImplementsAvailabilityP2a *obj) { + [obj methodA]; // okay: availability not inherited + [obj methodB]; // okay: unavailability not inherited +} + +__attribute__((objc_root_class)) +@interface ImplementsAvailabilityP2b +@end + +@implementation ImplementsAvailabilityP2b +-(void)methodA { + // Make sure we're not inheriting availability. + id obj = self; + [obj methodA]; // expected-warning{{'methodA' is deprecated: first deprecated in OS X 10.2}} + [obj methodB]; // expected-error{{'methodB' is unavailable}} +} +-(void)methodB { + // Make sure we're not inheriting unavailability. + id obj = self; + [obj methodA]; // expected-warning{{'methodA' is deprecated: first deprecated in OS X 10.2}} + [obj methodB]; // expected-error{{'methodB' is unavailable}} +} + +@end + +void testImplementsAvailabilityP2b(ImplementsAvailabilityP2b *obj) { + // still get warnings/errors because we see the protocol version. + + [obj methodA]; // expected-warning{{'methodA' is deprecated: first deprecated in OS X 10.2}} + [obj methodB]; // expected-error{{'methodB' is unavailable}} +} + +__attribute__((objc_root_class)) +@interface ImplementsAvailabilityP2c +-(void)methodA __attribute__((availability(macosx,introduced=10.2))); // expected-warning{{method introduced after the protocol method it implements on OS X (10.2 vs. 10.1)}} +-(void)methodB __attribute__((unavailable)); +@end + +__attribute__((objc_root_class)) +@interface ImplementsAvailabilityP2d +@end + +@implementation ImplementsAvailabilityP2d +-(void)methodA __attribute__((availability(macosx,introduced=10.2))) // expected-warning{{method introduced after the protocol method it implements on OS X (10.2 vs. 10.1)}} +{ +} +-(void)methodB __attribute__((unavailable)) { +} +@end