[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-03-20 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov created this revision.
slavapestov added reviewers: rjmccall, dexonsmith, aaron.ballman.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
dexonsmith edited reviewers, added: erik.pilkington, arphaman; removed: 
dexonsmith.
dexonsmith added a comment.

+Erik and Alex, can you take a look at this?


This CL lands the Clang-side support for a new Objective-C runtime feature that 
adds support for dynamically-allocated metadata.

What follows is a basic description of this mechanism:

In Swift, the class metadata contains an Objective-C class object as a prefix, 
followed by a vtable, generic parameters, and field offsets. If the inheritance 
hierarchy of a class crosses a resilience boundary, we cannot statically emit 
the entire metadata. Instead, it is lazily built at runtime when first needed.

In order for such classes to be accessed from Clang, the Objective-C runtime 
will support a new mechanism whereby Swift can instead emit a "class stub" 
object. The class stub object is used by Clang in places where a class 
reference is normally emitted:

- Classrefs emitted when messaging the class
- Categories

A class stub contains an "isa pointer" which is a scalar value 1 followed by a 
function pointer which realizes the real class metadata. A reference to a class 
stub from a classref also has its least significant pointer set to 1.

The Objective-C runtime distinguishes a class stub from a real class using this 
fake "isa pointer". Instead of directly loading from a classref, Clang instead 
calls `objc_loadClassref()`; this function checks if the classref contains a 
class stub, and if so, it calls the function pointer and stores the result back 
into the classref to fast path future loads.

Note that Clang already supports a objc_runtime_visible attribute. The 
objc_class_stub attribute can be thought of as a generalization of 
objc_runtime_visible. Whereas objc_runtime_visible replaces the classref load 
with a runtime lookup of a class by string, the class stub mechanism is more 
efficient because the result of the lookup is cached. Also, 
objc_runtime_visible does not support categories to be defined on the class, 
whereas objc_class_stub does.


Repository:
  rC Clang

https://reviews.llvm.org/D59628

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  lib/CodeGen/CGObjCMac.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/CodeGenObjC/class-stubs.m

Index: test/CodeGenObjC/class-stubs.m
===
--- /dev/null
+++ test/CodeGenObjC/class-stubs.m
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -emit-llvm -o - %s | \
+// RUN: FileCheck %s
+
+__attribute__((objc_class_stub))
+@interface A
++ (void) classMethod;
+@end
+
+int main() {
+  [A classMethod];
+}
+
+// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = private global i8* inttoptr (i64 add (i64 ptrtoint (%struct._class_t* @"OBJC_CLASS_$_A" to i64), i64 1) to i8*), section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8
+
+
+// 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
+
Index: lib/Sema/SemaDeclAttr.cpp
===
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -7123,6 +7123,9 @@
   case ParsedAttr::AT_ObjCSubclassingRestricted:
 handleSimpleAttribute(S, D, AL);
 break;
+  case ParsedAttr::AT_ObjCClassStub:
+handleSimpleAttribute(S, D, AL);
+break;
   case ParsedAttr::AT_ObjCCompleteDefinition:
 handleSimpleAttribute(S, D, AL);
 break;
Index: lib/CodeGen/CGObjCMac.cpp
===
--- lib/CodeGen/CGObjCMac.cpp
+++ lib/CodeGen/CGObjCMac.cpp
@@ -726,6 +726,25 @@
  "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::Constant *getLoadClassrefFn() const {
+// FIXME: Other attributes?
+
+// Add the non-lazy-bind attribute, since objc_loadClassref is likely to
+// be called a lot.
+llvm::Type *params[] = { Int8PtrPtrTy };
+return CGM.CreateRuntimeFunction(
+llvm::FunctionType::get(ClassnfABIPtrTy, params, false),
+"objc_loadClassref",
+llvm::AttributeList::get(CGM.getLLVMContext(),
+ llvm::At

[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-03-26 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov marked 11 inline comments as done.
slavapestov added a comment.

Thanks for the comments everyone. In the next version of the patch, I fixed 
most of the review feedback and also addressed an issue where super message 
sends were not calling `objc_loadClassRef()`.




Comment at: include/clang/Basic/Attr.td:1802
+  let Documentation = [ObjCClassStubDocs];
+}
+

aaron.ballman wrote:
> Does this attribute make sense outside of ObjC? e.g., should it require an 
> ObjC compiland? If it should only be used in ObjC, then it should have a 
> `LangOpts` field.
The other ObjC attributes, such as ObjCSubclassingRestricted, do not have a 
LangOpts. Do you want me to add a LangOpts field to those too? Or is it 
unnecessary since they're already restricted to InterfaceDecls?



Comment at: include/clang/Basic/AttrDocs.td:1116
+def ObjCClassStubDocs : Documentation {
+let Category = DocCatFunction;
+let Content = [{

aaron.ballman wrote:
> This seems like the wrong category -- the attribute doesn't apply to 
> functions.
Would DocCatType make more sense? Would you like me to change 
ObjCRuntimeVisible and a handful of other miscategorized attributes too?



Comment at: lib/CodeGen/CGObjCMac.cpp:7274
+// Classrefs pointing at Objective-C stub classes have the least
+// significant bit set to 1.
+auto *Tag = llvm::ConstantInt::get(CGM.IntPtrTy, 1);

rjmccall wrote:
> This isn't for an arbitrary class ref, it's for the global class list.  I'd 
> say something like "the global class list sets the LSB to 1 on any class 
> stubs".
Can you explain what the difference is? My understanding is that the thing you 
pass to objc_loadClassref() (or load from directly) is a "classref". This is 
for classes you reference, not classes you define.



Comment at: lib/CodeGen/CGObjCMac.cpp:7285
 
-Entry = new llvm::GlobalVariable(CGM.getModule(), 
ObjCTypes.ClassnfABIPtrTy,
+Entry = new llvm::GlobalVariable(CGM.getModule(), ClassGV->getType(),
  false, llvm::GlobalValue::PrivateLinkage,

jordan_rose wrote:
> Is there a concern here in the non-stub case if GetClassGlobal no longer 
> produces a ObjCTypes.ClassnfABIPtrTy? (Probably not, but thought I'd check 
> [again].)
You raise a good point. I brought back the assert in the next iteration of the 
patch.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-03-26 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov updated this revision to Diff 192378.
slavapestov marked 2 inline comments as done.
slavapestov added a comment.

Second revision adds the following:

- Validation of the attribute in Sema
- Sema tests for the above
- Correct behavior of super method calls of a class with the attribute
- More comprehensive CodeGen tests


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/ObjCRuntime.h
  lib/CodeGen/CGObjCMac.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaDeclObjC.cpp
  test/CodeGenObjC/class-stubs.m
  test/Misc/pragma-attribute-supported-attributes-list.test
  test/SemaObjC/class-stub-attr-unsupported.m
  test/SemaObjC/class-stub-attr.m

Index: test/SemaObjC/class-stub-attr.m
===
--- /dev/null
+++ 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: test/SemaObjC/class-stub-attr-unsupported.m
===
--- /dev/null
+++ 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))
+__attribute__((objc_subclassing_restricted))
+@interface StubClass : NSObject // expected-error {{'objc_class_stub' attribute is not supported for this target}}
+@end
Index: test/Misc/pragma-attribute-supported-attributes-list.test
===
--- test/Misc/pragma-attribute-supported-attributes-list.test
+++ test/Misc/pragma-attribute-supported-attributes-list.test
@@ -95,6 +95,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: ObjCCompleteDefinition (SubjectMatchRule_objc_interface)
 // CHECK-NEXT: ObjCDesignatedInitializer (SubjectMatchRule_objc_method)
 // CHECK-NEXT: ObjCException (SubjectMatchRule_objc_interface)
Index: test/CodeGenObjC/class-stubs.m
===
--- /dev/null
+++ test/CodeGenObjC/class-stubs.m
@@ -0,0 +1,81 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -emit-llvm -o - %s | \
+// RUN: 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_$_" = private global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Base" to i8*), i32 1), section "__DATA,__objc_classrefs,regular,no_dead_strip", 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_$_" = private 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" = private 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
+
+__attribute__((objc_class_stub))
+__attribute__((objc_subclassing_restricted))
+@interface 

[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-03-28 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov added a comment.

I don't know what the etiquette is around here about pinging reviewers for a 
re-review, but this CL is ready for another look. Your feedback is much 
appreciated!


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-04-02 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov updated this revision to Diff 193407.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/ObjCRuntime.h
  lib/CodeGen/CGObjCMac.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaDeclObjC.cpp
  test/CodeGenObjC/class-stubs.m
  test/Misc/pragma-attribute-supported-attributes-list.test
  test/SemaObjC/class-stub-attr-unsupported.m
  test/SemaObjC/class-stub-attr.m
  utils/TableGen/ClangAttrEmitter.cpp

Index: utils/TableGen/ClangAttrEmitter.cpp
===
--- utils/TableGen/ClangAttrEmitter.cpp
+++ utils/TableGen/ClangAttrEmitter.cpp
@@ -1969,10 +1969,15 @@
  << ", /*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;
+  const StringRef Code = (*I)->getValueAsString("CustomCode");
+  if (!Code.empty()) {
+OS << Code;
+  } else {
+const StringRef Name = (*I)->getValueAsString("Name");
+OS << "LangOpts." << Name;
+  }
   if (I + 1 != E)
 OS << " || ";
 }
@@ -2957,15 +2962,15 @@
 OS << "case AttrSyntax::" << Variety << ": {\n";
 // C++11-style attributes are further split out based on the Scope.
 for (auto I = List.cbegin(), E = List.cend(); I != E; ++I) {
-  if (I != List.cbegin())
-OS << " else ";
-  if (I->first.empty())
-OS << "if (ScopeName == \"\") {\n";
-  else
-OS << "if (ScopeName == \"" << I->first << "\") {\n";
-  OS << "  return llvm::StringSwitch(Name)\n";
-  GenerateHasAttrSpellingStringSwitch(I->second, OS, Spelling, I->first);
-  OS << "}";
+  if (I != List.cbegin())
+OS << " else ";
+  if (I->first.empty())
+OS << "if (ScopeName == \"\") {\n";
+  else
+OS << "if (ScopeName == \"" << I->first << "\") {\n";
+  OS << "  return llvm::StringSwitch(Name)\n";
+  GenerateHasAttrSpellingStringSwitch(I->second, OS, Spelling, I->first);
+  OS << "}";
 }
 OS << "\n} break;\n";
   };
@@ -3426,16 +3431,22 @@
   // 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;
+const StringRef Name = (*I)->getValueAsString("Name");
+FnName += Name;
+const StringRef Code = (*I)->getValueAsString("CustomCode");
+if (!Code.empty()) {
+  Test += "S.";
+  Test += Code;
+} else {
+  Test += "S.LangOpts.";
+  Test += Name;
+}
 if (I + 1 != E)
   Test += " || ";
-FnName += Part;
   }
   FnName += "LangOpts";
 
Index: test/SemaObjC/class-stub-attr.m
===
--- /dev/null
+++ 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: test/SemaObjC/class-stub-attr-unsupported.m
===
--- /dev/null
+++ 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_su

[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-05-16 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov updated this revision to Diff 199885.
slavapestov added a comment.

Updated patch to address review feedback.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/ObjCRuntime.h
  clang/lib/CodeGen/CGObjCMac.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclObjC.cpp
  clang/test/CodeGenObjC/class-stubs.m
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/SemaObjC/class-stub-attr-unsupported.m
  clang/test/SemaObjC/class-stub-attr.m
  clang/utils/TableGen/ClangAttrEmitter.cpp

Index: clang/utils/TableGen/ClangAttrEmitter.cpp
===
--- clang/utils/TableGen/ClangAttrEmitter.cpp
+++ clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -1922,6 +1922,34 @@
   return true;
 }
 
+static std::string GenerateTestExpression(ArrayRef LangOpts) {
+  std::string Test;
+
+  for (auto *E : LangOpts) {
+if (!Test.empty())
+  Test += " || ";
+
+if (E->getValueAsBit("Negated")) {
+  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 +1976,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,22 +3448,14 @@
   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;
+  // 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) {
-const StringRef Part = (*I)->getValueAsString("Name");
-if ((*I)->getValueAsBit("Negated")) {
+if ((*I)->getValueAsBit("Negated"))
   FnName += "Not";
-  Test += "!";
-}
-Test += "S.LangOpts.";
-Test +=  Part;
-if (I + 1 != E)
-  Test += " || ";
-FnName += Part;
+FnName += (*I)->getValueAsString("Name");
   }
   FnName += "LangOpts";
 
@@ -3458,7 +3467,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";
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_s

[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-05-16 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov updated this revision to Diff 199905.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/ObjCRuntime.h
  clang/lib/CodeGen/CGObjCMac.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclObjC.cpp
  clang/test/CodeGenObjC/class-stubs.m
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/SemaObjC/class-stub-attr-unsupported.m
  clang/test/SemaObjC/class-stub-attr.m
  clang/utils/TableGen/ClangAttrEmitter.cpp

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";
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_clas

[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-05-22 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov updated this revision to Diff 200808.
slavapestov marked 6 inline comments as done.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/ObjCRuntime.h
  clang/lib/CodeGen/CGObjCMac.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclObjC.cpp
  clang/test/CodeGenObjC/class-stubs.m
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/SemaObjC/class-stub-attr-unsupported.m
  clang/test/SemaObjC/class-stub-attr.m
  clang/utils/TableGen/ClangAttrEmitter.cpp

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";
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_cl

[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-05-29 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov updated this revision to Diff 202084.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/ObjCRuntime.h
  clang/lib/CodeGen/CGObjCMac.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclObjC.cpp
  clang/test/CodeGenObjC/class-stubs.m
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/SemaObjC/class-stub-attr-unsupported.m
  clang/test/SemaObjC/class-stub-attr.m
  clang/utils/TableGen/ClangAttrEmitter.cpp

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";
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_clas

[PATCH] D59628: Add support for __attribute__((objc_class_stub))

2019-05-29 Thread Slava Pestov via Phabricator via cfe-commits
slavapestov marked 2 inline comments as done.
slavapestov added inline comments.



Comment at: clang/include/clang/Basic/Attr.td:297
 def CUDA : LangOpt<"CUDA">;
-def COnly : LangOpt<"CPlusPlus", 1>;
+def COnly : LangOpt<"COnly", "!LangOpts.CPlusPlus">;
 def CPlusPlus : LangOpt<"CPlusPlus">;

aaron.ballman wrote:
> This compiles? I would have assumed that it would have to be `def COnly : 
> LangOpt<"COnly", [{!LangOpts.CPlusPlus}]>;`
> 
> (If it compiles as-is, that's fine, I just wasn't aware that you could use 
> string syntax for code blocks.)
It appears that both work! Going with your suggested version.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59628/new/

https://reviews.llvm.org/D59628



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits