Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -284,20 +284,25 @@ string CustomDiag = customDiag; } -class LangOpt { +class LangOpt { string Name = name; - bit Negated = negated; + + // A custom predicate, written as an expression evaluated in a context with + // "LangOpts" bound. + code CustomCode = customCode; } def MicrosoftExt : LangOpt<"MicrosoftExt">; def Borland : LangOpt<"Borland">; def CUDA : LangOpt<"CUDA">; -def COnly : LangOpt<"CPlusPlus", 1>; +def COnly : LangOpt<"COnly", "!LangOpts.CPlusPlus">; def CPlusPlus : LangOpt<"CPlusPlus">; def OpenCL : LangOpt<"OpenCL">; def RenderScript : LangOpt<"RenderScript">; def ObjC : LangOpt<"ObjC">; def BlocksSupported : LangOpt<"Blocks">; def ObjCAutoRefCount : LangOpt<"ObjCAutoRefCount">; +def ObjCNonFragileRuntime : LangOpt<"ObjCNonFragileRuntime", + "LangOpts.ObjCRuntime.allowsClassStubs()">; // Language option for CMSE extensions def Cmse : LangOpt<"Cmse">; @@ -1806,6 +1811,13 @@ let Documentation = [ObjCRuntimeVisibleDocs]; } +def ObjCClassStub : Attr { + let Spellings = [Clang<"objc_class_stub">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [ObjCClassStubDocs]; + let LangOpts = [ObjCNonFragileRuntime]; +} + def ObjCBoxable : Attr { let Spellings = [Clang<"objc_boxable">]; let Subjects = SubjectList<[Record], ErrorDiag>; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -1085,6 +1085,25 @@ }]; } +def ObjCClassStubDocs : Documentation { + let Category = DocCatType; + let Content = [{ +This attribute specifies that the Objective-C class to which it applies is +instantiated at runtime. + +Unlike ``__attribute__((objc_runtime_visible))``, a class having this attribute +still has a "class stub" that is visible to the linker. This allows categories +to be defined. Static message sends with the class as a receiver use a special +access pattern to ensure the class is lazily instantiated from the class stub. + +Classes annotated with this attribute cannot be subclassed and cannot have +implementations defined for them. This attribute is intended for use in +Swift-generated headers for classes defined in Swift. + +Adding or removing this attribute to a class is an ABI-breaking change. + }]; +} + def ObjCBoxableDocs : Documentation { let Category = DocCatDecl; let Content = [{ Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -895,6 +895,12 @@ def err_restricted_superclass_mismatch : Error< "cannot subclass a class that was declared with the " "'objc_subclassing_restricted' attribute">; +def err_class_stub_subclassing_mismatch : Error< + "'objc_class_stub' attribute cannot be specified on a class that does not " + "have the 'objc_subclassing_restricted' attribute">; +def err_implementation_of_class_stub : Error< + "cannot declare implementation of a class declared with the " + "'objc_class_stub' attribute">; def warn_objc_root_class_missing : Warning< "class %0 defined without specifying a base class">, InGroup; Index: clang/include/clang/Basic/ObjCRuntime.h =================================================================== --- clang/include/clang/Basic/ObjCRuntime.h +++ clang/include/clang/Basic/ObjCRuntime.h @@ -429,6 +429,22 @@ } } + /// Returns true if this Objective-C runtime supports Objective-C class + /// stubs. + bool allowsClassStubs() const { + switch (getKind()) { + case FragileMacOSX: + case GCC: + case GNUstep: + case ObjFW: + return false; + case MacOSX: + case iOS: + case WatchOS: + return true; + } + } + /// Try to parse an Objective-C runtime specification from the given /// string. /// Index: clang/lib/CodeGen/CGObjCMac.cpp =================================================================== --- clang/lib/CodeGen/CGObjCMac.cpp +++ clang/lib/CodeGen/CGObjCMac.cpp @@ -721,6 +721,33 @@ "objc_begin_catch"); } + /// Class objc_loadClassref (void *) + /// + /// Loads from a classref. For Objective-C stub classes, this invokes the + /// initialization callback stored inside the stub. For all other classes + /// this simply dereferences the pointer. + llvm::FunctionCallee getLoadClassrefFn() const { + // Add the non-lazy-bind attribute, since objc_loadClassref is likely to + // be called a lot. + // + // Also it is safe to make it readnone, since we never load or store the + // classref except by calling this function. + llvm::Type *params[] = { Int8PtrPtrTy }; + llvm::FunctionCallee F = CGM.CreateRuntimeFunction( + llvm::FunctionType::get(ClassnfABIPtrTy, params, false), + "objc_loadClassref", + llvm::AttributeList::get(CGM.getLLVMContext(), + llvm::AttributeList::FunctionIndex, + {llvm::Attribute::NonLazyBind, + llvm::Attribute::ReadNone, + llvm::Attribute::NoUnwind})); + if (!CGM.getTriple().isOSBinFormatCOFF()) + cast(F.getCallee())->setLinkage( + llvm::Function::ExternalWeakLinkage); + + return F; + } + llvm::StructType *EHTypeTy; llvm::Type *EHTypePtrTy; @@ -877,6 +904,9 @@ /// DefinedCategories - List of defined categories. SmallVector DefinedCategories; + /// DefinedStubCategories - List of defined categories on class stubs. + SmallVector DefinedStubCategories; + /// DefinedNonLazyCategories - List of defined "non-lazy" categories. SmallVector DefinedNonLazyCategories; @@ -1464,6 +1494,12 @@ bool isMetaclass, ForDefinition_t isForDefinition); + llvm::Constant *GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID); + + llvm::Value *EmitLoadOfClassRef(CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + llvm::GlobalVariable *Entry); + /// EmitClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given class reference. llvm::Value *EmitClassRef(CodeGenFunction &CGF, @@ -1933,7 +1969,7 @@ std::string str = StringClass.empty() ? "OBJC_CLASS_$_NSConstantString" : "OBJC_CLASS_$_" + StringClass; - auto GV = GetClassGlobal(str, NotForDefinition); + llvm::Constant *GV = GetClassGlobal(str, NotForDefinition); // Make sure the result is of the correct type. auto V = llvm::ConstantExpr::getBitCast(GV, CGM.IntTy->getPointerTo()); @@ -6069,6 +6105,9 @@ AddModuleClassList(DefinedCategories, "OBJC_LABEL_CATEGORY_$", GetSectionName("__objc_catlist", "regular,no_dead_strip")); + AddModuleClassList(DefinedStubCategories, "OBJC_LABEL_STUB_CATEGORY_$", + GetSectionName("__objc_catlist2", + "regular,no_dead_strip")); AddModuleClassList(DefinedNonLazyCategories, "OBJC_LABEL_NONLAZY_CATEGORY_$", GetSectionName("__objc_nlcatlist", "regular,no_dead_strip")); @@ -6560,7 +6599,10 @@ llvm::GlobalVariable *GCATV = finishAndCreateGlobal(values, ExtCatName.str(), CGM); CGM.addCompilerUsedGlobal(GCATV); - DefinedCategories.push_back(GCATV); + if (Interface->hasAttr()) + DefinedStubCategories.push_back(GCATV); + else + DefinedCategories.push_back(GCATV); // Determine if this category is also "non-lazy". if (ImplementationIsNonLazy(OCD)) @@ -7236,33 +7278,68 @@ return GV; } +llvm::Constant * +CGObjCNonFragileABIMac::GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID) { + llvm::Constant *ClassGV = GetClassGlobal(ID, /*metaclass*/ false, + NotForDefinition); + + if (!ID->hasAttr()) + return ClassGV; + + ClassGV = llvm::ConstantExpr::getPointerCast(ClassGV, ObjCTypes.Int8PtrTy); + + // Stub classes are pointer-aligned. Classrefs pointing at stub classes + // must set the least significant bit set to 1. + auto *Idx = llvm::ConstantInt::get(CGM.Int32Ty, 1); + return llvm::ConstantExpr::getGetElementPtr(CGM.Int8Ty, ClassGV, Idx); +} + +llvm::Value * +CGObjCNonFragileABIMac::EmitLoadOfClassRef(CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + llvm::GlobalVariable *Entry) { + if (ID && ID->hasAttr()) { + // Classrefs pointing at Objective-C stub classes must be loaded by calling + // a special runtime function. + return CGF.EmitRuntimeCall( + ObjCTypes.getLoadClassrefFn(), Entry, "load_classref_result"); + } + + CharUnits Align = CGF.getPointerAlign(); + return CGF.Builder.CreateAlignedLoad(Entry, Align); +} + llvm::Value * CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, IdentifierInfo *II, const ObjCInterfaceDecl *ID) { - CharUnits Align = CGF.getPointerAlign(); llvm::GlobalVariable *&Entry = ClassReferences[II]; if (!Entry) { llvm::Constant *ClassGV; if (ID) { - ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition); + ClassGV = GetClassGlobalForClassRef(ID); } else { ClassGV = GetClassGlobal((getClassSymbolPrefix() + II->getName()).str(), NotForDefinition); + assert(ClassGV->getType() == ObjCTypes.ClassnfABIPtrTy && + "classref was emitted with the wrong type?"); } std::string SectionName = GetSectionName("__objc_classrefs", "regular,no_dead_strip"); Entry = new llvm::GlobalVariable( - CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, + CGM.getModule(), ClassGV->getType(), false, getLinkageTypeForObjCMetadata(CGM, SectionName), ClassGV, "OBJC_CLASSLIST_REFERENCES_$_"); - Entry->setAlignment(Align.getQuantity()); - Entry->setSection(SectionName); + Entry->setAlignment(CGF.getPointerAlign().getQuantity()); + if (!ID || !ID->hasAttr()) + Entry->setSection(SectionName); + CGM.addCompilerUsedGlobal(Entry); } - return CGF.Builder.CreateAlignedLoad(Entry, Align); + + return EmitLoadOfClassRef(CGF, ID, Entry); } llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CodeGenFunction &CGF, @@ -7284,22 +7361,22 @@ llvm::Value * CGObjCNonFragileABIMac::EmitSuperClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { - CharUnits Align = CGF.getPointerAlign(); llvm::GlobalVariable *&Entry = SuperClassReferences[ID->getIdentifier()]; if (!Entry) { - auto ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition); + llvm::Constant *ClassGV = GetClassGlobalForClassRef(ID); std::string SectionName = GetSectionName("__objc_superrefs", "regular,no_dead_strip"); Entry = new llvm::GlobalVariable( - CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false, + CGM.getModule(), ClassGV->getType(), false, getLinkageTypeForObjCMetadata(CGM, SectionName), ClassGV, "OBJC_CLASSLIST_SUP_REFS_$_"); - Entry->setAlignment(Align.getQuantity()); + Entry->setAlignment(CGF.getPointerAlign().getQuantity()); Entry->setSection(SectionName); CGM.addCompilerUsedGlobal(Entry); } - return CGF.Builder.CreateAlignedLoad(Entry, Align); + + return EmitLoadOfClassRef(CGF, ID, Entry); } /// EmitMetaClassRef - Return a Value * of the address of _class_t Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -6993,6 +6993,9 @@ case ParsedAttr::AT_ObjCSubclassingRestricted: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_ObjCClassStub: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_ObjCExplicitProtocolImpl: handleObjCSuppresProtocolAttr(S, D, AL); break; Index: clang/lib/Sema/SemaDeclObjC.cpp =================================================================== --- clang/lib/Sema/SemaDeclObjC.cpp +++ clang/lib/Sema/SemaDeclObjC.cpp @@ -4061,6 +4061,9 @@ } } + if (IDecl->hasAttr()) + Diag(IC->getLocation(), diag::err_implementation_of_class_stub); + if (LangOpts.ObjCRuntime.isNonFragile()) { while (IDecl->getSuperClass()) { DiagnoseDuplicateIvars(IDecl, IDecl->getSuperClass()); @@ -4089,6 +4092,10 @@ Diag(Super->getLocation(), diag::note_class_declared); } } + + if (IntfDecl->hasAttr() && + !IntfDecl->hasAttr()) + Diag(IntfDecl->getLocation(), diag::err_class_stub_subclassing_mismatch); } DiagnoseVariableSizedIvars(*this, OCD); if (isInterfaceDeclKind) { Index: clang/test/CodeGenObjC/class-stubs.m =================================================================== --- /dev/null +++ clang/test/CodeGenObjC/class-stubs.m @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -emit-llvm -o - %s | FileCheck %s + +// -- classref for the message send in main() +// +// The class is declared with objc_class_stub, so LSB of the class pointer +// must be set to 1. +// +// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = internal global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Base" to i8*), i32 1), align 8 + +// -- classref for the super message send in anotherClassMethod() +// +// Metaclasses do not use the "stub" mechanism and are referenced statically. +// +// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_" = internal global %struct._class_t* @"OBJC_METACLASS_$_Derived", section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 + +// -- classref for the super message send in anotherInstanceMethod() +// +// The class is declared with objc_class_stub, so LSB of the class pointer +// must be set to 1. +// +// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_.1" = internal global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Derived" to i8*), i32 1), section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8 + +// -- category list for class stubs goes in __objc_catlist2. +// +// CHECK-LABEL: @"OBJC_LABEL_STUB_CATEGORY_$" = internal global [1 x i8*] [i8* bitcast (%struct._category_t* @"_OBJC_$_CATEGORY_Derived_$_MyCategory" to i8*)], section "__DATA,__objc_catlist2,regular,no_dead_strip", align 8 + +__attribute__((objc_class_stub)) +__attribute__((objc_subclassing_restricted)) +@interface Base ++ (void) classMethod; +- (void) instanceMethod; +@end + +__attribute__((objc_class_stub)) +__attribute__((objc_subclassing_restricted)) +@interface Derived : Base +@end + +int main() { + [Base classMethod]; +} +// CHECK-LABEL: define i32 @main() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CLASS:%.*]] = call %struct._class_t* @objc_loadClassref(i8** @"OBJC_CLASSLIST_REFERENCES_$_") +// CHECK-NEXT: [[SELECTOR:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ +// CHECK-NEXT: [[RECEIVER:%.*]] = bitcast %struct._class_t* [[CLASS]] to i8* +// CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* [[RECEIVER]], i8* [[SELECTOR]]) +// CHECK-NEXT: ret i32 0 + +// CHECK-LABEL: declare extern_weak %struct._class_t* @objc_loadClassref(i8**) +// CHECK-SAME: [[ATTRLIST:#.*]] + +@implementation Derived (MyCategory) + ++ (void) anotherClassMethod { + [super classMethod]; +} +// CHECK-LABEL: define internal void @"\01+[Derived(MyCategory) anotherClassMethod]"(i8* %self, i8* %_cmd) #0 { +// CHECK-NEXT: entry: +// CHECK: [[SUPER:%.*]] = alloca %struct._objc_super, align 8 +// CHECK: [[METACLASS_REF:%.*]] = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_SUP_REFS_$_", align 8 +// CHECK: [[CAST_METACLASS_REF:%.*]] = bitcast %struct._class_t* [[METACLASS_REF]] to i8* +// CHECK: [[DEST:%.*]] = getelementptr inbounds %struct._objc_super, %struct._objc_super* [[SUPER]], i32 0, i32 1 +// CHECK: store i8* [[CAST_METACLASS_REF]], i8** [[DEST]], align 8 +// CHECK: call void bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper2 to void (%struct._objc_super*, i8*)*)(%struct._objc_super* [[SUPER]], i8* {{%.*}}) +// CHECK: ret void + +- (void) anotherInstanceMethod { + [super instanceMethod]; +} +// CHECK-LABEL: define internal void @"\01-[Derived(MyCategory) anotherInstanceMethod]"(%0* %self, i8* %_cmd) #0 { +// CHECK-NEXT: entry: +// CHECK: [[SUPER:%.*]] = alloca %struct._objc_super, align 8 +// CHECK: [[CLASS_REF:%.*]] = call %struct._class_t* @objc_loadClassref(i8** @"OBJC_CLASSLIST_SUP_REFS_$_.1") +// CHECK: [[CAST_CLASS_REF:%.*]] = bitcast %struct._class_t* [[CLASS_REF]] to i8* +// CHECK: [[DEST:%.*]] = getelementptr inbounds %struct._objc_super, %struct._objc_super* [[SUPER]], i32 0, i32 1 +// CHECK: store i8* [[CAST_CLASS_REF]], i8** [[DEST]], align 8 +// CHECK: call void bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper2 to void (%struct._objc_super*, i8*)*)(%struct._objc_super* [[SUPER]], i8* {{%.*}}) +// CHECK: ret void + +@end + +// -- calls to objc_loadClassRef() are readnone +// CHECK: attributes [[ATTRLIST]] = { nounwind nonlazybind readnone } Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -97,6 +97,7 @@ // CHECK-NEXT: ObjCBridge (SubjectMatchRule_record, SubjectMatchRule_type_alias) // CHECK-NEXT: ObjCBridgeMutable (SubjectMatchRule_record) // CHECK-NEXT: ObjCBridgeRelated (SubjectMatchRule_record) +// CHECK-NEXT: ObjCClassStub (SubjectMatchRule_objc_interface) // CHECK-NEXT: ObjCDesignatedInitializer (SubjectMatchRule_objc_method) // CHECK-NEXT: ObjCException (SubjectMatchRule_objc_interface) // CHECK-NEXT: ObjCExplicitProtocolImpl (SubjectMatchRule_objc_protocol) Index: clang/test/SemaObjC/class-stub-attr-unsupported.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/class-stub-attr-unsupported.m @@ -0,0 +1,10 @@ +// RUN: %clang -target i386-apple-darwin -fsyntax-only -Xclang -verify %s +// RUN: %clang -target i386-apple-darwin -x objective-c++ -fsyntax-only -Xclang -verify %s + +@interface NSObject +@end + +__attribute__((objc_class_stub)) // expected-warning {{'objc_class_stub' attribute ignored}} +__attribute__((objc_subclassing_restricted)) +@interface StubClass : NSObject +@end Index: clang/test/SemaObjC/class-stub-attr.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/class-stub-attr.m @@ -0,0 +1,27 @@ +// RUN: %clang -target x86_64-apple-darwin -fsyntax-only -Xclang -verify %s +// RUN: %clang -target x86_64-apple-darwin -x objective-c++ -fsyntax-only -Xclang -verify %s + +@interface NSObject +@end + +__attribute__((objc_class_stub)) +@interface MissingSubclassingRestrictedAttribute : NSObject // expected-error {{'objc_class_stub' attribute cannot be specified on a class that does not have the 'objc_subclassing_restricted' attribute}} +@end + +__attribute__((objc_class_stub)) +__attribute__((objc_subclassing_restricted)) +@interface ValidClassStubAttribute : NSObject +@end + +@implementation ValidClassStubAttribute // expected-error {{cannot declare implementation of a class declared with the 'objc_class_stub' attribute}} +@end + +@implementation ValidClassStubAttribute (MyCategory) +@end + +__attribute__((objc_class_stub(123))) // expected-error {{'objc_class_stub' attribute takes no arguments}} +@interface InvalidClassStubAttribute : NSObject +@end + +__attribute__((objc_class_stub)) // expected-error {{'objc_class_stub' attribute only applies to Objective-C interfaces}} +int cannotHaveObjCClassStubAttribute() {} Index: clang/utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- clang/utils/TableGen/ClangAttrEmitter.cpp +++ clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1922,6 +1922,30 @@ return true; } +static std::string GenerateTestExpression(ArrayRef LangOpts) { + std::string Test; + + for (auto *E : LangOpts) { + if (!Test.empty()) + Test += " || "; + + const StringRef Code = E->getValueAsString("CustomCode"); + if (!Code.empty()) { + Test += "("; + Test += Code; + Test += ")"; + } else { + Test += "LangOpts."; + Test += E->getValueAsString("Name"); + } + } + + if (Test.empty()) + return "true"; + + return Test; +} + std::string PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, raw_ostream &OS) { @@ -1948,19 +1972,8 @@ // rules if the specific language options are specified. std::vector LangOpts = Rule.getLangOpts(); OS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue() - << ", /*IsSupported=*/"; - if (!LangOpts.empty()) { - for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - const StringRef Part = (*I)->getValueAsString("Name"); - if ((*I)->getValueAsBit("Negated")) - OS << "!"; - OS << "LangOpts." << Part; - if (I + 1 != E) - OS << " || "; - } - } else - OS << "true"; - OS << "));\n"; + << ", /*IsSupported=*/" << GenerateTestExpression(LangOpts) + << "));\n"; } } OS << "}\n\n"; @@ -3431,23 +3444,12 @@ if (LangOpts.empty()) return "defaultDiagnoseLangOpts"; - // Generate the test condition, as well as a unique function name for the - // diagnostic test. The list of options should usually be short (one or two - // options), and the uniqueness isn't strictly necessary (it is just for - // codegen efficiency). - std::string FnName = "check", Test; - for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - const StringRef Part = (*I)->getValueAsString("Name"); - if ((*I)->getValueAsBit("Negated")) { - FnName += "Not"; - Test += "!"; - } - Test += "S.LangOpts."; - Test += Part; - if (I + 1 != E) - Test += " || "; - FnName += Part; - } + // Generate a unique function name for the diagnostic test. The list of + // options should usually be short (one or two options), and the + // uniqueness isn't strictly necessary (it is just for codegen efficiency). + std::string FnName = "check"; + for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) + FnName += (*I)->getValueAsString("Name"); FnName += "LangOpts"; // If this code has already been generated, simply return the previous @@ -3458,7 +3460,8 @@ return *I; OS << "static bool " << FnName << "(Sema &S, const ParsedAttr &Attr) {\n"; - OS << " if (" << Test << ")\n"; + OS << " auto &LangOpts = S.LangOpts;\n"; + OS << " if (" << GenerateTestExpression(LangOpts) << ")\n"; OS << " return true;\n\n"; OS << " S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) "; OS << "<< Attr.getName();\n";