Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2912,6 +2912,10 @@ "%select{the protocol method it implements|its overridden method}1 is " "available">, InGroup; +def warn_availability_on_static_initializer : Warning< + "ignoring availability attribute %select{on '+load' method|" + "with constructor attribute|with destructor attribute}0">, + InGroup; def note_overridden_method : Note< "overridden method is here">; def note_protocol_method : Note< Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -9139,6 +9139,21 @@ AddToScope = false; } + // Diagnose availability attributes. Availability cannot be used on functions + // that are run during load/unload. + if (const auto *attr = NewFD->getAttr()) { + if (NewFD->hasAttr()) { + Diag(attr->getLocation(), diag::warn_availability_on_static_initializer) + << 1; + NewFD->dropAttr(); + } + if (NewFD->hasAttr()) { + Diag(attr->getLocation(), diag::warn_availability_on_static_initializer) + << 2; + NewFD->dropAttr(); + } + } + return NewFD; } Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -6823,6 +6823,12 @@ return false; // An implementation implicitly has the availability of the interface. + // Unless it is "+load" method. + if (const auto *MethodD = dyn_cast(Ctx)) + if (MethodD->isClassMethod() && + MethodD->getSelector().getAsString() == "load") + return true; + if (const auto *CatOrImpl = dyn_cast(Ctx)) { if (const ObjCInterfaceDecl *Interface = CatOrImpl->getClassInterface()) if (CheckContext(Interface)) Index: lib/Sema/SemaDeclObjC.cpp =================================================================== --- lib/Sema/SemaDeclObjC.cpp +++ lib/Sema/SemaDeclObjC.cpp @@ -4734,6 +4734,17 @@ Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86) checkObjCMethodX86VectorTypes(*this, ObjCMethod); + // + load method cannot have availability attributes. It get called on + // startup, so it has to have the availability of the deployment target. + if (const auto *attr = ObjCMethod->getAttr()) { + if (ObjCMethod->isClassMethod() && + ObjCMethod->getSelector().getAsString() == "load") { + Diag(attr->getLocation(), diag::warn_availability_on_static_initializer) + << 0; + ObjCMethod->dropAttr(); + } + } + ActOnDocumentableDecl(ObjCMethod); return ObjCMethod; Index: test/SemaObjC/unguarded-availability.m =================================================================== --- test/SemaObjC/unguarded-availability.m +++ test/SemaObjC/unguarded-availability.m @@ -7,7 +7,7 @@ typedef int AVAILABLE_10_12 new_int; // expected-note + {{marked partial here}} -int func_10_11() AVAILABLE_10_11; // expected-note 4 {{'func_10_11' has been explicitly marked partial here}} +int func_10_11() AVAILABLE_10_11; // expected-note 8 {{'func_10_11' has been explicitly marked partial here}} #ifdef OBJCPP // expected-note@+2 6 {{marked partial here}} @@ -311,3 +311,45 @@ (void)AK_Cat; // no warning (void)AK_CyborgCat; // expected-warning{{'AK_CyborgCat' is only available on macOS 10.12 or newer}} expected-note {{@available}} } + + +// test static initializers has the same availability as the deployment target and it cannot be overwritten. +@interface HasStaticInitializer : BaseClass ++ (void)load AVAILABLE_10_11; // expected-warning{{ignoring availability attribute on '+load' method}} +@end + +@implementation HasStaticInitializer ++ (void)load { + func_10_11(); // expected-warning{{'func_10_11' is only available on macOS 10.11 or newer}} expected-note{{enclose 'func_10_11' in an @available check to silence this warning}} +} +@end + +// test availability from interface is ignored when checking the unguarded availability in +load method. +AVAILABLE_10_11 +@interface HasStaticInitializer1 : BaseClass ++ (void)load; ++ (void)load: (int)x; // no warning. +@end + +@implementation HasStaticInitializer1 ++ (void)load { + func_10_11(); // expected-warning{{'func_10_11' is only available on macOS 10.11 or newer}} expected-note{{enclose 'func_10_11' in an @available check to silence this warning}} +} ++ (void)load: (int)x { + func_10_11(); // no warning. +} +@end + +__attribute__((constructor)) +void is_constructor(); + +AVAILABLE_10_11 // expected-warning{{ignoring availability attribute with constructor attribute}} +void is_constructor() { + func_10_11(); // expected-warning{{'func_10_11' is only available on macOS 10.11 or newer}} expected-note{{enclose 'func_10_11' in an @available check to silence this warning}} +} + +AVAILABLE_10_11 // expected-warning{{ignoring availability attribute with destructor attribute}} +__attribute__((destructor)) +void is_destructor() { + func_10_11(); // expected-warning{{'func_10_11' is only available on macOS 10.11 or newer}} expected-note{{enclose 'func_10_11' in an @available check to silence this warning}} +}