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().

llvm-svn: 162067
This commit is contained in:
Dmitri Gribenko 2012-08-17 00:08:38 +00:00
parent 476e5a3c9e
commit e4a5a90e8d
17 changed files with 1551 additions and 6 deletions

View File

@ -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 @@ declaration to specify that the function must be called while holding the listed
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) &amp;&amp; \
__has_attribute(pointer_with_type_tag) &amp;&amp; \
__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) &amp;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&nbsp;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) &amp;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) &amp;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>

View File

@ -826,6 +826,27 @@ def SharedLocksRequired : InheritableAttr {
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 {

View File

@ -343,6 +343,8 @@ def FormatNonLiteral : DiagGroup<"format-nonliteral", [FormatSecurity]>;
def Format2 : DiagGroup<"format=2",
[FormatNonLiteral, FormatSecurity, FormatY2K]>;
def TypeSafety : DiagGroup<"type-safety">;
def Extra : DiagGroup<"extra", [
MissingFieldInitializers,
IgnoredQualifiers,

View File

@ -677,6 +677,10 @@ def warn_availability_and_unavailable : Warning<
"'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<

View File

@ -1547,6 +1547,8 @@ def err_attribute_argument_n_not_int : Error<
"'%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 @@ def err_attribute_uuid_malformed_guid : Error<
"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 err_attribute_can_be_applied_only_to_value_decl : Error<
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 @@ def warn_identity_field_assign : Warning<
"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">;

View File

@ -1834,6 +1834,10 @@ private:
ParsedAttributes &Attrs,
SourceLocation *EndLoc);
void ParseTypeTagForDatatypeAttribute(IdentifierInfo &AttrName,
SourceLocation AttrNameLoc,
ParsedAttributes &Attrs,
SourceLocation *EndLoc);
void ParseTypeofSpecifier(DeclSpec &DS);
SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);

View File

@ -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 @@ private:
/// 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 @@ private:
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 @@ private:
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 @@ private:
: 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 @@ private:
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 @@ private:
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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
/// 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 @@ public:
return attr;
}
/// Add availability attribute.
AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc,
@ -533,6 +614,24 @@ public:
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 =

View File

@ -7145,6 +7145,42 @@ private:
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.

View File

@ -68,7 +68,6 @@ static bool isAttributeLateParsed(const IdentifierInfo &II) {
.Default(false);
}
/// ParseGNUAttributes - Parse a non-empty attributes list.
///
/// [GNU] attributes:
@ -193,6 +192,11 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
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 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName,
*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

View File

@ -21,6 +21,8 @@ using namespace clang;
size_t AttributeList::allocated_size() const {
if (IsAvailability) return AttributeFactory::AvailabilityAllocSize;
else if (IsTypeTagForDatatype)
return AttributeFactory::TypeTagForDatatypeAllocSize;
return (sizeof(AttributeList) + NumArgs * sizeof(Expr*));
}

View File

@ -513,6 +513,13 @@ void Sema::checkCall(NamedDecl *FDecl, Expr **Args,
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 @@ void Sema::DiagnoseEmptyLoopBody(const Stmt *S,
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();
}

View File

@ -7031,6 +7031,42 @@ void
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

View File

@ -221,6 +221,53 @@ static bool checkAttributeAtLeastNumArgs(Sema &S, const AttributeList &Attr,
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 handleLaunchBoundsAttr(Sema &S, Decl *D, const AttributeList &Attr){
}
}
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 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
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();

View File

@ -18,3 +18,22 @@ long long Signed64 = 123456789012345678901234567890i128; // expected-warning {{i
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}}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}