]> granicus.if.org Git - clang/commitdiff
Add the ability to define "fake" arguments on attributes.
authorJohn McCall <rjmccall@apple.com>
Wed, 28 Oct 2015 00:17:34 +0000 (00:17 +0000)
committerJohn McCall <rjmccall@apple.com>
Wed, 28 Oct 2015 00:17:34 +0000 (00:17 +0000)
Fake arguments are automatically handled for serialization, cloning,
and other representational tasks, but aren't included in pretty-printing
or parsing (should we eventually ever automate that).

This is chiefly useful for attributes that can be written by the
user, but which are also frequently synthesized by the compiler,
and which we'd like to remember details of the synthesis for.
As a simple example, use this to narrow the cases in which we were
generating a specialized note for implicitly unavailable declarations.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@251469 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/Attr.td
lib/Sema/SemaDeclAttr.cpp
test/SemaObjC/arc-system-header.m
test/SemaObjCXX/arc-system-header.mm
utils/TableGen/ClangAttrEmitter.cpp

index bed9218adaa73252c98916e6af42045b9028f152..a0484ea2f1ac2eabd72660968759cb7faa9dbc66 100644 (file)
@@ -129,9 +129,10 @@ def HasFunctionProto : SubsetSubject<DeclBase,
                                        isa<BlockDecl>(S)}]>;
 
 // A single argument to an attribute
-class Argument<string name, bit optional> {
+class Argument<string name, bit optional, bit fake = 0> {
   string Name = name;
   bit Optional = optional;
+  bit Fake = fake;
 }
 
 class BoolArgument<string name, bit opt = 0> : Argument<name, opt>;
@@ -167,7 +168,8 @@ class DefaultIntArgument<string name, int default> : IntArgument<name, 1> {
 // This argument is more complex, it includes the enumerator type name,
 // a list of strings to accept, and a list of enumerators to map them to.
 class EnumArgument<string name, string type, list<string> values,
-                   list<string> enums, bit opt = 0> : Argument<name, opt> {
+                   list<string> enums, bit opt = 0, bit fake = 0>
+    : Argument<name, opt, fake> {
   string Type = type;
   list<string> Values = values;
   list<string> Enums = enums;
@@ -1350,7 +1352,10 @@ def TransparentUnion : InheritableAttr {
 
 def Unavailable : InheritableAttr {
   let Spellings = [GNU<"unavailable">];
-  let Args = [StringArgument<"Message", 1>];
+  let Args = [StringArgument<"Message", 1>,
+              EnumArgument<"ImplicitSource", "ImplicitSourceKind",
+                           ["none", "forbiddenType"],
+                           ["ISK_None", "ISK_ForbiddenType"], 1, /*fake*/ 1>];
   let Documentation = [Undocumented];
 }
 
index 51f9e45d7e2ca3c7bbfd0bba8df6e47664f4ba4d..1f90b75e57cacd42d068fc6d53f43f122d60d192 100644 (file)
@@ -5378,7 +5378,7 @@ static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag,
   llvm::StringRef explanation;
   if (decl && isForbiddenTypeAllowed(S, decl, diag, explanation)) {
     decl->addAttr(UnavailableAttr::CreateImplicit(S.Context, explanation,
-                                                  diag.Loc));
+                                UnavailableAttr::ISK_ForbiddenType, diag.Loc));
     return;
   }
   if (S.getLangOpts().ObjCAutoRefCount)
@@ -5464,7 +5464,8 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K,
 
     if (!Message.empty()) {
       if (auto attr = D->getAttr<UnavailableAttr>())
-        if (attr->isImplicit())
+        if (attr->isImplicit() &&
+            attr->getImplicitSource() == UnavailableAttr::ISK_ForbiddenType)
           diag_available_here = diag::note_unavailability_inferred_here;
     }
 
index 85022bb9eefee62ce541723d8b3318473b0c2097..a627b3be9bb3b45ff12447afc93ad72b940c275f 100644 (file)
@@ -7,8 +7,8 @@
 void test(id op, void *cp) {
   cp = test0(op); // expected-error {{'test0' is unavailable: converts between Objective-C and C pointers in -fobjc-arc}}
   cp = *test1(&op); // expected-error {{'test1' is unavailable: converts between Objective-C and C pointers in -fobjc-arc}}
-// expected-note@arc-system-header.h:1 {{unsupported declaration here}}
-// expected-note@arc-system-header.h:5 {{unsupported declaration here}}
+// expected-note@arc-system-header.h:1 {{'test0' has been explicitly marked unavailable here}}
+// expected-note@arc-system-header.h:5 {{'test1' has been explicitly marked unavailable here}}
 }
 
 void test3(struct Test3 *p) {
@@ -24,7 +24,7 @@ void test4(Test4 *p) {
 
 void test5(struct Test5 *p) {
   p->field = 0; // expected-error {{'field' is unavailable: this system field has retaining ownership}}
-                // expected-note@arc-system-header.h:25 {{unsupported declaration here}}
+                // expected-note@arc-system-header.h:25 {{'field' has been explicitly marked unavailable here}}
 }
 
 id test6() {
index 44b1cd77ba8937c495abdbd40fe29f8c2e8c9f5a..b97e2f3b1eb4c2a32e25216954db61d7f0fc5177 100644 (file)
@@ -6,4 +6,4 @@ void f(A* a) {
   a->data.void_ptr = 0;
   a->data.a_b.b = 0; // expected-error{{'a_b' is unavailable: this system field has retaining ownership}}
 }
-// expected-note@arc-system-header.h:10{{unsupported declaration here}}
+// expected-note@arc-system-header.h:10{{'a_b' has been explicitly marked unavailable here}}
index d3e67ba91bb9548df55fb3fdf62d2a0d2acab66d..0c2857649811c517ad45032fc106bebd41f72a5c 100644 (file)
@@ -166,11 +166,12 @@ namespace {
     std::string lowerName, upperName;
     StringRef attrName;
     bool isOpt;
+    bool Fake;
 
   public:
     Argument(const Record &Arg, StringRef Attr)
       : lowerName(Arg.getValueAsString("Name")), upperName(lowerName),
-        attrName(Attr), isOpt(false) {
+        attrName(Attr), isOpt(false), Fake(false) {
       if (!lowerName.empty()) {
         lowerName[0] = std::tolower(lowerName[0]);
         upperName[0] = std::toupper(upperName[0]);
@@ -185,6 +186,9 @@ namespace {
     bool isOptional() const { return isOpt; }
     void setOptional(bool set) { isOpt = set; }
 
+    bool isFake() const { return Fake; }
+    void setFake(bool fake) { Fake = fake; }
+
     // These functions print the argument contents formatted in different ways.
     virtual void writeAccessors(raw_ostream &OS) const = 0;
     virtual void writeAccessorDefinitions(raw_ostream &OS) const {}
@@ -1078,6 +1082,9 @@ createArgument(const Record &Arg, StringRef Attr,
   if (Ptr && Arg.getValueAsBit("Optional"))
     Ptr->setOptional(true);
 
+  if (Ptr && Arg.getValueAsBit("Fake"))
+    Ptr->setFake(true);
+
   return Ptr;
 }
 
@@ -1186,23 +1193,33 @@ writePrettyPrintFunction(Record &R,
       continue;
     }
 
+    // Fake arguments aren't part of the parsed form and should not be
+    // pretty-printed.
+    bool hasNonFakeArgs = false;
+    for (const auto &arg : Args) {
+      if (arg->isFake()) continue;
+      hasNonFakeArgs = true;
+    }
+
     // FIXME: always printing the parenthesis isn't the correct behavior for
     // attributes which have optional arguments that were not provided. For
     // instance: __attribute__((aligned)) will be pretty printed as
     // __attribute__((aligned())). The logic should check whether there is only
     // a single argument, and if it is optional, whether it has been provided.
-    if (!Args.empty())
+    if (hasNonFakeArgs)
       OS << "(";
     if (Spelling == "availability") {
       writeAvailabilityValue(OS);
     } else {
-      for (auto I = Args.begin(), E = Args.end(); I != E; ++ I) {
-        if (I != Args.begin()) OS << ", ";
-        (*I)->writeValue(OS);
+      unsigned index = 0;
+      for (const auto &arg : Args) {
+        if (arg->isFake()) continue;
+        if (index++) OS << ", ";
+        arg->writeValue(OS);
       }
     }
 
-    if (!Args.empty())
+    if (hasNonFakeArgs)
       OS << ")";
     OS << Suffix + "\";\n";
 
@@ -1474,10 +1491,19 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
     std::vector<std::unique_ptr<Argument>> Args;
     Args.reserve(ArgRecords.size());
 
+    bool HasOptArg = false;
+    bool HasFakeArg = false;
     for (const auto *ArgRecord : ArgRecords) {
       Args.emplace_back(createArgument(*ArgRecord, R.getName()));
       Args.back()->writeDeclarations(OS);
       OS << "\n\n";
+
+      // For these purposes, fake takes priority over optional.
+      if (Args.back()->isFake()) {
+        HasFakeArg = true;
+      } else if (Args.back()->isOptional()) {
+        HasOptArg = true;
+      }
     }
 
     OS << "\npublic:\n";
@@ -1496,69 +1522,53 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
     if (!ElideSpelling)
       OS << CreateSemanticSpellings(Spellings, SemanticToSyntacticMap);
 
-    OS << "  static " << R.getName() << "Attr *CreateImplicit(";
-    OS << "ASTContext &Ctx";
-    if (!ElideSpelling)
-      OS << ", Spelling S";
-    for (auto const &ai : Args) {
-      OS << ", ";
-      ai->writeCtorParameters(OS);
-    }
-    OS << ", SourceRange Loc = SourceRange()";
-    OS << ") {\n";
-    OS << "    " << R.getName() << "Attr *A = new (Ctx) " << R.getName();
-    OS << "Attr(Loc, Ctx, ";
-    for (auto const &ai : Args) {
-      ai->writeImplicitCtorArgs(OS);
-      OS << ", ";
-    }
-    OS << (ElideSpelling ? "0" : "S") << ");\n";
-    OS << "    A->setImplicit(true);\n";
-    OS << "    return A;\n  }\n\n";
-
-    OS << "  " << R.getName() << "Attr(SourceRange R, ASTContext &Ctx\n";
-    
-    bool HasOpt = false;
-    for (auto const &ai : Args) {
-      OS << "              , ";
-      ai->writeCtorParameters(OS);
-      OS << "\n";
-      if (ai->isOptional())
-        HasOpt = true;
-    }
-
-    OS << "              , ";
-    OS << "unsigned SI\n";
+    // Emit CreateImplicit factory methods.
+    auto emitCreateImplicit = [&](bool emitFake) {
+      OS << "  static " << R.getName() << "Attr *CreateImplicit(";
+      OS << "ASTContext &Ctx";
+      if (!ElideSpelling)
+        OS << ", Spelling S";
+      for (auto const &ai : Args) {
+        if (ai->isFake() && !emitFake) continue;
+        OS << ", ";
+        ai->writeCtorParameters(OS);
+      }
+      OS << ", SourceRange Loc = SourceRange()";
+      OS << ") {\n";
+      OS << "    " << R.getName() << "Attr *A = new (Ctx) " << R.getName();
+      OS << "Attr(Loc, Ctx, ";
+      for (auto const &ai : Args) {
+        if (ai->isFake() && !emitFake) continue;
+        ai->writeImplicitCtorArgs(OS);
+        OS << ", ";
+      }
+      OS << (ElideSpelling ? "0" : "S") << ");\n";
+      OS << "    A->setImplicit(true);\n";
+      OS << "    return A;\n  }\n\n";
+    };
 
-    OS << "             )\n";
-    OS << "    : " << SuperName << "(attr::" << R.getName() << ", R, SI, "
-       << R.getValueAsBit("LateParsed") << ", "
-       << R.getValueAsBit("DuplicatesAllowedWhileMerging") << ")\n";
+    // Emit a CreateImplicit that takes all the arguments.
+    emitCreateImplicit(true);
 
-    for (auto const &ai : Args) {
-      OS << "              , ";
-      ai->writeCtorInitializers(OS);
-      OS << "\n";
+    // Emit a CreateImplicit that takes all the non-fake arguments.
+    if (HasFakeArg) {
+      emitCreateImplicit(false);
     }
 
-    OS << "  {\n";
-  
-    for (auto const &ai : Args) {
-      ai->writeCtorBody(OS);
-      OS << "\n";
-    }
-    OS << "  }\n\n";
+    // Emit constructors.
+    auto emitCtor = [&](bool emitOpt, bool emitFake) {
+      auto shouldEmitArg = [=](const std::unique_ptr<Argument> &arg) {
+        if (arg->isFake()) return emitFake;
+        if (arg->isOptional()) return emitOpt;
+        return true;
+      };
 
-    // If there are optional arguments, write out a constructor that elides the
-    // optional arguments as well.
-    if (HasOpt) {
       OS << "  " << R.getName() << "Attr(SourceRange R, ASTContext &Ctx\n";
       for (auto const &ai : Args) {
-        if (!ai->isOptional()) {
-          OS << "              , ";
-          ai->writeCtorParameters(OS);
-          OS << "\n";
-        }
+        if (!shouldEmitArg(ai)) continue;
+        OS << "              , ";
+        ai->writeCtorParameters(OS);
+        OS << "\n";
       }
 
       OS << "              , ";
@@ -1571,19 +1581,37 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
 
       for (auto const &ai : Args) {
         OS << "              , ";
-        ai->writeCtorDefaultInitializers(OS);
+        if (!shouldEmitArg(ai)) {
+          ai->writeCtorDefaultInitializers(OS);
+        } else {
+          ai->writeCtorInitializers(OS);
+        }
         OS << "\n";
       }
 
       OS << "  {\n";
   
       for (auto const &ai : Args) {
-        if (!ai->isOptional()) {
-          ai->writeCtorBody(OS);
-          OS << "\n";
-        }
+        if (!shouldEmitArg(ai)) continue;
+        ai->writeCtorBody(OS);
+        OS << "\n";
       }
       OS << "  }\n\n";
+
+    };
+
+    // Emit a constructor that includes all the arguments.
+    // This is necessary for cloning.
+    emitCtor(true, true);
+
+    // Emit a constructor that takes all the non-fake arguments.
+    if (HasFakeArg) {
+      emitCtor(true, false);
+    }
+    // Emit a constructor that takes all the non-fake, non-optional arguments.
+    if (HasOptArg) {
+      emitCtor(false, false);
     }
 
     OS << "  " << R.getName() << "Attr *clone(ASTContext &C) const;\n";
@@ -1605,6 +1633,9 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) {
       ai->writeAccessors(OS);
       OS << "\n\n";
 
+      // Don't write conversion routines for fake arguments.
+      if (ai->isFake()) continue;
+
       if (ai->isEnumArg())
         static_cast<const EnumArgument *>(ai.get())->writeConversion(OS);
       else if (ai->isVariadicEnumArg())