From 68098861a71f77762ab84bee3343d296af507331 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 28 Oct 2015 00:17:34 +0000 Subject: [PATCH] Add the ability to define "fake" arguments on attributes. 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 | 11 +- lib/Sema/SemaDeclAttr.cpp | 5 +- test/SemaObjC/arc-system-header.m | 6 +- test/SemaObjCXX/arc-system-header.mm | 2 +- utils/TableGen/ClangAttrEmitter.cpp | 165 ++++++++++++++++----------- 5 files changed, 113 insertions(+), 76 deletions(-) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index bed9218ada..a0484ea2f1 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -129,9 +129,10 @@ def HasFunctionProto : SubsetSubject(S)}]>; // A single argument to an attribute -class Argument { +class Argument { string Name = name; bit Optional = optional; + bit Fake = fake; } class BoolArgument : Argument; @@ -167,7 +168,8 @@ class DefaultIntArgument : IntArgument { // 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 values, - list enums, bit opt = 0> : Argument { + list enums, bit opt = 0, bit fake = 0> + : Argument { string Type = type; list Values = values; list 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]; } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 51f9e45d7e..1f90b75e57 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -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()) - if (attr->isImplicit()) + if (attr->isImplicit() && + attr->getImplicitSource() == UnavailableAttr::ISK_ForbiddenType) diag_available_here = diag::note_unavailability_inferred_here; } diff --git a/test/SemaObjC/arc-system-header.m b/test/SemaObjC/arc-system-header.m index 85022bb9ee..a627b3be9b 100644 --- a/test/SemaObjC/arc-system-header.m +++ b/test/SemaObjC/arc-system-header.m @@ -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() { diff --git a/test/SemaObjCXX/arc-system-header.mm b/test/SemaObjCXX/arc-system-header.mm index 44b1cd77ba..b97e2f3b1e 100644 --- a/test/SemaObjCXX/arc-system-header.mm +++ b/test/SemaObjCXX/arc-system-header.mm @@ -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}} diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index d3e67ba91b..0c28576498 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -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> 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 &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(ai.get())->writeConversion(OS); else if (ai->isVariadicEnumArg()) -- 2.40.0