[cfe-commits] r162067 - in /cfe/trunk: docs/ include/clang/Basic/ include/clang/Parse/ include/clang/Sema/ lib/Parse/ lib/Sema/ test/Sema/
Dmitri Gribenko
gribozavr at gmail.com
Thu Aug 16 17:08:38 PDT 2012
Author: gribozavr
Date: Thu Aug 16 19:08:38 2012
New Revision: 162067
URL: http://llvm.org/viewvc/llvm-project?rev=162067&view=rev
Log:
Add support for "type safety" attributes that allow checking that 'void *'
function arguments and arguments for variadic functions are of a particular
type which is determined by some other argument to the same function call.
Usecases include:
* MPI library implementations, where these attributes enable checking that
buffer type matches the passed MPI_Datatype;
* for HDF5 library there is a similar usecase as MPI;
* checking types of variadic functions' arguments for functions like
fcntl() and ioctl().
Added:
cfe/trunk/test/Sema/warn-type-safety-mpi-hdf5.c
cfe/trunk/test/Sema/warn-type-safety.c
cfe/trunk/test/Sema/warn-type-safety.cpp
Modified:
cfe/trunk/docs/LanguageExtensions.html
cfe/trunk/include/clang/Basic/Attr.td
cfe/trunk/include/clang/Basic/DiagnosticGroups.td
cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/include/clang/Parse/Parser.h
cfe/trunk/include/clang/Sema/AttributeList.h
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/Parse/ParseDecl.cpp
cfe/trunk/lib/Sema/AttributeList.cpp
cfe/trunk/lib/Sema/SemaChecking.cpp
cfe/trunk/lib/Sema/SemaDecl.cpp
cfe/trunk/lib/Sema/SemaDeclAttr.cpp
cfe/trunk/test/Sema/128bitint.c
Modified: cfe/trunk/docs/LanguageExtensions.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LanguageExtensions.html?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/docs/LanguageExtensions.html (original)
+++ cfe/trunk/docs/LanguageExtensions.html Thu Aug 16 19:08:38 2012
@@ -142,6 +142,13 @@
<li><a href="#ts_slr"><tt>shared_locks_required(...)</tt></a></li>
</ul>
</li>
+<li><a href="#type_safety">Type Safety Checking</a>
+ <ul>
+ <li><a href="#argument_with_type_tag"><tt>argument_with_type_tag(...)</tt></a></li>
+ <li><a href="#pointer_with_type_tag"><tt>pointer_with_type_tag(...)</tt></a></li>
+ <li><a href="#type_tag_for_datatype"><tt>type_tag_for_datatype(...)</tt></a></li>
+ </ul>
+</li>
</ul>
<!-- ======================================================================= -->
@@ -1913,6 +1920,161 @@
shared locks. Arguments must be lockable type, and there must be at
least one argument.</p>
+<!-- ======================================================================= -->
+<h2 id="type_safety">Type Safety Checking</h2>
+<!-- ======================================================================= -->
+
+<p>Clang supports additional attributes to enable checking type safety
+properties that can't be enforced by C type system. Usecases include:</p>
+<ul>
+<li>MPI library implementations, where these attributes enable checking that
+ buffer type matches the passed <tt>MPI_Datatype</tt>;</li>
+<li>for HDF5 library there is a similar usecase as MPI;</li>
+<li>checking types of variadic functions' arguments for functions like
+ <tt>fcntl()</tt> and <tt>ioctl()</tt>.</li>
+</ul>
+
+<p>You can detect support for these attributes with __has_attribute(). For
+example:</p>
+
+<blockquote>
+<pre>
+#if defined(__has_attribute)
+# if __has_attribute(argument_with_type_tag) && \
+ __has_attribute(pointer_with_type_tag) && \
+ __has_attribute(type_tag_for_datatype)
+# define ATTR_MPI_PWT(buffer_idx, type_idx) __attribute__((pointer_with_type_tag(mpi,buffer_idx,type_idx)))
+/* ... other macros ... */
+# endif
+#endif
+
+#if !defined(ATTR_MPI_PWT)
+#define ATTR_MPI_PWT(buffer_idx, type_idx)
+#endif
+
+int MPI_Send(void *buf, int count, MPI_Datatype datatype /*, other args omitted */)
+ ATTR_MPI_PWT(1,3);
+</pre>
+</blockquote>
+
+<h3 id="argument_with_type_tag"><tt>argument_with_type_tag(...)</tt></h3>
+
+<p>Use <tt>__attribute__((argument_with_type_tag(arg_kind, arg_idx,
+type_tag_idx)))</tt> on a function declaration to specify that the function
+accepts a type tag that determines the type of some other argument.
+<tt>arg_kind</tt> is an identifier that should be used when annotating all
+applicable type tags.</p>
+
+<p>This attribute is primarily useful for checking arguments of variadic
+functions (<tt>pointer_with_type_tag</tt> can be used in most of non-variadic
+cases).</p>
+
+<p>For example:</p>
+<blockquote>
+<pre>
+int fcntl(int fd, int cmd, ...)
+ __attribute__(( argument_with_type_tag(fcntl,3,2) ));
+</pre>
+</blockquote>
+
+<h3 id="pointer_with_type_tag"><tt>pointer_with_type_tag(...)</tt></h3>
+
+<p>Use <tt>__attribute__((pointer_with_type_tag(ptr_kind, ptr_idx,
+type_tag_idx)))</tt> on a function declaration to specify that the
+function a type tag that determines the pointee type of some other pointer
+argument.</p>
+
+<p>For example:</p>
+<blockquote>
+<pre>
+int MPI_Send(void *buf, int count, MPI_Datatype datatype /*, other args omitted */)
+ __attribute__(( pointer_with_type_tag(mpi,1,3) ));
+</pre>
+</blockquote>
+
+<h3 id="type_tag_for_datatype"><tt>type_tag_for_datatype(...)</tt></h3>
+
+<p>Clang supports annotating type tags of two forms.</p>
+
+<ul>
+<li><b>Type tag that is an expression containing a reference to some declared
+identifier.</b> Use <tt>__attribute__((type_tag_for_datatype(kind, type)))</tt>
+on a declaration with that identifier:
+
+<blockquote>
+<pre>
+extern struct mpi_datatype mpi_datatype_int
+ __attribute__(( type_tag_for_datatype(mpi,int) ));
+#define MPI_INT ((MPI_Datatype) &mpi_datatype_int)
+</pre>
+</blockquote></li>
+
+<li><b>Type tag that is an integral literal.</b> Introduce a <tt>static
+const</tt> variable with a corresponding initializer value and attach
+<tt>__attribute__((type_tag_for_datatype(kind, type)))</tt> on that
+declaration, for example:
+
+<blockquote>
+<pre>
+#define MPI_INT ((MPI_Datatype) 42)
+static const MPI_Datatype mpi_datatype_int
+ __attribute__(( type_tag_for_datatype(mpi,int) )) = 42
+</pre>
+</blockquote></li>
+</ul>
+
+<p>The attribute also accepts an optional third argument that determines how
+the expression is compared to the type tag. There are two supported flags:</p>
+
+<ul><li><tt>layout_compatible</tt> will cause types to be compared according to
+layout-compatibility rules (C++11 [class.mem] p 17, 18). This is
+implemented to support annotating types like <tt>MPI_DOUBLE_INT</tt>.
+
+<p>For example:</p>
+<blockquote>
+<pre>
+/* In mpi.h */
+struct internal_mpi_double_int { double d; int i; };
+extern struct mpi_datatype mpi_datatype_double_int
+ __attribute__(( type_tag_for_datatype(mpi, struct internal_mpi_double_int,
+ layout_compatible) ));
+
+#define MPI_DOUBLE_INT ((MPI_Datatype) &mpi_datatype_double_int)
+
+/* In user code */
+struct my_pair { double a; int b; };
+struct my_pair *buffer;
+MPI_Send(buffer, 1, MPI_DOUBLE_INT /*, ... */); // no warning
+
+struct my_int_pair { int a; int b; }
+struct my_int_pair *buffer2;
+MPI_Send(buffer2, 1, MPI_DOUBLE_INT /*, ... */); // warning: actual buffer element
+ // type 'struct my_int_pair'
+ // doesn't match specified MPI_Datatype
+</pre>
+</blockquote>
+</li>
+
+<li><tt>must_be_null</tt> specifies that the expression should be a null
+pointer constant, for example:
+
+<blockquote>
+<pre>
+/* In mpi.h */
+extern struct mpi_datatype mpi_datatype_null
+ __attribute__(( type_tag_for_datatype(mpi, void, must_be_null) ));
+
+#define MPI_DATATYPE_NULL ((MPI_Datatype) &mpi_datatype_null)
+
+/* In user code */
+MPI_Send(buffer, 1, MPI_DATATYPE_NULL /*, ... */); // warning: MPI_DATATYPE_NULL
+ // was specified but buffer
+ // is not a null pointer
+</pre>
+</blockquote>
+</li>
+</ul>
+
</div>
</body>
</html>
Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Thu Aug 16 19:08:38 2012
@@ -826,6 +826,27 @@
let TemplateDependent = 1;
}
+// Type safety attributes for `void *' pointers and type tags.
+
+def ArgumentWithTypeTag : InheritableAttr {
+ let Spellings = [GNU<"argument_with_type_tag">,
+ GNU<"pointer_with_type_tag">];
+ let Args = [IdentifierArgument<"ArgumentKind">,
+ UnsignedArgument<"ArgumentIdx">,
+ UnsignedArgument<"TypeTagIdx">,
+ BoolArgument<"IsPointer">];
+ let Subjects = [Function];
+}
+
+def TypeTagForDatatype : InheritableAttr {
+ let Spellings = [GNU<"type_tag_for_datatype">];
+ let Args = [IdentifierArgument<"ArgumentKind">,
+ TypeArgument<"MatchingCType">,
+ BoolArgument<"LayoutCompatible">,
+ BoolArgument<"MustBeNull">];
+ let Subjects = [Var];
+}
+
// Microsoft-related attributes
def MsStruct : InheritableAttr {
Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Thu Aug 16 19:08:38 2012
@@ -343,6 +343,8 @@
def Format2 : DiagGroup<"format=2",
[FormatNonLiteral, FormatSecurity, FormatY2K]>;
+def TypeSafety : DiagGroup<"type-safety">;
+
def Extra : DiagGroup<"extra", [
MissingFieldInitializers,
IgnoredQualifiers,
Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Thu Aug 16 19:08:38 2012
@@ -677,6 +677,10 @@
"'unavailable' availability overrides all other availability information">,
InGroup<Availability>;
+// Type safety attributes
+def err_type_safety_unknown_flag : Error<
+ "invalid comparison flag %0; use 'layout_compatible' or 'must_be_null'">;
+
// Language specific pragmas
// - Generic warnings
def warn_pragma_expected_lparen : Warning<
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Aug 16 19:08:38 2012
@@ -1547,6 +1547,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_identifier : Error<
+ "'%0' attribute requires parameter %1 to be an identifier">;
def err_attribute_argument_out_of_bounds : Error<
"'%0' attribute parameter %1 is out of bounds">;
def err_attribute_requires_objc_interface : Error<
@@ -1555,6 +1557,8 @@
"uuid attribute contains a malformed GUID">;
def warn_nonnull_pointers_only : Warning<
"nonnull attribute only applies to pointer arguments">;
+def err_attribute_pointers_only : Error<
+ "'%0' attribute only applies to pointer arguments">;
def err_attribute_invalid_implicit_this_argument : Error<
"'%0' attribute is invalid for the implicit this argument">;
def err_ownership_type : Error<
@@ -1770,7 +1774,6 @@
def warn_attribute_not_on_decl : Error<
"%0 attribute ignored when parsing type">;
-
// Availability attribute
def warn_availability_unknown_platform : Warning<
"unknown platform %0 in availability macro">, InGroup<Availability>;
@@ -5479,6 +5482,23 @@
"assigning %select{field|instance variable}0 to itself">,
InGroup<SelfAssignmentField>;
+// Type safety attributes
+def err_type_tag_for_datatype_not_ice : Error<
+ "'type_tag_for_datatype' attribute requires the initializer to be "
+ "an %select{integer|integral}0 constant expression">;
+def err_type_tag_for_datatype_too_large : Error<
+ "'type_tag_for_datatype' attribute requires the initializer to be "
+ "an %select{integer|integral}0 constant expression "
+ "that can be represented by a 64 bit integer">;
+def warn_type_tag_for_datatype_wrong_kind : Warning<
+ "this type tag was not designed to be used with this function">,
+ InGroup<TypeSafety>;
+def warn_type_safety_type_mismatch : Warning<
+ "argument type %0 doesn't match specified '%1' type tag "
+ "%select{that requires %3|}2">, InGroup<TypeSafety>;
+def warn_type_safety_null_pointer_required : Warning<
+ "specified %0 type tag requires a null pointer">, InGroup<TypeSafety>;
+
// Generic selections.
def err_assoc_type_incomplete : Error<
"type %0 in generic association incomplete">;
Modified: cfe/trunk/include/clang/Parse/Parser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Parser.h?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Parser.h (original)
+++ cfe/trunk/include/clang/Parse/Parser.h Thu Aug 16 19:08:38 2012
@@ -1834,6 +1834,10 @@
ParsedAttributes &Attrs,
SourceLocation *EndLoc);
+ void ParseTypeTagForDatatypeAttribute(IdentifierInfo &AttrName,
+ SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs,
+ SourceLocation *EndLoc);
void ParseTypeofSpecifier(DeclSpec &DS);
SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
Modified: cfe/trunk/include/clang/Sema/AttributeList.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AttributeList.h?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/AttributeList.h (original)
+++ cfe/trunk/include/clang/Sema/AttributeList.h Thu Aug 16 19:08:38 2012
@@ -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 {
@@ -87,6 +88,10 @@
/// availability attribute.
unsigned IsAvailability : 1;
+ /// True if this has extra information associated with a
+ /// type_tag_for_datatype attribute.
+ unsigned IsTypeTagForDatatype : 1;
+
unsigned AttrKind : 8;
/// \brief The location of the 'unavailable' keyword in an
@@ -119,6 +124,22 @@
return reinterpret_cast<const AvailabilityChange*>(this+1)[index];
}
+public:
+ struct TypeTagForDatatypeData {
+ ParsedType *MatchingCType;
+ unsigned LayoutCompatible : 1;
+ unsigned MustBeNull : 1;
+ };
+
+private:
+ TypeTagForDatatypeData &getTypeTagForDatatypeDataSlot() {
+ return *reinterpret_cast<TypeTagForDatatypeData *>(this + 1);
+ }
+
+ const TypeTagForDatatypeData &getTypeTagForDatatypeDataSlot() const {
+ return *reinterpret_cast<const TypeTagForDatatypeData *>(this + 1);
+ }
+
AttributeList(const AttributeList &); // DO NOT IMPLEMENT
void operator=(const AttributeList &); // DO NOT IMPLEMENT
void operator delete(void *); // DO NOT IMPLEMENT
@@ -126,6 +147,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,
@@ -134,12 +156,13 @@
: AttrName(attrName), ScopeName(scopeName), ParmName(parmName),
AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc),
NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false),
- UsedAsTypeAttr(false), IsAvailability(false),
- NextInPosition(0), NextInPool(0) {
+ UsedAsTypeAttr(false), IsAvailability(false),
+ IsTypeTagForDatatype(false), NextInPosition(0), NextInPool(0) {
if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*));
AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
}
+ /// Constructor for availability attributes.
AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc,
@@ -153,6 +176,7 @@
AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc),
NumArgs(0), SyntaxUsed(syntaxUsed),
Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
+ IsTypeTagForDatatype(false),
UnavailableLoc(unavailable), MessageExpr(messageExpr),
NextInPosition(0), NextInPool(0) {
new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced);
@@ -161,6 +185,25 @@
AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
}
+ /// Constructor for type_tag_for_datatype attribute.
+ AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
+ IdentifierInfo *scopeName, SourceLocation scopeLoc,
+ IdentifierInfo *argumentKindName,
+ SourceLocation argumentKindLoc,
+ ParsedType matchingCType, bool layoutCompatible,
+ bool mustBeNull, Syntax syntaxUsed)
+ : AttrName(attrName), ScopeName(scopeName), ParmName(argumentKindName),
+ AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(argumentKindLoc),
+ NumArgs(0), SyntaxUsed(syntaxUsed),
+ Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
+ IsTypeTagForDatatype(true), NextInPosition(NULL), NextInPool(NULL) {
+ TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot();
+ new (&ExtraData.MatchingCType) ParsedType(matchingCType);
+ ExtraData.LayoutCompatible = layoutCompatible;
+ ExtraData.MustBeNull = mustBeNull;
+ AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
+ }
+
friend class AttributePool;
friend class AttributeFactory;
@@ -279,6 +322,24 @@
assert(getKind() == AT_Availability && "Not an availability attribute");
return MessageExpr;
}
+
+ const ParsedType &getMatchingCType() const {
+ assert(getKind() == AT_TypeTagForDatatype &&
+ "Not a type_tag_for_datatype attribute");
+ return *getTypeTagForDatatypeDataSlot().MatchingCType;
+ }
+
+ bool getLayoutCompatible() const {
+ assert(getKind() == AT_TypeTagForDatatype &&
+ "Not a type_tag_for_datatype attribute");
+ return getTypeTagForDatatypeDataSlot().LayoutCompatible;
+ }
+
+ bool getMustBeNull() const {
+ assert(getKind() == AT_TypeTagForDatatype &&
+ "Not a type_tag_for_datatype attribute");
+ return getTypeTagForDatatypeDataSlot().MustBeNull;
+ }
};
/// A factory, from which one makes pools, from which one creates
@@ -294,7 +355,11 @@
AvailabilityAllocSize =
sizeof(AttributeList)
+ ((3 * sizeof(AvailabilityChange) + sizeof(void*) - 1)
- / sizeof(void*) * sizeof(void*))
+ / sizeof(void*) * sizeof(void*)),
+ TypeTagForDatatypeAllocSize =
+ sizeof(AttributeList)
+ + (sizeof(AttributeList::TypeTagForDatatypeData) + sizeof(void *) - 1)
+ / sizeof(void*) * sizeof(void*)
};
private:
@@ -411,6 +476,21 @@
AttributeList *createIntegerAttribute(ASTContext &C, IdentifierInfo *Name,
SourceLocation TokLoc, int Arg);
+
+ AttributeList *createTypeTagForDatatype(
+ IdentifierInfo *attrName, SourceRange attrRange,
+ IdentifierInfo *scopeName, SourceLocation scopeLoc,
+ IdentifierInfo *argumentKindName,
+ SourceLocation argumentKindLoc,
+ ParsedType matchingCType, bool layoutCompatible,
+ bool mustBeNull, AttributeList::Syntax syntax) {
+ void *memory = allocate(AttributeFactory::TypeTagForDatatypeAllocSize);
+ return add(new (memory) AttributeList(attrName, attrRange,
+ scopeName, scopeLoc,
+ argumentKindName, argumentKindLoc,
+ matchingCType, layoutCompatible,
+ mustBeNull, syntax));
+ }
};
/// addAttributeLists - Add two AttributeLists together
@@ -503,7 +583,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,
@@ -516,6 +596,7 @@
return attr;
}
+ /// Add availability attribute.
AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc,
@@ -533,6 +614,24 @@
return attr;
}
+ /// Add type_tag_for_datatype attribute.
+ AttributeList *addNewTypeTagForDatatype(
+ IdentifierInfo *attrName, SourceRange attrRange,
+ IdentifierInfo *scopeName, SourceLocation scopeLoc,
+ IdentifierInfo *argumentKindName,
+ SourceLocation argumentKindLoc,
+ ParsedType matchingCType, bool layoutCompatible,
+ bool mustBeNull, AttributeList::Syntax syntax) {
+ AttributeList *attr =
+ pool.createTypeTagForDatatype(attrName, attrRange,
+ scopeName, scopeLoc,
+ argumentKindName, argumentKindLoc,
+ matchingCType, layoutCompatible,
+ mustBeNull, syntax);
+ add(attr);
+ return attr;
+ }
+
AttributeList *addNewInteger(ASTContext &C, IdentifierInfo *name,
SourceLocation loc, int arg) {
AttributeList *attr =
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Aug 16 19:08:38 2012
@@ -7145,6 +7145,42 @@
void CheckBitFieldInitialization(SourceLocation InitLoc, FieldDecl *Field,
Expr *Init);
+public:
+ /// \brief Register a magic integral constant to be used as a type tag.
+ void RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,
+ uint64_t MagicValue, QualType Type,
+ bool LayoutCompatible, bool MustBeNull);
+
+ struct TypeTagData {
+ TypeTagData() {}
+
+ TypeTagData(QualType Type, bool LayoutCompatible, bool MustBeNull) :
+ Type(Type), LayoutCompatible(LayoutCompatible),
+ MustBeNull(MustBeNull)
+ {}
+
+ QualType Type;
+
+ /// If true, \c Type should be compared with other expression's types for
+ /// layout-compatibility.
+ unsigned LayoutCompatible : 1;
+ unsigned MustBeNull : 1;
+ };
+
+ /// A pair of ArgumentKind identifier and magic value. This uniquely
+ /// identifies the magic value.
+ typedef std::pair<const IdentifierInfo *, uint64_t> TypeTagMagicValue;
+
+private:
+ /// \brief A map from magic value to type information.
+ OwningPtr<llvm::DenseMap<TypeTagMagicValue, TypeTagData> >
+ TypeTagForDatatypeMagicValues;
+
+ /// \brief Peform checks on a call of a function with argument_with_type_tag
+ /// or pointer_with_type_tag attributes.
+ void CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
+ const Expr * const *ExprArgs);
+
/// \brief The parser's current scope.
///
/// The parser maintains this state here.
Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Thu Aug 16 19:08:38 2012
@@ -68,7 +68,6 @@
.Default(false);
}
-
/// ParseGNUAttributes - Parse a non-empty attributes list.
///
/// [GNU] attributes:
@@ -193,6 +192,11 @@
ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
return;
}
+ // Type safety attributes have their own grammar.
+ if (AttrName->isStr("type_tag_for_datatype")) {
+ ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
+ return;
+ }
ConsumeParen(); // ignore the left paren loc for now
@@ -1020,6 +1024,70 @@
*EndLoc = T.getCloseLocation();
}
+void Parser::ParseTypeTagForDatatypeAttribute(IdentifierInfo &AttrName,
+ SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs,
+ SourceLocation *EndLoc) {
+ assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('");
+
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ T.consumeOpen();
+
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected_ident);
+ T.skipToEnd();
+ return;
+ }
+ IdentifierInfo *ArgumentKind = Tok.getIdentifierInfo();
+ SourceLocation ArgumentKindLoc = ConsumeToken();
+
+ if (Tok.isNot(tok::comma)) {
+ Diag(Tok, diag::err_expected_comma);
+ T.skipToEnd();
+ return;
+ }
+ ConsumeToken();
+
+ SourceRange MatchingCTypeRange;
+ TypeResult MatchingCType = ParseTypeName(&MatchingCTypeRange);
+ if (MatchingCType.isInvalid()) {
+ T.skipToEnd();
+ return;
+ }
+
+ bool LayoutCompatible = false;
+ bool MustBeNull = false;
+ while (Tok.is(tok::comma)) {
+ ConsumeToken();
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_expected_ident);
+ T.skipToEnd();
+ return;
+ }
+ IdentifierInfo *Flag = Tok.getIdentifierInfo();
+ if (Flag->isStr("layout_compatible"))
+ LayoutCompatible = true;
+ else if (Flag->isStr("must_be_null"))
+ MustBeNull = true;
+ else {
+ Diag(Tok, diag::err_type_safety_unknown_flag) << Flag;
+ T.skipToEnd();
+ return;
+ }
+ ConsumeToken(); // consume flag
+ }
+
+ if (!T.consumeClose()) {
+ Attrs.addNewTypeTagForDatatype(&AttrName, AttrNameLoc, 0, AttrNameLoc,
+ ArgumentKind, ArgumentKindLoc,
+ MatchingCType.release(), LayoutCompatible,
+ MustBeNull, AttributeList::AS_GNU);
+ }
+
+ if (EndLoc)
+ *EndLoc = T.getCloseLocation();
+}
+
/// DiagnoseProhibitedCXX11Attribute - We have found the opening square brackets
/// of a C++11 attribute-specifier in a location where an attribute is not
/// permitted. By C++11 [dcl.attr.grammar]p6, this is ill-formed. Diagnose this
Modified: cfe/trunk/lib/Sema/AttributeList.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AttributeList.cpp?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AttributeList.cpp (original)
+++ cfe/trunk/lib/Sema/AttributeList.cpp Thu Aug 16 19:08:38 2012
@@ -21,6 +21,8 @@
size_t AttributeList::allocated_size() const {
if (IsAvailability) return AttributeFactory::AvailabilityAllocSize;
+ else if (IsTypeTagForDatatype)
+ return AttributeFactory::TypeTagForDatatypeAllocSize;
return (sizeof(AttributeList) + NumArgs * sizeof(Expr*));
}
Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Thu Aug 16 19:08:38 2012
@@ -513,6 +513,13 @@
I = FDecl->specific_attr_begin<NonNullAttr>(),
E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I)
CheckNonNullArguments(*I, Args, Loc);
+
+ // Type safety checking.
+ for (specific_attr_iterator<ArgumentWithTypeTagAttr>
+ i = FDecl->specific_attr_begin<ArgumentWithTypeTagAttr>(),
+ e = FDecl->specific_attr_end<ArgumentWithTypeTagAttr>(); i != e; ++i) {
+ CheckArgumentWithTypeTag(*i, Args);
+ }
}
/// CheckConstructorCall - Check a constructor call for correctness and safety
@@ -5468,3 +5475,410 @@
Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line);
}
}
+
+//===--- Layout compatibility ----------------------------------------------//
+
+namespace {
+
+bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2);
+
+/// \brief Check if two enumeration types are layout-compatible.
+bool isLayoutCompatible(ASTContext &C, EnumDecl *ED1, EnumDecl *ED2) {
+ // C++11 [dcl.enum] p8:
+ // Two enumeration types are layout-compatible if they have the same
+ // underlying type.
+ return ED1->isComplete() && ED2->isComplete() &&
+ C.hasSameType(ED1->getIntegerType(), ED2->getIntegerType());
+}
+
+/// \brief Check if two fields are layout-compatible.
+bool isLayoutCompatible(ASTContext &C, FieldDecl *Field1, FieldDecl *Field2) {
+ if (!isLayoutCompatible(C, Field1->getType(), Field2->getType()))
+ return false;
+
+ if (Field1->isBitField() != Field2->isBitField())
+ return false;
+
+ if (Field1->isBitField()) {
+ // Make sure that the bit-fields are the same length.
+ unsigned Bits1 = Field1->getBitWidthValue(C);
+ unsigned Bits2 = Field2->getBitWidthValue(C);
+
+ if (Bits1 != Bits2)
+ return false;
+ }
+
+ return true;
+}
+
+/// \brief Check if two standard-layout structs are layout-compatible.
+/// (C++11 [class.mem] p17)
+bool isLayoutCompatibleStruct(ASTContext &C,
+ RecordDecl *RD1,
+ RecordDecl *RD2) {
+ // If both records are C++ classes, check that base classes match.
+ if (const CXXRecordDecl *D1CXX = dyn_cast<CXXRecordDecl>(RD1)) {
+ // If one of records is a CXXRecordDecl we are in C++ mode,
+ // thus the other one is a CXXRecordDecl, too.
+ const CXXRecordDecl *D2CXX = cast<CXXRecordDecl>(RD2);
+ // Check number of base classes.
+ if (D1CXX->getNumBases() != D2CXX->getNumBases())
+ return false;
+
+ // Check the base classes.
+ for (CXXRecordDecl::base_class_const_iterator
+ Base1 = D1CXX->bases_begin(),
+ BaseEnd1 = D1CXX->bases_end(),
+ Base2 = D2CXX->bases_begin();
+ Base1 != BaseEnd1;
+ ++Base1, ++Base2) {
+ if (!isLayoutCompatible(C, Base1->getType(), Base2->getType()))
+ return false;
+ }
+ } else if (const CXXRecordDecl *D2CXX = dyn_cast<CXXRecordDecl>(RD2)) {
+ // If only RD2 is a C++ class, it should have zero base classes.
+ if (D2CXX->getNumBases() > 0)
+ return false;
+ }
+
+ // Check the fields.
+ RecordDecl::field_iterator Field2 = RD2->field_begin(),
+ Field2End = RD2->field_end(),
+ Field1 = RD1->field_begin(),
+ Field1End = RD1->field_end();
+ for ( ; Field1 != Field1End && Field2 != Field2End; ++Field1, ++Field2) {
+ if (!isLayoutCompatible(C, *Field1, *Field2))
+ return false;
+ }
+ if (Field1 != Field1End || Field2 != Field2End)
+ return false;
+
+ return true;
+}
+
+/// \brief Check if two standard-layout unions are layout-compatible.
+/// (C++11 [class.mem] p18)
+bool isLayoutCompatibleUnion(ASTContext &C,
+ RecordDecl *RD1,
+ RecordDecl *RD2) {
+ llvm::SmallPtrSet<FieldDecl *, 8> UnmatchedFields;
+ for (RecordDecl::field_iterator Field2 = RD2->field_begin(),
+ Field2End = RD2->field_end();
+ Field2 != Field2End; ++Field2) {
+ UnmatchedFields.insert(*Field2);
+ }
+
+ for (RecordDecl::field_iterator Field1 = RD1->field_begin(),
+ Field1End = RD1->field_end();
+ Field1 != Field1End; ++Field1) {
+ llvm::SmallPtrSet<FieldDecl *, 8>::iterator
+ I = UnmatchedFields.begin(),
+ E = UnmatchedFields.end();
+
+ for ( ; I != E; ++I) {
+ if (isLayoutCompatible(C, *Field1, *I)) {
+ bool Result = UnmatchedFields.erase(*I);
+ (void) Result;
+ assert(Result);
+ break;
+ }
+ }
+ if (I == E)
+ return false;
+ }
+
+ return UnmatchedFields.empty();
+}
+
+bool isLayoutCompatible(ASTContext &C, RecordDecl *RD1, RecordDecl *RD2) {
+ if (RD1->isUnion() != RD2->isUnion())
+ return false;
+
+ if (RD1->isUnion())
+ return isLayoutCompatibleUnion(C, RD1, RD2);
+ else
+ return isLayoutCompatibleStruct(C, RD1, RD2);
+}
+
+/// \brief Check if two types are layout-compatible in C++11 sense.
+bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2) {
+ if (T1.isNull() || T2.isNull())
+ return false;
+
+ // C++11 [basic.types] p11:
+ // If two types T1 and T2 are the same type, then T1 and T2 are
+ // layout-compatible types.
+ if (C.hasSameType(T1, T2))
+ return true;
+
+ T1 = T1.getCanonicalType().getUnqualifiedType();
+ T2 = T2.getCanonicalType().getUnqualifiedType();
+
+ const Type::TypeClass TC1 = T1->getTypeClass();
+ const Type::TypeClass TC2 = T2->getTypeClass();
+
+ if (TC1 != TC2)
+ return false;
+
+ if (TC1 == Type::Enum) {
+ return isLayoutCompatible(C,
+ cast<EnumType>(T1)->getDecl(),
+ cast<EnumType>(T2)->getDecl());
+ } else if (TC1 == Type::Record) {
+ if (!T1->isStandardLayoutType() || !T2->isStandardLayoutType())
+ return false;
+
+ return isLayoutCompatible(C,
+ cast<RecordType>(T1)->getDecl(),
+ cast<RecordType>(T2)->getDecl());
+ }
+
+ return false;
+}
+}
+
+//===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----//
+
+namespace {
+/// \brief Given a type tag expression find the type tag itself.
+///
+/// \param TypeExpr Type tag expression, as it appears in user's code.
+///
+/// \param VD Declaration of an identifier that appears in a type tag.
+///
+/// \param MagicValue Type tag magic value.
+bool FindTypeTagExpr(const Expr *TypeExpr, const ASTContext &Ctx,
+ const ValueDecl **VD, uint64_t *MagicValue) {
+ while(true) {
+ if (!TypeExpr)
+ return false;
+
+ TypeExpr = TypeExpr->IgnoreParenImpCasts()->IgnoreParenCasts();
+
+ switch (TypeExpr->getStmtClass()) {
+ case Stmt::UnaryOperatorClass: {
+ const UnaryOperator *UO = cast<UnaryOperator>(TypeExpr);
+ if (UO->getOpcode() == UO_AddrOf || UO->getOpcode() == UO_Deref) {
+ TypeExpr = UO->getSubExpr();
+ continue;
+ }
+ return false;
+ }
+
+ case Stmt::DeclRefExprClass: {
+ const DeclRefExpr *DRE = cast<DeclRefExpr>(TypeExpr);
+ *VD = DRE->getDecl();
+ return true;
+ }
+
+ case Stmt::IntegerLiteralClass: {
+ const IntegerLiteral *IL = cast<IntegerLiteral>(TypeExpr);
+ llvm::APInt MagicValueAPInt = IL->getValue();
+ if (MagicValueAPInt.getActiveBits() <= 64) {
+ *MagicValue = MagicValueAPInt.getZExtValue();
+ return true;
+ } else
+ return false;
+ }
+
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass: {
+ const AbstractConditionalOperator *ACO =
+ cast<AbstractConditionalOperator>(TypeExpr);
+ bool Result;
+ if (ACO->getCond()->EvaluateAsBooleanCondition(Result, Ctx)) {
+ if (Result)
+ TypeExpr = ACO->getTrueExpr();
+ else
+ TypeExpr = ACO->getFalseExpr();
+ continue;
+ }
+ return false;
+ }
+
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *BO = cast<BinaryOperator>(TypeExpr);
+ if (BO->getOpcode() == BO_Comma) {
+ TypeExpr = BO->getRHS();
+ continue;
+ }
+ return false;
+ }
+
+ default:
+ return false;
+ }
+ }
+}
+
+/// \brief Retrieve the C type corresponding to type tag TypeExpr.
+///
+/// \param TypeExpr Expression that specifies a type tag.
+///
+/// \param MagicValues Registered magic values.
+///
+/// \param FoundWrongKind Set to true if a type tag was found, but of a wrong
+/// kind.
+///
+/// \param TypeInfo Information about the corresponding C type.
+///
+/// \returns true if the corresponding C type was found.
+bool GetMatchingCType(
+ const IdentifierInfo *ArgumentKind,
+ const Expr *TypeExpr, const ASTContext &Ctx,
+ const llvm::DenseMap<Sema::TypeTagMagicValue,
+ Sema::TypeTagData> *MagicValues,
+ bool &FoundWrongKind,
+ Sema::TypeTagData &TypeInfo) {
+ FoundWrongKind = false;
+
+ // Variable declaration that has type_tag_for_datatype attribute.
+ const ValueDecl *VD = NULL;
+
+ uint64_t MagicValue;
+
+ if (!FindTypeTagExpr(TypeExpr, Ctx, &VD, &MagicValue))
+ return false;
+
+ if (VD) {
+ for (specific_attr_iterator<TypeTagForDatatypeAttr>
+ I = VD->specific_attr_begin<TypeTagForDatatypeAttr>(),
+ E = VD->specific_attr_end<TypeTagForDatatypeAttr>();
+ I != E; ++I) {
+ if (I->getArgumentKind() != ArgumentKind) {
+ FoundWrongKind = true;
+ return false;
+ }
+ TypeInfo.Type = I->getMatchingCType();
+ TypeInfo.LayoutCompatible = I->getLayoutCompatible();
+ TypeInfo.MustBeNull = I->getMustBeNull();
+ return true;
+ }
+ return false;
+ }
+
+ if (!MagicValues)
+ return false;
+
+ llvm::DenseMap<Sema::TypeTagMagicValue,
+ Sema::TypeTagData>::const_iterator I =
+ MagicValues->find(std::make_pair(ArgumentKind, MagicValue));
+ if (I == MagicValues->end())
+ return false;
+
+ TypeInfo = I->second;
+ return true;
+}
+} // unnamed namespace
+
+void Sema::RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,
+ uint64_t MagicValue, QualType Type,
+ bool LayoutCompatible,
+ bool MustBeNull) {
+ if (!TypeTagForDatatypeMagicValues)
+ TypeTagForDatatypeMagicValues.reset(
+ new llvm::DenseMap<TypeTagMagicValue, TypeTagData>);
+
+ TypeTagMagicValue Magic(ArgumentKind, MagicValue);
+ (*TypeTagForDatatypeMagicValues)[Magic] =
+ TypeTagData(Type, LayoutCompatible, MustBeNull);
+}
+
+namespace {
+bool IsSameCharType(QualType T1, QualType T2) {
+ const BuiltinType *BT1 = T1->getAs<BuiltinType>();
+ if (!BT1)
+ return false;
+
+ const BuiltinType *BT2 = T2->getAs<BuiltinType>();
+ if (!BT2)
+ return false;
+
+ BuiltinType::Kind T1Kind = BT1->getKind();
+ BuiltinType::Kind T2Kind = BT2->getKind();
+
+ return (T1Kind == BuiltinType::SChar && T2Kind == BuiltinType::Char_S) ||
+ (T1Kind == BuiltinType::UChar && T2Kind == BuiltinType::Char_U) ||
+ (T1Kind == BuiltinType::Char_U && T2Kind == BuiltinType::UChar) ||
+ (T1Kind == BuiltinType::Char_S && T2Kind == BuiltinType::SChar);
+}
+} // unnamed namespace
+
+void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
+ const Expr * const *ExprArgs) {
+ const IdentifierInfo *ArgumentKind = Attr->getArgumentKind();
+ bool IsPointerAttr = Attr->getIsPointer();
+
+ const Expr *TypeTagExpr = ExprArgs[Attr->getTypeTagIdx()];
+ bool FoundWrongKind;
+ TypeTagData TypeInfo;
+ if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context,
+ TypeTagForDatatypeMagicValues.get(),
+ FoundWrongKind, TypeInfo)) {
+ if (FoundWrongKind)
+ Diag(TypeTagExpr->getExprLoc(),
+ diag::warn_type_tag_for_datatype_wrong_kind)
+ << TypeTagExpr->getSourceRange();
+ return;
+ }
+
+ const Expr *ArgumentExpr = ExprArgs[Attr->getArgumentIdx()];
+ if (IsPointerAttr) {
+ // Skip implicit cast of pointer to `void *' (as a function argument).
+ if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgumentExpr))
+ if (ICE->getType()->isVoidPointerType())
+ ArgumentExpr = ICE->getSubExpr();
+ }
+ QualType ArgumentType = ArgumentExpr->getType();
+
+ // Passing a `void*' pointer shouldn't trigger a warning.
+ if (IsPointerAttr && ArgumentType->isVoidPointerType())
+ return;
+
+ if (TypeInfo.MustBeNull) {
+ // Type tag with matching void type requires a null pointer.
+ if (!ArgumentExpr->isNullPointerConstant(Context,
+ Expr::NPC_ValueDependentIsNotNull)) {
+ Diag(ArgumentExpr->getExprLoc(),
+ diag::warn_type_safety_null_pointer_required)
+ << ArgumentKind->getName()
+ << ArgumentExpr->getSourceRange()
+ << TypeTagExpr->getSourceRange();
+ }
+ return;
+ }
+
+ QualType RequiredType = TypeInfo.Type;
+ if (IsPointerAttr)
+ RequiredType = Context.getPointerType(RequiredType);
+
+ bool mismatch = false;
+ if (!TypeInfo.LayoutCompatible) {
+ mismatch = !Context.hasSameType(ArgumentType, RequiredType);
+
+ // C++11 [basic.fundamental] p1:
+ // Plain char, signed char, and unsigned char are three distinct types.
+ //
+ // But we treat plain `char' as equivalent to `signed char' or `unsigned
+ // char' depending on the current char signedness mode.
+ if (mismatch)
+ if ((IsPointerAttr && IsSameCharType(ArgumentType->getPointeeType(),
+ RequiredType->getPointeeType())) ||
+ (!IsPointerAttr && IsSameCharType(ArgumentType, RequiredType)))
+ mismatch = false;
+ } else
+ if (IsPointerAttr)
+ mismatch = !isLayoutCompatible(Context,
+ ArgumentType->getPointeeType(),
+ RequiredType->getPointeeType());
+ else
+ mismatch = !isLayoutCompatible(Context, ArgumentType, RequiredType);
+
+ if (mismatch)
+ Diag(ArgumentExpr->getExprLoc(), diag::warn_type_safety_type_mismatch)
+ << ArgumentType << ArgumentKind->getName()
+ << TypeInfo.LayoutCompatible << RequiredType
+ << ArgumentExpr->getSourceRange()
+ << TypeTagExpr->getSourceRange();
+}
+
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Thu Aug 16 19:08:38 2012
@@ -7031,6 +7031,42 @@
Sema::FinalizeDeclaration(Decl *ThisDecl) {
// Note that we are no longer parsing the initializer for this declaration.
ParsingInitForAutoVars.erase(ThisDecl);
+
+ // Now we have parsed the initializer and can update the table of magic
+ // tag values.
+ if (ThisDecl && ThisDecl->hasAttr<TypeTagForDatatypeAttr>()) {
+ const VarDecl *VD = dyn_cast<VarDecl>(ThisDecl);
+ if (VD && VD->getType()->isIntegralOrEnumerationType()) {
+ for (specific_attr_iterator<TypeTagForDatatypeAttr>
+ I = ThisDecl->specific_attr_begin<TypeTagForDatatypeAttr>(),
+ E = ThisDecl->specific_attr_end<TypeTagForDatatypeAttr>();
+ I != E; ++I) {
+ const Expr *MagicValueExpr = VD->getInit();
+ if (!MagicValueExpr) {
+ continue;
+ }
+ llvm::APSInt MagicValueInt;
+ if (!MagicValueExpr->isIntegerConstantExpr(MagicValueInt, Context)) {
+ Diag(I->getRange().getBegin(),
+ diag::err_type_tag_for_datatype_not_ice)
+ << LangOpts.CPlusPlus << MagicValueExpr->getSourceRange();
+ continue;
+ }
+ if (MagicValueInt.getActiveBits() > 64) {
+ Diag(I->getRange().getBegin(),
+ diag::err_type_tag_for_datatype_too_large)
+ << LangOpts.CPlusPlus << MagicValueExpr->getSourceRange();
+ continue;
+ }
+ uint64_t MagicValue = MagicValueInt.getZExtValue();
+ RegisterTypeTagForDatatype(I->getArgumentKind(),
+ MagicValue,
+ I->getMatchingCType(),
+ I->getLayoutCompatible(),
+ I->getMustBeNull());
+ }
+ }
+ }
}
Sema::DeclGroupPtrTy
Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Thu Aug 16 19:08:38 2012
@@ -221,6 +221,53 @@
return true;
}
+/// \brief Check if IdxExpr is a valid argument index for a function or
+/// instance method D. May output an error.
+///
+/// \returns true if IdxExpr is a valid index.
+static bool checkFunctionOrMethodArgumentIndex(Sema &S, const Decl *D,
+ StringRef AttrName,
+ SourceLocation AttrLoc,
+ unsigned AttrArgNum,
+ const Expr *IdxExpr,
+ uint64_t &Idx)
+{
+ assert(isFunctionOrMethod(D) && hasFunctionProto(D));
+
+ // 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;
+
+ llvm::APSInt IdxInt;
+ if (IdxExpr->isTypeDependent() || IdxExpr->isValueDependent() ||
+ !IdxExpr->isIntegerConstantExpr(IdxInt, S.Context)) {
+ S.Diag(AttrLoc, diag::err_attribute_argument_n_not_int)
+ << AttrName << AttrArgNum << IdxExpr->getSourceRange();
+ return false;
+ }
+
+ Idx = IdxInt.getLimitedValue();
+ if (Idx < FirstIdx || (!isFunctionOrMethodVariadic(D) && Idx > NumArgs)) {
+ S.Diag(AttrLoc, diag::err_attribute_argument_out_of_bounds)
+ << AttrName << AttrArgNum << IdxExpr->getSourceRange();
+ return false;
+ }
+ Idx--; // Convert to zero-based.
+ if (HasImplicitThisParam) {
+ if (Idx == 0) {
+ S.Diag(AttrLoc,
+ diag::err_attribute_invalid_implicit_this_argument)
+ << AttrName << IdxExpr->getSourceRange();
+ return false;
+ }
+ --Idx;
+ }
+
+ return true;
+}
+
///
/// \brief Check if passed in Decl is a field or potentially shared global var
/// \return true if the Decl is a field or potentially shared global variable
@@ -3696,6 +3743,79 @@
}
}
+static void handleArgumentWithTypeTagAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ StringRef AttrName = Attr.getName()->getName();
+ if (!Attr.getParameterName()) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_identifier)
+ << Attr.getName() << /* arg num = */ 1;
+ return;
+ }
+
+ if (Attr.getNumArgs() != 2) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments)
+ << /* required args = */ 3;
+ return;
+ }
+
+ IdentifierInfo *ArgumentKind = Attr.getParameterName();
+
+ if (!isFunctionOrMethod(D) || !hasFunctionProto(D)) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type)
+ << Attr.getName() << ExpectedFunctionOrMethod;
+ return;
+ }
+
+ uint64_t ArgumentIdx;
+ if (!checkFunctionOrMethodArgumentIndex(S, D, AttrName,
+ Attr.getLoc(), 2,
+ Attr.getArg(0), ArgumentIdx))
+ return;
+
+ uint64_t TypeTagIdx;
+ if (!checkFunctionOrMethodArgumentIndex(S, D, AttrName,
+ Attr.getLoc(), 3,
+ Attr.getArg(1), TypeTagIdx))
+ return;
+
+ bool IsPointer = (AttrName == "pointer_with_type_tag");
+ if (IsPointer) {
+ // Ensure that buffer has a pointer type.
+ QualType BufferTy = getFunctionOrMethodArgType(D, ArgumentIdx);
+ if (!BufferTy->isPointerType()) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_pointers_only)
+ << AttrName;
+ }
+ }
+
+ D->addAttr(::new (S.Context) ArgumentWithTypeTagAttr(Attr.getRange(),
+ S.Context,
+ ArgumentKind,
+ ArgumentIdx,
+ TypeTagIdx,
+ IsPointer));
+}
+
+static void handleTypeTagForDatatypeAttr(Sema &S, Decl *D,
+ const AttributeList &Attr) {
+ IdentifierInfo *PointerKind = Attr.getParameterName();
+ if (!PointerKind) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_identifier)
+ << "type_tag_for_datatype" << 1;
+ return;
+ }
+
+ QualType MatchingCType = S.GetTypeFromParser(Attr.getMatchingCType(), NULL);
+
+ D->addAttr(::new (S.Context) TypeTagForDatatypeAttr(
+ Attr.getRange(),
+ S.Context,
+ PointerKind,
+ MatchingCType,
+ Attr.getLayoutCompatible(),
+ Attr.getMustBeNull()));
+}
+
//===----------------------------------------------------------------------===//
// Checker-specific attribute handlers.
//===----------------------------------------------------------------------===//
@@ -4326,6 +4446,14 @@
handleAcquiredAfterAttr(S, D, Attr);
break;
+ // Type safety attributes.
+ case AttributeList::AT_ArgumentWithTypeTag:
+ handleArgumentWithTypeTagAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_TypeTagForDatatype:
+ handleTypeTagForDatatypeAttr(S, D, Attr);
+ break;
+
default:
// Ask target about the attribute.
const TargetAttributesSema &TargetAttrs = S.getTargetAttributesSema();
Modified: cfe/trunk/test/Sema/128bitint.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/128bitint.c?rev=162067&r1=162066&r2=162067&view=diff
==============================================================================
--- cfe/trunk/test/Sema/128bitint.c (original)
+++ cfe/trunk/test/Sema/128bitint.c Thu Aug 16 19:08:38 2012
@@ -18,3 +18,22 @@
unsigned long long UnsignedTooBig = 123456789012345678901234567890; // expected-warning {{integer constant is too large for its type}}
__uint128_t Unsigned128 = 123456789012345678901234567890Ui128;
unsigned long long Unsigned64 = 123456789012345678901234567890Ui128; // expected-warning {{implicit conversion from 'unsigned __int128' to 'unsigned long long' changes value from 123456789012345678901234567890 to 14083847773837265618}}
+
+// Ensure we don't crash when user passes 128-bit values to type safety
+// attributes.
+void pointer_with_type_tag_arg_num_1(void *buf, int datatype)
+ __attribute__(( pointer_with_type_tag(mpi,0x10000000000000001i128,1) )); // expected-error {{attribute parameter 2 is out of bounds}}
+
+void pointer_with_type_tag_arg_num_2(void *buf, int datatype)
+ __attribute__(( pointer_with_type_tag(mpi,1,0x10000000000000001i128) )); // expected-error {{attribute parameter 3 is out of bounds}}
+
+void MPI_Send(void *buf, int datatype) __attribute__(( pointer_with_type_tag(mpi,1,2) ));
+
+static const __uint128_t mpi_int_wrong __attribute__(( type_tag_for_datatype(mpi,int) )) = 0x10000000000000001i128; // expected-error {{'type_tag_for_datatype' attribute requires the initializer to be an integer constant expression that can be represented by a 64 bit integer}}
+static const int mpi_int __attribute__(( type_tag_for_datatype(mpi,int) )) = 10;
+
+void test(int *buf)
+{
+ MPI_Send(buf, 0x10000000000000001i128); // expected-warning {{implicit conversion from '__int128' to 'int' changes value}}
+}
+
Added: cfe/trunk/test/Sema/warn-type-safety-mpi-hdf5.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-type-safety-mpi-hdf5.c?rev=162067&view=auto
==============================================================================
--- cfe/trunk/test/Sema/warn-type-safety-mpi-hdf5.c (added)
+++ cfe/trunk/test/Sema/warn-type-safety-mpi-hdf5.c Thu Aug 16 19:08:38 2012
@@ -0,0 +1,307 @@
+// RUN: %clang_cc1 -std=c99 -DOPEN_MPI -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c99 -DMPICH -fsyntax-only -verify %s
+// RUN: %clang_cc1 -x c++ -std=c++98 -DOPEN_MPI -fsyntax-only -verify %s
+// RUN: %clang_cc1 -x c++ -std=c++98 -DMPICH -fsyntax-only -verify %s
+//
+// RUN: %clang_cc1 -std=c99 -DOPEN_MPI -fno-signed-char -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c99 -DMPICH -fno-signed-char -fsyntax-only -verify %s
+
+//===--- limits.h mock ----------------------------------------------------===//
+
+#ifdef __CHAR_UNSIGNED__
+#define CHAR_MIN 0
+#define CHAR_MAX (__SCHAR_MAX__*2 +1)
+#else
+#define CHAR_MIN (-__SCHAR_MAX__-1)
+#define CHAR_MAX __SCHAR_MAX__
+#endif
+
+//===--- mpi.h mock -------------------------------------------------------===//
+
+#define NULL ((void *)0)
+
+#ifdef OPEN_MPI
+typedef struct ompi_datatype_t *MPI_Datatype;
+#endif
+
+#ifdef MPICH
+typedef int MPI_Datatype;
+#endif
+
+int MPI_Send(void *buf, int count, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,1,3) ));
+
+int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype,
+ void *recvbuf, int recvcount, MPI_Datatype recvtype)
+ __attribute__(( pointer_with_type_tag(mpi,1,3), pointer_with_type_tag(mpi,4,6) ));
+
+#ifdef OPEN_MPI
+// OpenMPI and LAM/MPI-style datatype definitions
+
+#define OMPI_PREDEFINED_GLOBAL(type, global) ((type) &(global))
+
+#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_FLOAT_INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_float_int)
+#define MPI_2INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_2int)
+
+#define MPI_IN_PLACE ((void *) 1)
+
+extern struct ompi_predefined_datatype_t ompi_mpi_datatype_null __attribute__(( type_tag_for_datatype(mpi,void,must_be_null) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_float __attribute__(( type_tag_for_datatype(mpi,float) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_int __attribute__(( type_tag_for_datatype(mpi,int) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_long __attribute__(( type_tag_for_datatype(mpi,long) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_long_long_int __attribute__(( type_tag_for_datatype(mpi,long long int) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_char __attribute__(( type_tag_for_datatype(mpi,char) ));
+
+struct ompi_struct_mpi_float_int {float f; int i;};
+extern struct ompi_predefined_datatype_t ompi_mpi_float_int __attribute__(( type_tag_for_datatype(mpi, struct ompi_struct_mpi_float_int, layout_compatible) ));
+
+struct ompi_struct_mpi_2int {int i1; int i2;};
+extern struct ompi_predefined_datatype_t ompi_mpi_2int __attribute__(( type_tag_for_datatype(mpi, struct ompi_struct_mpi_2int, layout_compatible) ));
+#endif
+
+#ifdef MPICH
+// MPICH2 and MVAPICH2-style datatype definitions
+
+#define MPI_COMM_WORLD ((MPI_Comm) 0x44000000)
+
+#define MPI_DATATYPE_NULL ((MPI_Datatype) 0xa0000000)
+#define MPI_FLOAT ((MPI_Datatype) 0xa0000001)
+#define MPI_INT ((MPI_Datatype) 0xa0000002)
+#define MPI_LONG ((MPI_Datatype) 0xa0000003)
+#define MPI_LONG_LONG_INT ((MPI_Datatype) 0xa0000004)
+#define MPI_CHAR ((MPI_Datatype) 0xa0000005)
+
+#define MPI_FLOAT_INT ((MPI_Datatype) 0xa0000006)
+#define MPI_2INT ((MPI_Datatype) 0xa0000007)
+
+#define MPI_IN_PLACE (void *) -1
+
+static const MPI_Datatype mpich_mpi_datatype_null __attribute__(( type_tag_for_datatype(mpi,void,must_be_null) )) = 0xa0000000;
+static const MPI_Datatype mpich_mpi_float __attribute__(( type_tag_for_datatype(mpi,float) )) = 0xa0000001;
+static const MPI_Datatype mpich_mpi_int __attribute__(( type_tag_for_datatype(mpi,int) )) = 0xa0000002;
+static const MPI_Datatype mpich_mpi_long __attribute__(( type_tag_for_datatype(mpi,long) )) = 0xa0000003;
+static const MPI_Datatype mpich_mpi_long_long_int __attribute__(( type_tag_for_datatype(mpi,long long int) )) = 0xa0000004;
+static const MPI_Datatype mpich_mpi_char __attribute__(( type_tag_for_datatype(mpi,char) )) = 0xa0000005;
+
+struct mpich_struct_mpi_float_int { float f; int i; };
+struct mpich_struct_mpi_2int { int i1; int i2; };
+static const MPI_Datatype mpich_mpi_float_int __attribute__(( type_tag_for_datatype(mpi, struct mpich_struct_mpi_float_int, layout_compatible) )) = 0xa0000006;
+static const MPI_Datatype mpich_mpi_2int __attribute__(( type_tag_for_datatype(mpi, struct mpich_struct_mpi_2int, layout_compatible) )) = 0xa0000007;
+#endif
+
+//===--- HDF5 headers mock ------------------------------------------------===//
+
+typedef int hid_t;
+void H5open(void);
+
+#ifndef HDF_PRIVATE
+#define H5OPEN H5open(),
+#else
+#define H5OPEN
+#endif
+
+#define H5T_NATIVE_CHAR (CHAR_MIN?H5T_NATIVE_SCHAR:H5T_NATIVE_UCHAR)
+#define H5T_NATIVE_SCHAR (H5OPEN H5T_NATIVE_SCHAR_g)
+#define H5T_NATIVE_UCHAR (H5OPEN H5T_NATIVE_UCHAR_g)
+#define H5T_NATIVE_INT (H5OPEN H5T_NATIVE_INT_g)
+#define H5T_NATIVE_LONG (H5OPEN H5T_NATIVE_LONG_g)
+
+hid_t H5T_NATIVE_SCHAR_g __attribute__(( type_tag_for_datatype(hdf5,signed char) ));
+hid_t H5T_NATIVE_UCHAR_g __attribute__(( type_tag_for_datatype(hdf5,unsigned char) ));
+hid_t H5T_NATIVE_INT_g __attribute__(( type_tag_for_datatype(hdf5,int) ));
+hid_t H5T_NATIVE_LONG_g __attribute__(( type_tag_for_datatype(hdf5,long) ));
+
+void H5Dwrite(hid_t mem_type_id, const void *buf) __attribute__(( pointer_with_type_tag(hdf5,2,1) ));
+
+//===--- Tests ------------------------------------------------------------===//
+
+//===--- MPI
+
+struct pair_float_int
+{
+ float f; int i;
+};
+
+struct pair_int_int
+{
+ int i1; int i2;
+};
+
+void test_mpi_predefined_types(
+ int *int_buf,
+ long *long_buf1,
+ long *long_buf2,
+ void *void_buf,
+ struct pair_float_int *pfi,
+ struct pair_int_int *pii)
+{
+ char char_buf[255];
+
+ // Layout-compatible scalar types.
+ MPI_Send(int_buf, 1, MPI_INT); // no-warning
+
+ // Layout-compatible class types.
+ MPI_Send(pfi, 1, MPI_FLOAT_INT); // no-warning
+ MPI_Send(pii, 1, MPI_2INT); // no-warning
+
+ // Layout-incompatible scalar types.
+ MPI_Send(long_buf1, 1, MPI_INT); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'int *'}}
+
+ // Layout-incompatible class types.
+ MPI_Send(pii, 1, MPI_FLOAT_INT); // expected-warning {{argument type 'struct pair_int_int *' doesn't match specified 'mpi' type tag}}
+ MPI_Send(pfi, 1, MPI_2INT); // expected-warning {{argument type 'struct pair_float_int *' doesn't match specified 'mpi' type tag}}
+
+ // Layout-incompatible class-scalar types.
+ MPI_Send(long_buf1, 1, MPI_2INT); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag}}
+
+ // Function with two buffers.
+ MPI_Gather(long_buf1, 1, MPI_INT, // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'int *'}}
+ long_buf2, 1, MPI_INT); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'int *'}}
+
+ // Array buffers should work like pointer buffers.
+ MPI_Send(char_buf, 255, MPI_CHAR); // no-warning
+
+ // Explicit casts should not be dropped.
+ MPI_Send((int *) char_buf, 255, MPI_INT); // no-warning
+ MPI_Send((int *) char_buf, 255, MPI_CHAR); // expected-warning {{argument type 'int *' doesn't match specified 'mpi' type tag that requires 'char *'}}
+
+ // `void*' buffer should never warn.
+ MPI_Send(void_buf, 255, MPI_CHAR); // 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);
+
+ // 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);
+
+ MPI_Gather(int_buf, 0, MPI_DATATYPE_NULL, // expected-warning {{specified mpi type tag requires a null pointer}}
+ int_buf, 1, MPI_INT);
+}
+
+MPI_Datatype my_int_datatype __attribute__(( type_tag_for_datatype(mpi,int) ));
+
+struct S1 { int a; int b; };
+MPI_Datatype my_s1_datatype __attribute__(( type_tag_for_datatype(mpi,struct S1) ));
+
+// Layout-compatible to S1, but should be treated as a different type.
+struct S2 { int a; int b; };
+MPI_Datatype my_s2_datatype __attribute__(( type_tag_for_datatype(mpi,struct S2) ));
+
+void test_user_types(int *int_buf,
+ long *long_buf,
+ struct S1 *s1_buf,
+ struct S2 *s2_buf)
+{
+ MPI_Send(int_buf, 1, my_int_datatype); // no-warning
+ MPI_Send(long_buf, 1, my_int_datatype); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'int *'}}
+
+ MPI_Send(s1_buf, 1, my_s1_datatype); // no-warning
+ MPI_Send(s1_buf, 1, my_s2_datatype); // expected-warning {{argument type 'struct S1 *' doesn't match specified 'mpi' type tag that requires 'struct S2 *'}}
+
+ MPI_Send(long_buf, 1, my_s1_datatype); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'struct S1 *'}}
+ MPI_Send(s1_buf, 1, MPI_INT); // expected-warning {{argument type 'struct S1 *' doesn't match specified 'mpi' type tag that requires 'int *'}}
+}
+
+MPI_Datatype my_unknown_datatype;
+
+void test_not_annotated(int *int_buf,
+ long *long_buf,
+ MPI_Datatype type)
+{
+ // Using 'MPI_Datatype's without attributes should not produce warnings.
+ MPI_Send(long_buf, 1, my_unknown_datatype); // no-warning
+ MPI_Send(int_buf, 1, type); // no-warning
+}
+
+struct S1_compat { int a; int b; };
+MPI_Datatype my_s1_compat_datatype
+ __attribute__(( type_tag_for_datatype(mpi, struct S1_compat, layout_compatible) ));
+
+struct S3 { int a; long b; double c; double d; struct S1 s1; };
+struct S3_compat { int a; long b; double c; double d; struct S2 s2; };
+MPI_Datatype my_s3_compat_datatype
+ __attribute__(( type_tag_for_datatype(mpi, struct S3_compat, layout_compatible) ));
+
+struct S4 { char c; };
+struct S4_compat { signed char c; };
+MPI_Datatype my_s4_compat_datatype
+ __attribute__(( type_tag_for_datatype(mpi, struct S4_compat, layout_compatible) ));
+
+union U1 { int a; long b; double c; double d; struct S1 s1; };
+union U1_compat { long b; double c; struct S2 s; int a; double d; };
+MPI_Datatype my_u1_compat_datatype
+ __attribute__(( type_tag_for_datatype(mpi, union U1_compat, layout_compatible) ));
+
+union U2 { int a; long b; double c; struct S1 s1; };
+MPI_Datatype my_u2_datatype
+ __attribute__(( type_tag_for_datatype(mpi, union U2, layout_compatible) ));
+
+void test_layout_compatibility(struct S1 *s1_buf, struct S3 *s3_buf,
+ struct S4 *s4_buf,
+ union U1 *u1_buf, union U2 *u2_buf)
+{
+ MPI_Send(s1_buf, 1, my_s1_compat_datatype); // no-warning
+ MPI_Send(s3_buf, 1, my_s3_compat_datatype); // no-warning
+ MPI_Send(s1_buf, 1, my_s3_compat_datatype); // expected-warning {{argument type 'struct S1 *' doesn't match specified 'mpi' type tag}}
+ MPI_Send(s4_buf, 1, my_s4_compat_datatype); // expected-warning {{argument type 'struct S4 *' doesn't match specified 'mpi' type tag}}
+ MPI_Send(u1_buf, 1, my_u1_compat_datatype); // no-warning
+ MPI_Send(u1_buf, 1, my_u2_datatype); // expected-warning {{argument type 'union U1 *' doesn't match specified 'mpi' type tag}}
+ MPI_Send(u2_buf, 1, my_u1_compat_datatype); // expected-warning {{argument type 'union U2 *' doesn't match specified 'mpi' type tag}}
+}
+
+// There is an MPI_REAL predefined in MPI, but some existing MPI programs do
+// this.
+typedef float real;
+#define MPI_REAL MPI_FLOAT
+
+void test_mpi_real_user_type(real *real_buf, float *float_buf)
+{
+ MPI_Send(real_buf, 1, MPI_REAL); // no-warning
+ MPI_Send(real_buf, 1, MPI_FLOAT); // no-warning
+ MPI_Send(float_buf, 1, MPI_REAL); // no-warning
+ MPI_Send(float_buf, 1, MPI_FLOAT); // no-warning
+}
+
+//===--- HDF5
+
+void test_hdf5(char *char_buf,
+ signed char *schar_buf,
+ unsigned char *uchar_buf,
+ int *int_buf,
+ long *long_buf)
+{
+ H5Dwrite(H5T_NATIVE_CHAR, char_buf); // no-warning
+#ifdef __CHAR_UNSIGNED__
+ H5Dwrite(H5T_NATIVE_CHAR, schar_buf); // expected-warning {{argument type 'signed char *' doesn't match specified 'hdf5' type tag that requires 'unsigned char *'}}
+ H5Dwrite(H5T_NATIVE_CHAR, uchar_buf); // no-warning
+#else
+ H5Dwrite(H5T_NATIVE_CHAR, schar_buf); // no-warning
+ H5Dwrite(H5T_NATIVE_CHAR, uchar_buf); // expected-warning {{argument type 'unsigned char *' doesn't match specified 'hdf5' type tag that requires 'signed char *'}}
+#endif
+ H5Dwrite(H5T_NATIVE_SCHAR, schar_buf); // no-warning
+ H5Dwrite(H5T_NATIVE_UCHAR, uchar_buf); // no-warning
+ H5Dwrite(H5T_NATIVE_INT, int_buf); // no-warning
+ H5Dwrite(H5T_NATIVE_LONG, long_buf); // no-warning
+
+#ifdef __CHAR_UNSIGNED__
+ H5Dwrite(H5T_NATIVE_CHAR, int_buf); // expected-warning {{argument type 'int *' doesn't match specified 'hdf5' type tag that requires 'unsigned char *'}}
+#else
+ H5Dwrite(H5T_NATIVE_CHAR, int_buf); // expected-warning {{argument type 'int *' doesn't match specified 'hdf5' type tag that requires 'signed char *'}}
+#endif
+ H5Dwrite(H5T_NATIVE_INT, long_buf); // expected-warning {{argument type 'long *' doesn't match specified 'hdf5' type tag that requires 'int *'}}
+
+ // FIXME: we should warn here, but it will cause false positives because
+ // different kinds may use same magic values.
+ //H5Dwrite(MPI_INT, int_buf);
+}
+
Added: cfe/trunk/test/Sema/warn-type-safety.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-type-safety.c?rev=162067&view=auto
==============================================================================
--- cfe/trunk/test/Sema/warn-type-safety.c (added)
+++ cfe/trunk/test/Sema/warn-type-safety.c Thu Aug 16 19:08:38 2012
@@ -0,0 +1,152 @@
+// RUN: %clang_cc1 -std=c99 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -x c++ -std=c++98 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c99 -fno-signed-char -fsyntax-only -verify %s
+
+struct A {};
+
+typedef struct A *MPI_Datatype;
+
+int wrong1(void *buf, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag )); // expected-error {{attribute requires parameter 1 to be an identifier}}
+
+int wrong2(void *buf, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,0,7) )); // expected-error {{attribute parameter 2 is out of bounds}}
+
+int wrong3(void *buf, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,3,7) )); // expected-error {{attribute parameter 2 is out of bounds}}
+
+int wrong4(void *buf, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,1,0) )); // expected-error {{attribute parameter 3 is out of bounds}}
+
+int wrong5(void *buf, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,1,3) )); // expected-error {{attribute parameter 3 is out of bounds}}
+
+int wrong6(void *buf, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,0x8000000000000001ULL,1) )); // expected-error {{attribute parameter 2 is out of bounds}}
+
+extern int x;
+
+int wrong7(void *buf, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,x,2) )); // expected-error {{attribute requires parameter 2 to be an integer constant}}
+
+int wrong8(void *buf, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,1,x) )); // expected-error {{attribute requires parameter 3 to be an integer constant}}
+
+int wrong9 __attribute__(( pointer_with_type_tag(mpi,1,2) )); // expected-error {{attribute only applies to functions and methods}}
+
+int wrong10(double buf, MPI_Datatype type)
+ __attribute__(( pointer_with_type_tag(mpi,1,2) )); // expected-error {{'pointer_with_type_tag' attribute only applies to pointer arguments}}
+
+
+extern struct A datatype_wrong1
+ __attribute__(( type_tag_for_datatype )); // expected-error {{attribute requires parameter 1 to be an identifier}}
+
+extern struct A datatype_wrong2
+ __attribute__(( type_tag_for_datatype(mpi,1,2) )); // expected-error {{expected a type}}
+
+extern struct A datatype_wrong3
+ __attribute__(( type_tag_for_datatype(mpi,not_a_type) )); // expected-error {{unknown type name 'not_a_type'}}
+
+extern struct A datatype_wrong4
+ __attribute__(( type_tag_for_datatype(mpi,int,int) )); // expected-error {{expected identifier}}
+
+extern struct A datatype_wrong5
+ __attribute__(( type_tag_for_datatype(mpi,int,not_a_flag) )); // expected-error {{invalid comparison flag 'not_a_flag'}}
+
+extern struct A datatype_wrong6
+ __attribute__(( type_tag_for_datatype(mpi,int,layout_compatible,not_a_flag) )); // expected-error {{invalid comparison flag 'not_a_flag'}}
+
+
+// Using a tag with kind A in a place where the function requires kind B should
+// warn.
+
+void A_func(void *ptr, void *tag) __attribute__(( pointer_with_type_tag(a,1,2) ));
+
+extern struct A A_tag __attribute__(( type_tag_for_datatype(a,int) ));
+extern struct A B_tag __attribute__(( type_tag_for_datatype(b,int) ));
+
+void C_func(void *ptr, int tag) __attribute__(( pointer_with_type_tag(c,1,2) ));
+
+static const int C_tag __attribute__(( type_tag_for_datatype(c,int) )) = 10;
+static const int D_tag __attribute__(( type_tag_for_datatype(d,int) )) = 20;
+
+void test_tag_mismatch(int *ptr)
+{
+ A_func(ptr, &A_tag); // no-warning
+ A_func(ptr, &B_tag); // expected-warning {{this type tag was not designed to be used with this function}}
+ C_func(ptr, C_tag); // no-warning
+ C_func(ptr, D_tag); // expected-warning {{this type tag was not designed to be used with this function}}
+ C_func(ptr, 10); // no-warning
+ C_func(ptr, 20); // should warn, but may cause false positives
+}
+
+// Check that we look through typedefs in the special case of allowing 'char'
+// to be matched with 'signed char' or 'unsigned char'.
+void E_func(void *ptr, int tag) __attribute__(( pointer_with_type_tag(e,1,2) ));
+
+typedef char E_char;
+typedef char E_char_2;
+typedef signed char E_char_signed;
+typedef unsigned char E_char_unsigned;
+
+static const int E_tag __attribute__(( type_tag_for_datatype(e,E_char) )) = 10;
+
+void test_char_typedef(char *char_buf,
+ E_char_2 *e_char_buf,
+ E_char_signed *e_char_signed_buf,
+ E_char_unsigned *e_char_unsigned_buf)
+{
+ E_func(char_buf, E_tag);
+ E_func(e_char_buf, E_tag);
+#ifdef __CHAR_UNSIGNED__
+ E_func(e_char_signed_buf, E_tag); // expected-warning {{argument type 'E_char_signed *' (aka 'signed char *') doesn't match specified 'e' type tag that requires 'E_char *' (aka 'char *')}}
+ E_func(e_char_unsigned_buf, E_tag);
+#else
+ E_func(e_char_signed_buf, E_tag);
+ E_func(e_char_unsigned_buf, E_tag); // expected-warning {{argument type 'E_char_unsigned *' (aka 'unsigned char *') doesn't match specified 'e' type tag that requires 'E_char *' (aka 'char *')}}
+#endif
+}
+
+// Tests for argument_with_type_tag.
+
+#define F_DUPFD 10
+#define F_SETLK 20
+
+struct flock { };
+
+static const int F_DUPFD_tag __attribute__(( type_tag_for_datatype(fcntl,int) )) = F_DUPFD;
+static const int F_SETLK_tag __attribute__(( type_tag_for_datatype(fcntl,struct flock *) )) = F_SETLK;
+
+int fcntl(int fd, int cmd, ...) __attribute__(( argument_with_type_tag(fcntl,3,2) ));
+
+void test_argument_with_type_tag(struct flock *f)
+{
+ fcntl(0, F_DUPFD, 10); // no-warning
+ fcntl(0, F_SETLK, f); // no-warning
+
+ fcntl(0, F_SETLK, 10); // expected-warning {{argument type 'int' doesn't match specified 'fcntl' type tag that requires 'struct flock *'}}
+ fcntl(0, F_DUPFD, f); // expected-warning {{argument type 'struct flock *' doesn't match specified 'fcntl' type tag that requires 'int'}}
+}
+
+void test_tag_expresssion(int b) {
+ fcntl(0, b ? F_DUPFD : F_SETLK, 10); // no-warning
+ fcntl(0, b + F_DUPFD, 10); // no-warning
+ fcntl(0, (b, F_DUPFD), 10); // expected-warning {{expression result unused}}
+}
+
+// Check that using 64-bit magic values as tags works and tag values do not
+// overflow internally.
+void F_func(void *ptr, unsigned long long tag) __attribute__((pointer_with_type_tag(f,1,2) ));
+
+static const unsigned long long F_tag1 __attribute__(( type_tag_for_datatype(f,int) )) = 0xFFFFFFFFFFFFFFFFULL;
+static const unsigned long long F_tag2 __attribute__(( type_tag_for_datatype(f,float) )) = 0xFFFFFFFFULL;
+
+void test_64bit_magic(int *int_ptr, float *float_ptr)
+{
+ F_func(int_ptr, 0xFFFFFFFFFFFFFFFFULL);
+ F_func(int_ptr, 0xFFFFFFFFULL); // expected-warning {{argument type 'int *' doesn't match specified 'f' type tag that requires 'float *'}}
+ F_func(float_ptr, 0xFFFFFFFFFFFFFFFFULL); // expected-warning {{argument type 'float *' doesn't match specified 'f' type tag that requires 'int *'}}
+ F_func(float_ptr, 0xFFFFFFFFULL);
+}
+
+
Added: cfe/trunk/test/Sema/warn-type-safety.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-type-safety.cpp?rev=162067&view=auto
==============================================================================
--- cfe/trunk/test/Sema/warn-type-safety.cpp (added)
+++ cfe/trunk/test/Sema/warn-type-safety.cpp Thu Aug 16 19:08:38 2012
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+typedef struct ompi_datatype_t *MPI_Datatype;
+
+#define OMPI_PREDEFINED_GLOBAL(type, global) ((type) &(global))
+
+#define MPI_FLOAT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_float)
+#define MPI_INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_int)
+#define MPI_NULL OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_null)
+
+extern struct ompi_predefined_datatype_t ompi_mpi_float __attribute__(( type_tag_for_datatype(mpi,float) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_int __attribute__(( type_tag_for_datatype(mpi,int) ));
+extern struct ompi_predefined_datatype_t ompi_mpi_null __attribute__(( type_tag_for_datatype(mpi,void,must_be_null) ));
+
+int f(int x) { return x; }
+static const int wrong_init __attribute__(( type_tag_for_datatype(zzz,int) )) = f(100); // expected-error {{'type_tag_for_datatype' attribute requires the initializer to be an integral constant expression}}
+
+//===--- Tests ------------------------------------------------------------===//
+// Check that hidden 'this' is handled correctly.
+
+class C
+{
+public:
+ void f1(void *buf, int count, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,5,6) )); // expected-error {{attribute parameter 2 is out of bounds}}
+
+ void f2(void *buf, int count, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,2,5) )); // expected-error {{attribute parameter 3 is out of bounds}}
+
+ void f3(void *buf, int count, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,1,5) )); // expected-error {{attribute is invalid for the implicit this argument}}
+
+ void f4(void *buf, int count, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,2,1) )); // expected-error {{attribute is invalid for the implicit this argument}}
+
+ void MPI_Send(void *buf, int count, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,2,4) )); // no-error
+};
+
+// Check that we don't crash on type and value dependent expressions.
+template<int a>
+void value_dep(void *buf, int count, MPI_Datatype datatype)
+ __attribute__(( pointer_with_type_tag(mpi,a,5) )); // expected-error {{attribute requires parameter 2 to be an integer constant}}
+
+class OperatorIntStar
+{
+public:
+ operator int*();
+};
+
+void test1(C *c, int *int_buf)
+{
+ c->MPI_Send(int_buf, 1, MPI_INT); // no-warning
+ c->MPI_Send(int_buf, 1, MPI_FLOAT); // expected-warning {{argument type 'int *' doesn't match specified 'mpi' type tag that requires 'float *'}}
+
+ OperatorIntStar i;
+ c->MPI_Send(i, 1, MPI_INT); // no-warning
+ c->MPI_Send(i, 1, MPI_FLOAT); // expected-warning {{argument type 'int *' doesn't match specified 'mpi' type tag that requires 'float *'}}
+}
+
+template<typename T>
+void test2(C *c, int *int_buf, T tag)
+{
+ c->MPI_Send(int_buf, 1, tag); // no-warning
+}
+
+void test3(C *c, int *int_buf) {
+ test2(c, int_buf, MPI_INT);
+ test2(c, int_buf, MPI_NULL);
+}
+
More information about the cfe-commits
mailing list