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> <li><a href="#ts_slr"><tt>shared_locks_required(...)</tt></a></li>
</ul> </ul>
</li> </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> </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 shared locks. Arguments must be lockable type, and there must be at
least one argument.</p> 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> </div>
</body> </body>
</html> </html>

View File

@ -826,6 +826,27 @@ def SharedLocksRequired : InheritableAttr {
let TemplateDependent = 1; 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 // Microsoft-related attributes
def MsStruct : InheritableAttr { def MsStruct : InheritableAttr {

View File

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

View File

@ -677,6 +677,10 @@ def warn_availability_and_unavailable : Warning<
"'unavailable' availability overrides all other availability information">, "'unavailable' availability overrides all other availability information">,
InGroup<Availability>; 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 // Language specific pragmas
// - Generic warnings // - Generic warnings
def warn_pragma_expected_lparen : Warning< 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">; "'%0' attribute requires parameter %1 to be an integer constant">;
def err_attribute_argument_n_not_string : Error< def err_attribute_argument_n_not_string : Error<
"'%0' attribute requires parameter %1 to be a string">; "'%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< def err_attribute_argument_out_of_bounds : Error<
"'%0' attribute parameter %1 is out of bounds">; "'%0' attribute parameter %1 is out of bounds">;
def err_attribute_requires_objc_interface : Error< def err_attribute_requires_objc_interface : Error<
@ -1555,6 +1557,8 @@ def err_attribute_uuid_malformed_guid : Error<
"uuid attribute contains a malformed GUID">; "uuid attribute contains a malformed GUID">;
def warn_nonnull_pointers_only : Warning< def warn_nonnull_pointers_only : Warning<
"nonnull attribute only applies to pointer arguments">; "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< def err_attribute_invalid_implicit_this_argument : Error<
"'%0' attribute is invalid for the implicit this argument">; "'%0' attribute is invalid for the implicit this argument">;
def err_ownership_type : Error< 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< def warn_attribute_not_on_decl : Error<
"%0 attribute ignored when parsing type">; "%0 attribute ignored when parsing type">;
// Availability attribute // Availability attribute
def warn_availability_unknown_platform : Warning< def warn_availability_unknown_platform : Warning<
"unknown platform %0 in availability macro">, InGroup<Availability>; "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">, "assigning %select{field|instance variable}0 to itself">,
InGroup<SelfAssignmentField>; 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. // Generic selections.
def err_assoc_type_incomplete : Error< def err_assoc_type_incomplete : Error<
"type %0 in generic association incomplete">; "type %0 in generic association incomplete">;

View File

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

View File

@ -19,6 +19,7 @@
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceLocation.h"
#include "clang/Basic/VersionTuple.h" #include "clang/Basic/VersionTuple.h"
#include "clang/Sema/Ownership.h"
#include <cassert> #include <cassert>
namespace clang { namespace clang {
@ -87,6 +88,10 @@ private:
/// availability attribute. /// availability attribute.
unsigned IsAvailability : 1; unsigned IsAvailability : 1;
/// True if this has extra information associated with a
/// type_tag_for_datatype attribute.
unsigned IsTypeTagForDatatype : 1;
unsigned AttrKind : 8; unsigned AttrKind : 8;
/// \brief The location of the 'unavailable' keyword in an /// \brief The location of the 'unavailable' keyword in an
@ -119,6 +124,22 @@ private:
return reinterpret_cast<const AvailabilityChange*>(this+1)[index]; 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 AttributeList(const AttributeList &); // DO NOT IMPLEMENT
void operator=(const AttributeList &); // DO NOT IMPLEMENT void operator=(const AttributeList &); // DO NOT IMPLEMENT
void operator delete(void *); // DO NOT IMPLEMENT void operator delete(void *); // DO NOT IMPLEMENT
@ -126,6 +147,7 @@ private:
size_t allocated_size() const; size_t allocated_size() const;
/// Constructor for attributes with expression arguments.
AttributeList(IdentifierInfo *attrName, SourceRange attrRange, AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc, IdentifierInfo *parmName, SourceLocation parmLoc,
@ -134,12 +156,13 @@ private:
: AttrName(attrName), ScopeName(scopeName), ParmName(parmName), : AttrName(attrName), ScopeName(scopeName), ParmName(parmName),
AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc),
NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false), NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false),
UsedAsTypeAttr(false), IsAvailability(false), UsedAsTypeAttr(false), IsAvailability(false),
NextInPosition(0), NextInPool(0) { IsTypeTagForDatatype(false), NextInPosition(0), NextInPool(0) {
if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*)); if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*));
AttrKind = getKind(getName(), getScopeName(), syntaxUsed); AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
} }
/// Constructor for availability attributes.
AttributeList(IdentifierInfo *attrName, SourceRange attrRange, AttributeList(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc, IdentifierInfo *parmName, SourceLocation parmLoc,
@ -153,6 +176,7 @@ private:
AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc),
NumArgs(0), SyntaxUsed(syntaxUsed), NumArgs(0), SyntaxUsed(syntaxUsed),
Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
IsTypeTagForDatatype(false),
UnavailableLoc(unavailable), MessageExpr(messageExpr), UnavailableLoc(unavailable), MessageExpr(messageExpr),
NextInPosition(0), NextInPool(0) { NextInPosition(0), NextInPool(0) {
new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced);
@ -161,6 +185,25 @@ private:
AttrKind = getKind(getName(), getScopeName(), syntaxUsed); 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 AttributePool;
friend class AttributeFactory; friend class AttributeFactory;
@ -279,6 +322,24 @@ public:
assert(getKind() == AT_Availability && "Not an availability attribute"); assert(getKind() == AT_Availability && "Not an availability attribute");
return MessageExpr; 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 /// A factory, from which one makes pools, from which one creates
@ -294,7 +355,11 @@ public:
AvailabilityAllocSize = AvailabilityAllocSize =
sizeof(AttributeList) sizeof(AttributeList)
+ ((3 * sizeof(AvailabilityChange) + sizeof(void*) - 1) + ((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: private:
@ -411,6 +476,21 @@ public:
AttributeList *createIntegerAttribute(ASTContext &C, IdentifierInfo *Name, AttributeList *createIntegerAttribute(ASTContext &C, IdentifierInfo *Name,
SourceLocation TokLoc, int Arg); 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 /// addAttributeLists - Add two AttributeLists together
@ -503,7 +583,7 @@ public:
/// dependencies on this method, it may not be long-lived. /// dependencies on this method, it may not be long-lived.
AttributeList *&getListRef() { return list; } AttributeList *&getListRef() { return list; }
/// Add attribute with expression arguments.
AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange, AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc, IdentifierInfo *parmName, SourceLocation parmLoc,
@ -516,6 +596,7 @@ public:
return attr; return attr;
} }
/// Add availability attribute.
AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange, AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *scopeName, SourceLocation scopeLoc,
IdentifierInfo *parmName, SourceLocation parmLoc, IdentifierInfo *parmName, SourceLocation parmLoc,
@ -533,6 +614,24 @@ public:
return attr; 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, AttributeList *addNewInteger(ASTContext &C, IdentifierInfo *name,
SourceLocation loc, int arg) { SourceLocation loc, int arg) {
AttributeList *attr = AttributeList *attr =

View File

@ -7145,6 +7145,42 @@ private:
void CheckBitFieldInitialization(SourceLocation InitLoc, FieldDecl *Field, void CheckBitFieldInitialization(SourceLocation InitLoc, FieldDecl *Field,
Expr *Init); 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. /// \brief The parser's current scope.
/// ///
/// The parser maintains this state here. /// The parser maintains this state here.

View File

@ -68,7 +68,6 @@ static bool isAttributeLateParsed(const IdentifierInfo &II) {
.Default(false); .Default(false);
} }
/// ParseGNUAttributes - Parse a non-empty attributes list. /// ParseGNUAttributes - Parse a non-empty attributes list.
/// ///
/// [GNU] attributes: /// [GNU] attributes:
@ -193,6 +192,11 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc); ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc);
return; 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 ConsumeParen(); // ignore the left paren loc for now
@ -1020,6 +1024,70 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName,
*EndLoc = T.getCloseLocation(); *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 /// DiagnoseProhibitedCXX11Attribute - We have found the opening square brackets
/// of a C++11 attribute-specifier in a location where an attribute is not /// 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 /// 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 { size_t AttributeList::allocated_size() const {
if (IsAvailability) return AttributeFactory::AvailabilityAllocSize; if (IsAvailability) return AttributeFactory::AvailabilityAllocSize;
else if (IsTypeTagForDatatype)
return AttributeFactory::TypeTagForDatatypeAllocSize;
return (sizeof(AttributeList) + NumArgs * sizeof(Expr*)); 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>(), I = FDecl->specific_attr_begin<NonNullAttr>(),
E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I)
CheckNonNullArguments(*I, Args, Loc); 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 /// 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); 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) { Sema::FinalizeDeclaration(Decl *ThisDecl) {
// Note that we are no longer parsing the initializer for this declaration. // Note that we are no longer parsing the initializer for this declaration.
ParsingInitForAutoVars.erase(ThisDecl); 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 Sema::DeclGroupPtrTy

View File

@ -221,6 +221,53 @@ static bool checkAttributeAtLeastNumArgs(Sema &S, const AttributeList &Attr,
return true; 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 /// \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 /// \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. // Checker-specific attribute handlers.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -4326,6 +4446,14 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
handleAcquiredAfterAttr(S, D, Attr); handleAcquiredAfterAttr(S, D, Attr);
break; break;
// Type safety attributes.
case AttributeList::AT_ArgumentWithTypeTag:
handleArgumentWithTypeTagAttr(S, D, Attr);
break;
case AttributeList::AT_TypeTagForDatatype:
handleTypeTagForDatatypeAttr(S, D, Attr);
break;
default: default:
// Ask target about the attribute. // Ask target about the attribute.
const TargetAttributesSema &TargetAttrs = S.getTargetAttributesSema(); 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}} unsigned long long UnsignedTooBig = 123456789012345678901234567890; // expected-warning {{integer constant is too large for its type}}
__uint128_t Unsigned128 = 123456789012345678901234567890Ui128; __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}} 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);
}