jdenny created this revision.
jdenny added reviewers: aaron.ballman, hfinkel.

Parameter indices in some attributes (argument_with_type_tag,
pointer_with_type_tag, nonnull, ownership_takes, ownership_holds, and 
ownership_returns) are specified in source as one-origin including any 
this parameter, are stored as zero-origin excluding any this
parameter, and were erroneously printing (-ast-print) as the stored
values.  This patch fixes those cases by printing re-incremented
values.

An alternative solution is to store only the original values, but that
requires modifying all users.  I tried that for nonnull and found that
it required repeating index adjustment logic in many places while this
patch encapsulates that logic in relatively few places.

Another alternative is to store both sets of values, but that would be
less efficient.

This patch also fixes argument_with_type_tag and pointer_with_type_tag
not to print their fake IsPointer arguments.


https://reviews.llvm.org/D43248

Files:
  include/clang/Basic/Attr.td
  lib/Sema/SemaDeclAttr.cpp
  test/Sema/attr-print.cpp
  utils/TableGen/ClangAttrEmitter.cpp

Index: utils/TableGen/ClangAttrEmitter.cpp
===================================================================
--- utils/TableGen/ClangAttrEmitter.cpp
+++ utils/TableGen/ClangAttrEmitter.cpp
@@ -738,6 +738,42 @@
     }
   };
 
+  class VariadicParamIndexArgument : public VariadicArgument {
+    std::string DisallowImplicitThisParamName;
+
+  protected:
+    // Assumed to receive a parameter: raw_ostream OS.
+    virtual void writeValueImpl(raw_ostream &OS) const {
+      OS << "    OS << (Val + 1";
+      if (!DisallowImplicitThisParamName.empty())
+        OS << " + get" << DisallowImplicitThisParamName << "()";
+      OS << ");\n";
+    }
+  public:
+    VariadicParamIndexArgument(const Record &Arg, StringRef Attr)
+        : VariadicArgument(Arg, Attr, "unsigned"),
+          DisallowImplicitThisParamName(
+              Arg.getValueAsString("DisallowImplicitThisParamName")) {}
+  };
+
+  class ParamIndexArgument : public SimpleArgument {
+    std::string DisallowImplicitThisParamName;
+
+  protected:
+    void writeValue(raw_ostream &OS) const override {
+      OS << "\" << (get" << getUpperName() << "() + 1";
+      if (!DisallowImplicitThisParamName.empty())
+        OS << " + get" << DisallowImplicitThisParamName << "()";
+      OS << ") << \"";
+    }
+
+  public:
+    ParamIndexArgument(const Record &Arg, StringRef Attr)
+        : SimpleArgument(Arg, Attr, "unsigned"),
+          DisallowImplicitThisParamName(
+              Arg.getValueAsString("DisallowImplicitThisParamName")) {}
+  };
+
   // Unique the enums, but maintain the original declaration ordering.
   std::vector<StringRef>
   uniqueEnumsInOrder(const std::vector<StringRef> &enums) {
@@ -1237,6 +1273,10 @@
     Ptr = llvm::make_unique<VariadicEnumArgument>(Arg, Attr);
   else if (ArgName == "VariadicExprArgument")
     Ptr = llvm::make_unique<VariadicExprArgument>(Arg, Attr);
+  else if (ArgName == "VariadicParamIndexArgument")
+    Ptr = llvm::make_unique<VariadicParamIndexArgument>(Arg, Attr);
+  else if (ArgName == "ParamIndexArgument")
+    Ptr = llvm::make_unique<ParamIndexArgument>(Arg, Attr);
   else if (ArgName == "VersionArgument")
     Ptr = llvm::make_unique<VersionArgument>(Arg, Attr);
 
Index: test/Sema/attr-print.cpp
===================================================================
--- /dev/null
+++ test/Sema/attr-print.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 %s -ast-print | FileCheck %s
+
+void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 3, 2)));
+// CHECK: void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 3, 2)));
+void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 1, 2)));
+// CHECK: void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 1, 2)));
+
+void nn(int *, int *) __attribute__((nonnull(1, 2)));
+// CHECK: void nn(int *, int *) __attribute__((nonnull(1, 2)));
+
+void ownt(int *, int *) __attribute__((ownership_takes(foo, 1, 2)));
+// CHECK: void ownt(int *, int *) __attribute__((ownership_takes(foo, 1, 2)));
+void ownh(int *, int *) __attribute__((ownership_holds(foo, 1, 2)));
+// CHECK: void ownh(int *, int *) __attribute__((ownership_holds(foo, 1, 2)));
+void ownr(int) __attribute__((ownership_returns(foo, 1)));
+// CHECK: void ownr(int) __attribute__((ownership_returns(foo, 1)));
+
+class C {
+  void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 4, 3)));
+  // CHECK: void awtt(int, int, ...) __attribute__((argument_with_type_tag(foo, 4, 3)));
+  void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
+  // CHECK: void pwtt(void *, int) __attribute__((pointer_with_type_tag(foo, 2, 3)));
+
+  void nn(int *, int *) __attribute__((nonnull(2, 3)));
+  // CHECK: void nn(int *, int *) __attribute__((nonnull(2, 3)));
+
+  void ownt(int *, int *) __attribute__((ownership_takes(foo, 2, 3)));
+  // CHECK: void ownt(int *, int *) __attribute__((ownership_takes(foo, 2, 3)));
+  void ownh(int *, int *) __attribute__((ownership_holds(foo, 2, 3)));
+  // CHECK: void ownh(int *, int *) __attribute__((ownership_holds(foo, 2, 3)));
+  void ownr(int) __attribute__((ownership_returns(foo, 2)));
+  // CHECK: void ownr(int) __attribute__((ownership_returns(foo, 2)));
+};
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -314,7 +314,8 @@
 template <typename AttrInfo>
 static bool checkFunctionOrMethodParameterIndex(
     Sema &S, const Decl *D, const AttrInfo &Attr, unsigned AttrArgNum,
-    const Expr *IdxExpr, uint64_t &Idx, bool AllowImplicitThis = false) {
+    const Expr *IdxExpr, uint64_t &Idx, bool AllowImplicitThis = false,
+    bool *DisallowImplicitThisParam = nullptr) {
   assert(isFunctionOrMethodOrBlock(D));
 
   // In C++ the implicit 'this' function parameter also counts.
@@ -349,7 +350,11 @@
       return false;
     }
     --Idx;
+    if (DisallowImplicitThisParam)
+      *DisallowImplicitThisParam = true;
   }
+  else if (DisallowImplicitThisParam)
+    *DisallowImplicitThisParam = false;
 
   return true;
 }
@@ -1452,11 +1457,15 @@
 }
 
 static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  // Init of DisallowImplicitThisParam is for case of no values, but no values
+  // means it doesn't matter.
+  bool DisallowImplicitThisParam = false;
   SmallVector<unsigned, 8> NonNullArgs;
   for (unsigned I = 0; I < Attr.getNumArgs(); ++I) {
     Expr *Ex = Attr.getArgAsExpr(I);
     uint64_t Idx;
-    if (!checkFunctionOrMethodParameterIndex(S, D, Attr, I + 1, Ex, Idx))
+    if (!checkFunctionOrMethodParameterIndex(S, D, Attr, I + 1, Ex, Idx, false,
+                                             &DisallowImplicitThisParam))
       return;
 
     // Is the function argument a pointer type?
@@ -1492,6 +1501,7 @@
   llvm::array_pod_sort(Start, Start + Size);
   D->addAttr(::new (S.Context)
              NonNullAttr(Attr.getRange(), S.Context, Start, Size,
+                         DisallowImplicitThisParam,
                          Attr.getAttributeSpellingListIndex()));
 }
 
@@ -1706,11 +1716,15 @@
     Module = &S.PP.getIdentifierTable().get(ModuleName);
   }
 
+  // Init of DisallowImplicitThisParam is for case of no values, but no values
+  // means it doesn't matter.
+  bool DisallowImplicitThisParam = false;
   SmallVector<unsigned, 8> OwnershipArgs;
   for (unsigned i = 1; i < AL.getNumArgs(); ++i) {
     Expr *Ex = AL.getArgAsExpr(i);
     uint64_t Idx;
-    if (!checkFunctionOrMethodParameterIndex(S, D, AL, i, Ex, Idx))
+    if (!checkFunctionOrMethodParameterIndex(S, D, AL, i, Ex, Idx, false,
+                                             &DisallowImplicitThisParam))
       return;
 
     // Is the function argument a pointer type?
@@ -1766,6 +1780,7 @@
 
   D->addAttr(::new (S.Context)
              OwnershipAttr(AL.getLoc(), S.Context, Module, start, size,
+                           DisallowImplicitThisParam,
                            AL.getAttributeSpellingListIndex()));
 }
 
@@ -4583,14 +4598,18 @@
     return;
   }
 
+  bool DisallowImplicitThisParam;
+
   uint64_t ArgumentIdx;
   if (!checkFunctionOrMethodParameterIndex(S, D, Attr, 2, Attr.getArgAsExpr(1),
-                                           ArgumentIdx))
+                                           ArgumentIdx, false,
+                                           &DisallowImplicitThisParam))
     return;
 
   uint64_t TypeTagIdx;
   if (!checkFunctionOrMethodParameterIndex(S, D, Attr, 3, Attr.getArgAsExpr(2),
-                                           TypeTagIdx))
+                                           TypeTagIdx, false,
+                                           &DisallowImplicitThisParam))
     return;
 
   bool IsPointer = (Attr.getName()->getName() == "pointer_with_type_tag");
@@ -4605,7 +4624,8 @@
 
   D->addAttr(::new (S.Context)
              ArgumentWithTypeTagAttr(Attr.getRange(), S.Context, ArgumentKind,
-                                     ArgumentIdx, TypeTagIdx, IsPointer,
+                                     ArgumentIdx, TypeTagIdx,
+                                     DisallowImplicitThisParam, IsPointer,
                                      Attr.getAttributeSpellingListIndex()));
 }
 
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -163,6 +163,32 @@
 class VariadicExprArgument<string name> : Argument<name, 1>;
 class VariadicStringArgument<string name> : Argument<name, 1>;
 
+// Like VariadicUnsignedArgument except values represent function parameter
+// indices.  That is, they are stored with zero-origin indexing, but they
+// print with one-origin indexing as in the original source code.  Moreover, if
+// there's an implicit this parameter whose index is not allowed as a value,
+// then the stored values are decremented by one more relative to the printed
+// original values.  The burden for storing properly decremented values is on
+// the user (see checkFunctionOrMethodParameterIndex in SemaDeclAttr.cpp), but
+// this class handles printing the re-incremented values.
+class VariadicParamIndexArgument<string name,
+                                 string disallowImplicitThisParamName>
+    : VariadicUnsignedArgument<name> {
+  // In the enclosing attribute, this is the name of the fake BoolArgument
+  // that records whether both (1) the attribute is on a function with an
+  // implicit this parameter and (2) the this parameter's index is not allowed
+  // as a value here.  Empty string if there's no such BoolArgument because
+  // it would always be false.
+  string DisallowImplicitThisParamName = disallowImplicitThisParamName;
+}
+
+// Like VariadicParamIndexArgument but for a single non-optional function
+// parameter index.
+class ParamIndexArgument<string name, string disallowImplicitThisParamName>
+    : UnsignedArgument<name> {
+  string DisallowImplicitThisParamName = disallowImplicitThisParamName;
+}
+
 // A version of the form major.minor[.subminor].
 class VersionArgument<string name, bit opt = 0> : Argument<name, opt>;
 
@@ -1376,7 +1402,8 @@
   let Spellings = [GCC<"nonnull">];
   let Subjects = SubjectList<[ObjCMethod, HasFunctionProto, ParmVar], WarnDiag,
                              "functions, methods, and parameters">;
-  let Args = [VariadicUnsignedArgument<"Args">];
+  let Args = [VariadicParamIndexArgument<"Args", "DisallowImplicitThisParam">,
+              BoolArgument<"DisallowImplicitThisParam", 0, /*fake*/ 1>];
   let AdditionalMembers =
 [{bool isNonNull(unsigned idx) const {
     if (!args_size())
@@ -1654,7 +1681,9 @@
              Returns;
     }
   }];
-  let Args = [IdentifierArgument<"Module">, VariadicUnsignedArgument<"Args">];
+  let Args = [IdentifierArgument<"Module">,
+              VariadicParamIndexArgument<"Args", "DisallowImplicitThisParam">,
+              BoolArgument<"DisallowImplicitThisParam", 0, /*fake*/ 1>];
   let Subjects = SubjectList<[HasFunctionProto]>;
   let Documentation = [Undocumented];
 }
@@ -2449,9 +2478,10 @@
   let Spellings = [GNU<"argument_with_type_tag">,
                    GNU<"pointer_with_type_tag">];
   let Args = [IdentifierArgument<"ArgumentKind">,
-              UnsignedArgument<"ArgumentIdx">,
-              UnsignedArgument<"TypeTagIdx">,
-              BoolArgument<"IsPointer">];
+              ParamIndexArgument<"ArgumentIdx", "DisallowImplicitThisParam">,
+              ParamIndexArgument<"TypeTagIdx", "DisallowImplicitThisParam">,
+              BoolArgument<"DisallowImplicitThisParam", 0, /*fake*/ 1>,
+              BoolArgument<"IsPointer", 0, /*fake*/ 1>];
   let HasCustomParsing = 1;
   let Documentation = [ArgumentWithTypeTagDocs, PointerWithTypeTagDocs];
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to