Hello,

=== Objective ===
Implement a set of annotations for MPI libraries and a diagnostic for
clang that checks that specified buffer type matches the passed
MPI_Datatype.

=== Background ===
Many MPI functions take a void* `buffer' and an MPI_Datatype (like
MPI_INT, MPI_LONG etc) that describes how to send/receive that buffer.
 For example,

int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest,
    int tag, MPI_Comm comm);

Users call it like this:
int *int_buf = ...;
MPI_Send(int_buf, 1, MPI_INT, ...);

Thus, user is on its own to ensure that buffer type matches the MPI_Datatype.

=== Annotation design ===
I decided to annotate MPI functions that have buffer arguments and
declarations of built-in `MPI_Datatype's.  Functions are easy: we use
mpi_typed_arg(buffer-arg-index, type-arg-index) attribute:

int MPI_Send(void *buf, int count, MPI_Datatype datatype, ...etc...)
    __attribute__(( mpi_typed_arg(1,3) ));

`MPI_Datatype's are trickier.  From the grammar of GNU attributes in
lib/Parse/ParseDecl.cpp we see that there is no easy way to pass a
type argument to an attribute.  For example, this is not currently
parseable by clang:

#define MPI_INT ((MPI_Datatype) &ompi_mpi_int)
extern struct ompi_predefined_datatype_t ompi_mpi_int
        __attribute__(( mpi_datatype(int) ));

Matching C datatype for MPI_DATATYPE_NULL is void.

#define MPI_DATATYPE_NULL ((MPI_Datatype) &ompi_mpi_int)
extern struct ompi_predefined_datatype_t ompi_mpi_void
        __attribute__(( mpi_datatype(void) ));

While checking a function call we recognize mpi_datatype attribute if
datatype argument is either:
* an OpenMPI-style expression `((MPI_Datatype) &ident)';
* a user-specified type `ident';
where ident has mpi_datatype attribute.

We emit a diagnostic in 2 cases.
Case 1.  (Any type except MPI_DATATYPE_NULL)  All of the following is true:
* buffer is not of `void *' type;
* buffer's element type doesn't match specified MPI_Datatype.

Case 2.  (MPI_DATATYPE_NULL)  All of the following is true:
* buffer is not of `void *' type;
* buffer is not a null pointer constant;
* mpi_datatype(void) was specified.

=== Clang modifications ===
We need to allow arbitrary types as attribute arguments.  If I
understand everything correctly, GNU attribute grammar becomes
ambiguous in that case, so attribute argument parsing will be driven
by attribute name.  Fortunately, that is compatible with C++11
attributes where attribute argument list is just a sequence of tokens
with balanced delimiters.

I added AttributeList::TypeArg member, the required AttributeList
constructor and factory method.  In Attr.td a new field,
HasTypeArgument, was added and a tablegen pass was implemented.  The
parser uses the generated table to decide if it should parse a list of
expressions or a type name.

The check itself is called from Sema::CheckFunctionCall, which seems
to be designed for similar purposes.

=== Compatibility ===
The annotation-based approach is compatible with OpenMPI and LAM MPI.
I already have a prototype patch for OpenMPI.

MPICH is not compatible without refactoring their mpi.h header (they
have MPI_XXX macros defined to magic numbers).

=== Miscellaneous ===
There is an `iboutletcollection' attribute that takes a type argument.
 It would be good if this implementation of type arguments could be
used for that, too.

Please see attached patch.  Parsing diagnostics are not very good (see
testcase), advice is welcome.

Dmitri

-- 
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <[email protected]>*/
Index: test/Sema/warn-mpi-type-mismatch.c
===================================================================
--- test/Sema/warn-mpi-type-mismatch.c	(revision 0)
+++ test/Sema/warn-mpi-type-mismatch.c	(revision 0)
@@ -0,0 +1,150 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+//===--- mpi.h mock -------------------------------------------------------===//
+
+#define NULL ((void *)0)
+
+typedef struct ompi_communicator_t *MPI_Comm;
+typedef struct ompi_datatype_t *MPI_Datatype;
+
+int MPI_Wrong1(void *buf, int count, MPI_Datatype datatype, int dest,
+    int tag, MPI_Comm comm)
+    __attribute__(( mpi_typed_arg )); // expected-error {{attribute requires exactly 2 arguments}}
+
+int MPI_Wrong2(void *buf, int count, MPI_Datatype datatype)
+    __attribute__(( mpi_typed_arg(0,7) )); // expected-error {{attribute parameter 1 is out of bounds}}
+
+int MPI_Wrong3(void *buf, int count, MPI_Datatype datatype)
+    __attribute__(( mpi_typed_arg(5,7) )); // expected-error {{attribute parameter 1 is out of bounds}}
+
+int MPI_Wrong4(void *buf, int count, MPI_Datatype datatype)
+    __attribute__(( mpi_typed_arg(3,0) )); // expected-error {{attribute parameter 2 is out of bounds}}
+
+int MPI_Wrong5(void *buf, int count, MPI_Datatype datatype)
+    __attribute__(( mpi_typed_arg(3,5) )); // expected-error {{attribute parameter 2 is out of bounds}}
+
+extern int x;
+
+int MPI_Wrong6(void *buf, int count, MPI_Datatype datatype)
+    __attribute__(( mpi_typed_arg((x+1),7) )); // expected-error {{attribute requires parameter 1 to be an integer constant}}
+
+int MPI_Wrong7(void *buf, int count, MPI_Datatype datatype)
+    __attribute__(( mpi_typed_arg(3,x) )); // expected-error {{attribute requires parameter 2 to be an integer constant}}
+
+int MPI_Wrong8() __attribute__(( mpi_typed_arg(1,3) )); // expected-error {{attribute only applies to functions and methods}}
+
+int MPI_Wrong9(double buf, MPI_Datatype type)
+    __attribute__(( mpi_typed_arg(1,2) )); // expected-error {{buffer argument is not a pointer}}
+
+int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest,
+    int tag, MPI_Comm comm)
+    __attribute__(( mpi_typed_arg(1,3) ));
+
+int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype,
+               void *recvbuf, int recvcount, MPI_Datatype recvtype,
+               int root, MPI_Comm comm)
+               __attribute__(( mpi_typed_arg(1,3), mpi_typed_arg(4,6) ));
+
+#define OMPI_PREDEFINED_GLOBAL(type, global) ((type) &(global))
+
+#define MPI_COMM_WORLD OMPI_PREDEFINED_GLOBAL(MPI_Comm, ompi_mpi_comm_world)
+extern struct ompi_predefined_communicator_t ompi_mpi_comm_world;
+
+#define MPI_DATATYPE_NULL OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_datatype_null)
+#define MPI_FLOAT         OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_float)
+#define MPI_INT           OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_int)
+#define MPI_LONG          OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_long)
+#define MPI_LONG_LONG_INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_long_long_int)
+#define MPI_CHAR          OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_char)
+
+#define MPI_IN_PLACE ((void *) 1)
+
+extern struct ompi_predefined_datatype_t ompi_mpi_wrong1
+    __attribute__(( mpi_datatype )); // expected-error {{'mpi_datatype' attribute requires parameter 1 to be a type name}}
+
+extern struct ompi_predefined_datatype_t ompi_mpi_wrong2
+    __attribute__(( mpi_datatype(1,2) )); // expected-error {{type name requires a specifier or qualifier}} \
+                                          // expected-warning {{type specifier missing, defaults to 'int'}} \
+                                          // expected-error {{expected ')'}}
+
+extern struct ompi_predefined_datatype_t ompi_mpi_wrong3
+    __attribute__(( mpi_datatype(not_a_type) )); // expected-error {{type name requires a specifier or qualifier}} \
+                                                 // expected-warning {{type specifier missing, defaults to 'int'}} \
+                                                 // expected-error {{expected ')'}}
+
+extern struct ompi_predefined_datatype_t ompi_mpi_datatype_null __attribute__(( mpi_datatype(void) )); // mpi_datatype(void) is for MPI_DATATYPE_NULL
+extern struct ompi_predefined_datatype_t ompi_mpi_float         __attribute__(( mpi_datatype(float) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_int           __attribute__(( mpi_datatype(int) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_long          __attribute__(( mpi_datatype(long) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_long_long_int __attribute__(( mpi_datatype(long long int) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_char          __attribute__(( mpi_datatype(char) ));
+
+//===--- Tests ------------------------------------------------------------===//
+
+void test_predefined_types(int *int_buf, long *long_buf1, long *long_buf2, void *void_buf)
+{
+  char char_buf[255];
+
+  MPI_Send(int_buf,   1, MPI_INT, 1, 1, MPI_COMM_WORLD); // no-warning
+
+  MPI_Send(long_buf1, 1, MPI_INT, 1, 1, MPI_COMM_WORLD); // expected-warning {{actual buffer element type 'long' doesn't match specified MPI_Datatype (which is 'int')}}
+
+  // Function with two buffers.
+  MPI_Gather(long_buf1, 1, MPI_INT, // expected-warning {{actual buffer element type 'long' doesn't match specified MPI_Datatype (which is 'int')}}
+             long_buf2, 1, MPI_INT, // expected-warning {{actual buffer element type 'long' doesn't match specified MPI_Datatype (which is 'int')}}
+             1, MPI_COMM_WORLD);
+
+  // Array buffers should work like pointer buffers.
+  MPI_Send(char_buf,  255, MPI_CHAR, 1, 1, MPI_COMM_WORLD); // no-warning
+
+  // Explicit casts should not be dropped.
+  MPI_Send((int *) char_buf,  255, MPI_INT, 1, 1, MPI_COMM_WORLD); // no-warning
+  MPI_Send((int *) char_buf,  255, MPI_CHAR, 1, 1, MPI_COMM_WORLD); // expected-warning {{actual buffer element type 'int' doesn't match specified MPI_Datatype (which is 'char')}}
+
+  // `void*' buffer should never warn.
+  MPI_Send(void_buf,  255, MPI_CHAR, 1, 1, MPI_COMM_WORLD); // no-warning
+
+  // We expect that MPI_IN_PLACE is `void*', shouldn't warn.
+  MPI_Gather(MPI_IN_PLACE, 0, MPI_INT,
+             int_buf,      1, MPI_INT,
+             1, MPI_COMM_WORLD);
+
+  // Special handling for MPI_DATATYPE_NULL: buffer pointer should be either
+  // a `void*' pointer or a null pointer constant.
+  MPI_Gather(NULL,    0, MPI_DATATYPE_NULL, // no-warning
+             int_buf, 1, MPI_INT,
+             1, MPI_COMM_WORLD);
+
+  MPI_Gather(int_buf, 0, MPI_DATATYPE_NULL, // expected-warning {{MPI_DATATYPE_NULL was specified but buffer is not a null pointer}}
+             int_buf, 1, MPI_INT,
+             1, MPI_COMM_WORLD);
+}
+
+MPI_Datatype my_int_datatype __attribute__(( mpi_datatype(int) ));
+
+struct X { int a; int b; };
+MPI_Datatype my_x_datatype __attribute__(( mpi_datatype(struct X) ));
+
+MPI_Datatype my_unknown_datatype;
+
+typedef float real;
+#define MPI_REAL MPI_FLOAT
+
+void test_user_types(int *int_buf, long *long_buf, struct X *x_buf, real* real_buf, MPI_Datatype type)
+{
+  MPI_Send(int_buf,  1, my_int_datatype, 1, 1, MPI_COMM_WORLD); // no-warning
+  MPI_Send(long_buf, 1, my_int_datatype, 1, 1, MPI_COMM_WORLD); // expected-warning {{actual buffer element type 'long' doesn't match specified MPI_Datatype (which is 'int')}}
+
+  MPI_Send(x_buf, 1, my_x_datatype, 1, 1, MPI_COMM_WORLD); // no-warning
+
+  MPI_Send(long_buf, 1, my_x_datatype, 1, 1, MPI_COMM_WORLD); // expected-warning {{actual buffer element type 'long' doesn't match specified MPI_Datatype (which is 'struct X')}}
+  MPI_Send(x_buf, 1, MPI_INT, 1, 1, MPI_COMM_WORLD); // expected-warning {{actual buffer element type 'struct X' doesn't match specified MPI_Datatype (which is 'int')}}
+
+  MPI_Send(real_buf, 1, MPI_REAL, 1, 1, MPI_COMM_WORLD); // no-warning
+  MPI_Send(real_buf, 1, MPI_FLOAT, 1, 1, MPI_COMM_WORLD); // no-warning
+
+  // Using 'MPI_Datatype's without attributes should not produce warnings.
+  MPI_Send(long_buf, 1, my_unknown_datatype, 1, 1, MPI_COMM_WORLD); // no-warning
+  MPI_Send(int_buf, 1, type, 1, 1, MPI_COMM_WORLD); // no-warning
+}
+
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td	(revision 150307)
+++ include/clang/Basic/DiagnosticGroups.td	(working copy)
@@ -295,6 +295,9 @@
 def Format2 : DiagGroup<"format=2",
                         [FormatNonLiteral, FormatSecurity, FormatY2K]>;
 
+// MPI warnings
+def MPI : DiagGroup<"mpi">;
+
 def Extra : DiagGroup<"extra", [
     MissingFieldInitializers,
     IgnoredQualifiers,
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td	(revision 150307)
+++ include/clang/Basic/Attr.td	(working copy)
@@ -93,6 +93,8 @@
   list<string> Namespaces = [];
   // Set to true for attributes with arguments which require delayed parsing. 
   bit LateParsed = 0;  
+  // Set to true for attributes with a single type argument.
+  bit HasTypeArgument = 0;
   // Set to true for attributes which must be instantiated within templates
   bit TemplateDependent = 0;
   // Any additional text that should be included verbatim in the class.  
@@ -697,3 +699,19 @@
   let LateParsed = 1;
   let TemplateDependent = 1;
 }
+
+// MPI buffer type checking attributes
+
+def MPITypedArg : InheritableAttr {
+  let Spellings = ["mpi_typed_arg"];
+  let Args = [UnsignedArgument<"BufferIdx">, UnsignedArgument<"TypeIdx">];
+  let Subjects = [Function];
+}
+
+def MPIDatatype : InheritableAttr {
+  let Spellings = ["mpi_datatype"];
+  let Args = [TypeArgument<"MatchingCType">];
+  let Subjects = [Var];
+  let HasTypeArgument = 1;
+}
+
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td	(revision 150307)
+++ include/clang/Basic/DiagnosticSemaKinds.td	(working copy)
@@ -1472,6 +1472,8 @@
   "'%0' attribute requires parameter %1 to be an integer constant">;
 def err_attribute_argument_n_not_string : Error<
   "'%0' attribute requires parameter %1 to be a string">;
+def err_attribute_argument_n_not_type_name : Error<
+  "'%0' attribute requires parameter %1 to be a type name">;
 def err_attribute_argument_out_of_bounds : Error<
   "'%0' attribute parameter %1 is out of bounds">;
 def err_attribute_requires_objc_interface : Error<
@@ -1623,8 +1625,9 @@
   "%0 attribute can only be applied to value declarations">;
 def warn_attribute_not_on_decl : Error<
   "%0 attribute ignored when parsing type">;
+def err_mpi_typed_arg_attribute_buffer_not_pointer : Error<
+  "buffer argument is not a pointer">;
 
-
 // Availability attribute
 def warn_availability_unknown_platform : Warning<
   "unknown platform %0 in availability macro">;
@@ -4977,6 +4980,14 @@
   "unspecified (use strncmp instead)">,
   InGroup<DiagGroup<"string-compare">>;
 
+// MPI checks
+def warn_mpi_type_mismatch : Warning<
+  "actual buffer element type %0 doesn't match specified MPI_Datatype (which is %1)">,
+  InGroup<MPI>;
+def warn_mpi_datatype_null_but_buffer_not_null : Warning<
+  "MPI_DATATYPE_NULL was specified but buffer is not a null pointer">,
+  InGroup<MPI>;
+
 // Generic selections.
 def err_assoc_type_incomplete : Error<
   "type %0 in generic association incomplete">;
Index: include/clang/Sema/AttributeList.h
===================================================================
--- include/clang/Sema/AttributeList.h	(revision 150307)
+++ include/clang/Sema/AttributeList.h	(working copy)
@@ -19,6 +19,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/VersionTuple.h"
+#include "clang/Sema/Ownership.h"
 #include <cassert>
 
 namespace clang {
@@ -50,15 +51,18 @@
 /// 2: __attribute__(( mode(byte) )). ParmName used, Args/NumArgs unused.
 /// 3: __attribute__(( format(printf, 1, 2) )). ParmName/Args/NumArgs all used.
 /// 4: __attribute__(( aligned(16) )). ParmName is unused, Args/Num used.
+/// 5: __attribute__(( mpi_datatype(type-name) )). Only TypeArg used, NumArgs=0.
 ///
 class AttributeList { // TODO: This should really be called ParsedAttribute
 private:
   IdentifierInfo *AttrName;
   IdentifierInfo *ScopeName;
   IdentifierInfo *ParmName;
+  ParsedType TypeArg;
   SourceRange AttrRange;
   SourceLocation ScopeLoc;
   SourceLocation ParmLoc;
+  SourceRange TypeRange;
 
   /// The number of expression arguments this attribute has.
   /// The expressions themselves are stored after the object.
@@ -80,6 +84,9 @@
   /// availability attribute.
   unsigned IsAvailability : 1;
 
+  /// True if \c TypeArg and \c TypeRange are meaningful.
+  unsigned HasTypeArg : 1;
+
   unsigned AttrKind : 8;
 
   /// \brief The location of the 'unavailable' keyword in an
@@ -119,6 +126,7 @@
 
   size_t allocated_size() const;
 
+  /// Constructor for attributes with expression arguments.
   AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
                 IdentifierInfo *scopeName, SourceLocation scopeLoc,
                 IdentifierInfo *parmName, SourceLocation parmLoc,
@@ -128,12 +136,13 @@
       AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc),
       NumArgs(numArgs),
       DeclspecAttribute(declspec), CXX0XAttribute(cxx0x), Invalid(false),
-      UsedAsTypeAttr(false), IsAvailability(false), 
+      UsedAsTypeAttr(false), IsAvailability(false), HasTypeArg(false),
       NextInPosition(0), NextInPool(0) {
     if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*));
     AttrKind = getKind(getName());
   }
 
+  /// Constructor for availability attributes.
   AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
                 IdentifierInfo *scopeName, SourceLocation scopeLoc,
                 IdentifierInfo *parmName, SourceLocation parmLoc,
@@ -147,6 +156,7 @@
       AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc),
       NumArgs(0), DeclspecAttribute(declspec), CXX0XAttribute(cxx0x),
       Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
+      HasTypeArg(false),
       UnavailableLoc(unavailable), MessageExpr(messageExpr),
       NextInPosition(0), NextInPool(0) {
     new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced);
@@ -155,6 +165,20 @@
     AttrKind = getKind(getName());
   }
 
+  /// Constructor for attributes with one type argument.
+  AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
+                IdentifierInfo *scopeName, SourceLocation scopeLoc,
+                ParsedType typeArg, SourceRange typeRange, bool cxx0x)
+    : AttrName(attrName), ScopeName(scopeName), ParmName(NULL),
+      TypeArg(typeArg),
+      AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(SourceLocation()),
+      TypeRange(typeRange),
+      NumArgs(0), DeclspecAttribute(false), CXX0XAttribute(cxx0x),
+      Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
+      HasTypeArg(true), NextInPosition(NULL), NextInPool(NULL) {
+    AttrKind = getKind(getName());
+  }
+
   friend class AttributePool;
   friend class AttributeFactory;
 
@@ -214,6 +238,8 @@
     AT_malloc,
     AT_may_alias,
     AT_mode,
+    AT_mpi_datatype,
+    AT_mpi_typed_arg,
     AT_MsStruct,
     AT_naked,
     AT_neon_polyvector_type,    // Clang-specific.
@@ -301,6 +327,10 @@
   bool isUsedAsTypeAttr() const { return UsedAsTypeAttr; }
   void setUsedAsTypeAttr() { UsedAsTypeAttr = true; }
 
+  bool hasTypeArg() const { return HasTypeArg; }
+  ParsedType getTypeArg() const { return TypeArg; }
+  SourceRange getTypeRange() const { return TypeRange; }
+
   Kind getKind() const { return Kind(AttrKind); }
   static Kind getKind(const IdentifierInfo *Name);
 
@@ -516,6 +546,16 @@
 
   AttributeList *createIntegerAttribute(ASTContext &C, IdentifierInfo *Name,
                                         SourceLocation TokLoc, int Arg);
+
+  AttributeList *createAttributeWithTypeArg(
+                    IdentifierInfo *attrName, SourceRange attrRange,
+                    IdentifierInfo *scopeName, SourceLocation scopeLoc,
+                    ParsedType typeArg, SourceRange typeRange, bool cxx0x) {
+    void *memory = allocate(sizeof(AttributeList));
+    return add(new (memory) AttributeList(attrName, attrRange,
+                                          scopeName, scopeLoc,
+                                          typeArg, typeRange, cxx0x));
+  }
 };
 
 /// addAttributeLists - Add two AttributeLists together
@@ -608,7 +648,7 @@
   /// dependencies on this method, it may not be long-lived.
   AttributeList *&getListRef() { return list; }
 
-
+  /// Add attribute with expression arguments.
   AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange,
                         IdentifierInfo *scopeName, SourceLocation scopeLoc,
                         IdentifierInfo *parmName, SourceLocation parmLoc,
@@ -621,6 +661,7 @@
     return attr;
   }
 
+  /// Add availability attribute.
   AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange,
                         IdentifierInfo *scopeName, SourceLocation scopeLoc,
                         IdentifierInfo *parmName, SourceLocation parmLoc,
@@ -639,6 +680,20 @@
     return attr;
   }
 
+  /// Add attribute with one type argument.
+  AttributeList *addNewWithTypeArg(
+                        IdentifierInfo *attrName, SourceRange attrRange,
+                        IdentifierInfo *scopeName, SourceLocation scopeLoc,
+                        ParsedType typeArg, SourceRange typeRange,
+                        bool cxx0x = false) {
+    AttributeList *attr =
+      pool.createAttributeWithTypeArg(attrName, attrRange,
+                                      scopeName, scopeLoc,
+                                      typeArg, typeRange, cxx0x);
+    add(attr);
+    return attr;
+  }
+
   AttributeList *addNewInteger(ASTContext &C, IdentifierInfo *name,
                                SourceLocation loc, int arg) {
     AttributeList *attr =
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h	(revision 150307)
+++ include/clang/Sema/Sema.h	(working copy)
@@ -6396,6 +6396,9 @@
   void CheckBitFieldInitialization(SourceLocation InitLoc, FieldDecl *Field,
                                    Expr *Init);
 
+  void CheckMPITypedArguments(const MPITypedArgAttr *Attr,
+                              const Expr * const *ExprArgs);
+
   /// \brief The parser's current scope.
   ///
   /// The parser maintains this state here.
Index: include/clang/Parse/CMakeLists.txt
===================================================================
--- include/clang/Parse/CMakeLists.txt	(revision 150307)
+++ include/clang/Parse/CMakeLists.txt	(working copy)
@@ -2,3 +2,8 @@
   -I ${CMAKE_CURRENT_SOURCE_DIR}/../../
   SOURCE ../Basic/Attr.td
   TARGET ClangAttrLateParsed)
+
+clang_tablegen(AttrHasTypeArgument.inc -gen-clang-attr-has-type-argument-list
+  -I ${CMAKE_CURRENT_SOURCE_DIR}/../../
+  SOURCE ../Basic/Attr.td
+  TARGET ClangAttrHasTypeArgument)
Index: include/clang/Parse/Makefile
===================================================================
--- include/clang/Parse/Makefile	(revision 150307)
+++ include/clang/Parse/Makefile	(working copy)
@@ -1,6 +1,6 @@
 CLANG_LEVEL := ../../..
 TD_SRC_DIR = $(PROJ_SRC_DIR)/../Basic
-BUILT_SOURCES = AttrLateParsed.inc
+BUILT_SOURCES = AttrLateParsed.inc AttrHasTypeArgument.inc
 
 TABLEGEN_INC_FILES_COMMON = 1
 
@@ -10,4 +10,10 @@
                                    $(ObjDir)/.dir
 	$(Echo) "Building Clang attribute late-parsed table with tblgen"
 	$(Verb) $(ClangTableGen) -gen-clang-attr-late-parsed-list -o $(call SYSPATH, $@) \
-		-I $(PROJ_SRC_DIR)/../../ $<
\ No newline at end of file
+		-I $(PROJ_SRC_DIR)/../../ $<
+
+$(ObjDir)/AttrHasTypeArgument.inc.tmp : $(TD_SRC_DIR)/Attr.td $(CLANG_TBLGEN) \
+                                   $(ObjDir)/.dir
+	$(Echo) "Building Clang attributes with type argument list with tblgen"
+	$(Verb) $(ClangTableGen) -gen-clang-attr-has-type-argument-list -o $(call SYSPATH, $@) \
+		-I $(PROJ_SRC_DIR)/../../ $<
Index: utils/TableGen/ClangAttrEmitter.cpp
===================================================================
--- utils/TableGen/ClangAttrEmitter.cpp	(revision 150307)
+++ utils/TableGen/ClangAttrEmitter.cpp	(working copy)
@@ -913,7 +913,10 @@
 
 }
 
-void ClangAttrLateParsedListEmitter::run(raw_ostream &OS) {
+namespace {
+void GenAttrListWithPropertySetToTrue(raw_ostream &OS,
+                                      RecordKeeper &Records,
+                                      StringRef FieldName) {
   OS << "// This file is generated by TableGen. Do not edit.\n\n";
 
   std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
@@ -922,21 +925,29 @@
        I != E; ++I) {
     Record &Attr = **I;
 
-    bool LateParsed = Attr.getValueAsBit("LateParsed");
+    bool FieldValue = Attr.getValueAsBit(FieldName);
 
-    if (LateParsed) {
+    if (FieldValue) {
       std::vector<StringRef> Spellings =
         getValueAsListOfStrings(Attr, "Spellings");
 
       for (std::vector<StringRef>::const_iterator I = Spellings.begin(),
            E = Spellings.end(); I != E; ++I) {
-        OS << ".Case(\"" << (*I) << "\", " << LateParsed << ")\n";
+        OS << ".Case(\"" << (*I) << "\", " << FieldValue << ")\n";
       }
     }
   }
 }
+} // unnamed namespace
 
+void ClangAttrLateParsedListEmitter::run(raw_ostream &OS) {
+  GenAttrListWithPropertySetToTrue(OS, Records, "LateParsed");
+}
 
+void ClangAttrHasTypeArgumentListEmitter::run(raw_ostream &OS) {
+  GenAttrListWithPropertySetToTrue(OS, Records, "HasTypeArgument");
+}
+
 void ClangAttrTemplateInstantiateEmitter::run(raw_ostream &OS) {
   OS << "// This file is generated by TableGen. Do not edit.\n\n";
 
Index: utils/TableGen/ClangAttrEmitter.h
===================================================================
--- utils/TableGen/ClangAttrEmitter.h	(revision 150307)
+++ utils/TableGen/ClangAttrEmitter.h	(working copy)
@@ -109,6 +109,19 @@
   void run(raw_ostream &OS);
 };
 
+/// ClangAttrHasTypeArgumentListEmitter emits the list of attributes with
+/// HasTypeArgument property for clang.
+class ClangAttrHasTypeArgumentListEmitter : public TableGenBackend {
+  RecordKeeper &Records;
+
+ public:
+  explicit ClangAttrHasTypeArgumentListEmitter(RecordKeeper &R)
+    : Records(R)
+    {}
+
+  void run(raw_ostream &OS);
+};
+
 /// ClangAttrTemplateInstantiateEmitter emits code to instantiate dependent
 /// attributes on templates.
 class ClangAttrTemplateInstantiateEmitter : public TableGenBackend {
Index: utils/TableGen/TableGen.cpp
===================================================================
--- utils/TableGen/TableGen.cpp	(revision 150307)
+++ utils/TableGen/TableGen.cpp	(working copy)
@@ -36,6 +36,7 @@
   GenClangAttrPCHWrite,
   GenClangAttrSpellingList,
   GenClangAttrLateParsedList,
+  GenClangAttrHasTypeArgumentList,
   GenClangAttrTemplateInstantiate,
   GenClangDiagsDefs,
   GenClangDiagGroups,
@@ -72,6 +73,10 @@
                     clEnumValN(GenClangAttrLateParsedList,
                                "gen-clang-attr-late-parsed-list",
                                "Generate a clang attribute LateParsed list"),
+                    clEnumValN(GenClangAttrHasTypeArgumentList,
+                               "gen-clang-attr-has-type-argument-list",
+                               "Generate a clang attribute HasTypeArgument "
+                               "list"),
                     clEnumValN(GenClangAttrTemplateInstantiate,
                                "gen-clang-attr-template-instantiate",
                                "Generate a clang template instantiate code"),
@@ -126,6 +131,9 @@
     case GenClangAttrLateParsedList:
       ClangAttrLateParsedListEmitter(Records).run(OS);
       break;
+    case GenClangAttrHasTypeArgumentList:
+      ClangAttrHasTypeArgumentListEmitter(Records).run(OS);
+      break;
     case GenClangAttrTemplateInstantiate:
       ClangAttrTemplateInstantiateEmitter(Records).run(OS);
       break;
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp	(revision 150307)
+++ lib/Sema/SemaDeclAttr.cpp	(working copy)
@@ -3185,6 +3185,83 @@
   }
 }
 
+static void handleMPIDatatypeAttr(Sema &S, Scope *Sc, Decl *D,
+                                  const AttributeList &Attr) {
+  if (!Attr.hasTypeArg()) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_type_name)
+        << "mpi_datatype" << 1;
+    return;
+  }
+
+  QualType T = S.GetTypeFromParser(Attr.getTypeArg(), NULL);
+
+  D->addAttr(::new (S.Context) MPIDatatypeAttr(Attr.getRange(),
+                                               S.Context, T));
+}
+
+static void handleMPITypedArgAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  if (!checkAttributeNumArgs(S, Attr, 2))
+    return;
+
+  if (!isFunctionOrMethod(D) || !hasFunctionProto(D)) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type)
+      << Attr.getName() << ExpectedFunctionOrMethod;
+    return;
+  }
+
+  // In C++ the implicit 'this' function parameter also counts.
+  // Parameters are counted from one.
+  const bool HasImplicitThisParam = isInstanceMethod(D);
+  const unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam;
+  const unsigned FirstIdx = 1;
+
+  // Check arg 0 -- BufferIdx.
+  Expr *BufferIdxExpr = Attr.getArg(0);
+  llvm::APSInt BufferIdxInt(32);
+  if (!BufferIdxExpr->isIntegerConstantExpr(BufferIdxInt, S.Context)) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_int)
+      << "mpi_typed_arg" << 1 << BufferIdxExpr->getSourceRange();
+    return;
+  }
+
+  unsigned BufferIdx = BufferIdxInt.getZExtValue();
+  if (BufferIdx < FirstIdx || BufferIdx > NumArgs) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds)
+      << "mpi_typed_arg" << 1 << BufferIdxExpr->getSourceRange();
+    return;
+  }
+  BufferIdx--; // Convert to zero-based.
+
+  // Check arg 1 -- TypeIdx.
+  Expr *TypeIdxExpr = Attr.getArg(1);
+  llvm::APSInt TypeIdxInt(32);
+  if (!TypeIdxExpr->isIntegerConstantExpr(TypeIdxInt, S.Context)) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_int)
+      << "mpi_typed_arg" << 2 << TypeIdxExpr->getSourceRange();
+    return;
+  }
+
+  unsigned TypeIdx = TypeIdxInt.getZExtValue();
+  if (TypeIdx < FirstIdx || TypeIdx > NumArgs) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds)
+      << "mpi_typed_arg" << 2 << TypeIdxExpr->getSourceRange();
+    return;
+  }
+  TypeIdx--; // Convert to zero-based.
+
+  // Ensure that buffer is of a pointer type.
+  QualType BufferTy = getFunctionOrMethodArgType(D, BufferIdx);
+  if (!BufferTy->isPointerType()) {
+    S.Diag(Attr.getLoc(),
+           diag::err_mpi_typed_arg_attribute_buffer_not_pointer);
+  }
+
+  D->addAttr(::new (S.Context) MPITypedArgAttr(Attr.getRange(),
+                                               S.Context,
+                                               BufferIdx,
+                                               TypeIdx));
+}
+
 //===----------------------------------------------------------------------===//
 // Checker-specific attribute handlers.
 //===----------------------------------------------------------------------===//
@@ -3588,6 +3665,12 @@
   case AttributeList::AT_mode:        handleModeAttr        (S, D, Attr); break;
   case AttributeList::AT_malloc:      handleMallocAttr      (S, D, Attr); break;
   case AttributeList::AT_may_alias:   handleMayAliasAttr    (S, D, Attr); break;
+  case AttributeList::AT_mpi_datatype:
+    handleMPIDatatypeAttr(S, scope, D, Attr);
+    break;
+  case AttributeList::AT_mpi_typed_arg:
+    handleMPITypedArgAttr(S, D, Attr);
+    break;
   case AttributeList::AT_nocommon:    handleNoCommonAttr    (S, D, Attr); break;
   case AttributeList::AT_nonnull:     handleNonNullAttr     (S, D, Attr); break;
   case AttributeList::AT_ownership_returns:
Index: lib/Sema/AttributeList.cpp
===================================================================
--- lib/Sema/AttributeList.cpp	(revision 150307)
+++ lib/Sema/AttributeList.cpp	(working copy)
@@ -233,5 +233,7 @@
     .Case("shared_locks_required", AT_shared_locks_required)
     .Case("shared_trylock_function", AT_shared_trylock_function)
     .Case("unlock_function", AT_unlock_function)
+    .Case("mpi_datatype", AT_mpi_datatype)
+    .Case("mpi_typed_arg", AT_mpi_typed_arg)
     .Default(UnknownAttribute);
 }
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp	(revision 150307)
+++ lib/Sema/SemaChecking.cpp	(working copy)
@@ -450,6 +450,12 @@
                           TheCall->getCallee()->getLocStart());
   }
 
+  for (specific_attr_iterator<MPITypedArgAttr>
+         i = FDecl->specific_attr_begin<MPITypedArgAttr>(),
+         e = FDecl->specific_attr_end<MPITypedArgAttr>(); i != e; ++i) {
+    CheckMPITypedArguments(*i, TheCall->getArgs());
+  }
+
   unsigned CMId = FDecl->getMemoryFunctionKind();
   if (CMId == 0)
     return false;
@@ -4827,3 +4833,89 @@
     }
   }
 }
+
+//===--- CHECK: MPI: actual buffer type and MPI_Datatype should match ------//
+
+namespace {
+bool GetSpecifiedMPIType(const Expr *TypeExpr, QualType &Type) {
+  TypeExpr = TypeExpr->IgnoreParenImpCasts()->IgnoreParenCasts();
+
+  // Variable declaration that has mpi_datatype attribute.
+  const ValueDecl *VD = NULL;
+
+  // Check if TypeExpr is an OpenMPI-style predefined MPI_Datatype:
+  //    ((type) &(global))
+  if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(TypeExpr)) {
+    if (UO->getOpcode() == UO_AddrOf) {
+      const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(
+                                          UO->getSubExpr()->IgnoreParens());
+      if (DRE) {
+        // Found declaration of the predefined MPI_Datatype.
+        VD = DRE->getDecl();
+      }
+    }
+  } else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(TypeExpr)) {
+    // User-defined MPI_Datatype: ((type) val)
+    VD = DRE->getDecl();
+  }
+
+  if (VD) {
+    for (specific_attr_iterator<MPIDatatypeAttr>
+            i = VD->specific_attr_begin<MPIDatatypeAttr>(),
+            e = VD->specific_attr_end<MPIDatatypeAttr>(); i != e; ++i) {
+      Type = i->getMatchingCType();
+      return true;
+    }
+  }
+  return false;
+}
+
+bool IsVoidPtrType(QualType T) {
+  return T->isPointerType() && T->getPointeeType()->isVoidType();
+}
+} // unnamed namespace
+
+void Sema::CheckMPITypedArguments(const MPITypedArgAttr *Attr,
+                                  const Expr * const *ExprArgs) {
+  const Expr *TypeExpr = ExprArgs[Attr->getTypeIdx()];
+  QualType SpecifiedType;
+  if (!GetSpecifiedMPIType(TypeExpr, SpecifiedType))
+    return;
+
+  const Expr *BufferExpr = ExprArgs[Attr->getBufferIdx()];
+  // Skip implicit cast of buffer to `void *' (as a function argument).
+  if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(BufferExpr)) {
+    if (IsVoidPtrType(ICE->getType()))
+      BufferExpr = ICE->getSubExpr();
+  }
+  QualType BufferPtrType = BufferExpr->getType();
+  if (!BufferPtrType->isPointerType())
+    return;
+
+  // Passing a void* as a buffer shouldn't trigger a warning.
+  if (IsVoidPtrType(BufferPtrType))
+    return;
+
+  if (SpecifiedType->isVoidType()) {
+    // MPI_DATATYPE_NULL was specified.
+    if (!BufferExpr->isNullPointerConstant(Context,
+                                           Expr::NPC_ValueDependentIsNotNull)) {
+      Diag(BufferExpr->getExprLoc(),
+           diag::warn_mpi_datatype_null_but_buffer_not_null)
+          << BufferExpr->getSourceRange()
+          << TypeExpr->getSourceRange();
+    }
+    return;
+  }
+
+  QualType BufferElementType = BufferPtrType->getPointeeType();
+
+  if (BufferElementType.getCanonicalType() !=
+      SpecifiedType.getCanonicalType()) {
+    Diag(BufferExpr->getExprLoc(), diag::warn_mpi_type_mismatch)
+        << BufferElementType << SpecifiedType
+        << BufferExpr->getSourceRange()
+        << TypeExpr->getSourceRange();
+  }
+}
+
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp	(revision 150307)
+++ lib/Parse/ParseDecl.cpp	(working copy)
@@ -63,6 +63,11 @@
         .Default(false);
 }
 
+static bool AttributeHasTypeArgument(const IdentifierInfo &II) {
+  return llvm::StringSwitch<bool>(II.getName())
+#include "clang/Parse/AttrHasTypeArgument.inc"
+    .Default(false);
+}
 
 /// ParseGNUAttributes - Parse a non-empty attributes list.
 ///
@@ -83,6 +88,7 @@
 ///          attrib-name '(' identifier ')'
 ///          attrib-name '(' identifier ',' nonempty-expr-list ')'
 ///          attrib-name '(' argument-expression-list [C99 6.5.2] ')'
+/// [clang]  attrib-name '(' type-name [C99 6.7.6] ')'
 ///
 /// [GNU]  attrib-name:
 ///          identifier
@@ -148,6 +154,19 @@
           Eof.startToken();
           Eof.setLocation(Tok.getLocation());
           LA->Toks.push_back(Eof);
+        } else if (AttributeHasTypeArgument(*AttrName)) {
+          ConsumeParen();
+          SourceRange Range;
+          TypeResult TypeArg = ParseTypeName(&Range);
+          if (TypeArg.isInvalid()) {
+            SkipUntil(tok::r_paren, false, true);
+          } else {
+            attrs.addNewWithTypeArg(AttrName, AttrNameLoc,
+                                    0, AttrNameLoc,
+                                    TypeArg.take(), Range);
+          }
+          if (ExpectAndConsume(tok::r_paren, diag::err_expected_rparen))
+            SkipUntil(tok::r_paren, false);
         } else {
           ParseGNUAttributeArgs(AttrName, AttrNameLoc, attrs, endLoc);
         }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to