forked from OSchip/llvm-project
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:
parent
476e5a3c9e
commit
e4a5a90e8d
|
@ -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) && \
|
||||
__has_attribute(pointer_with_type_tag) && \
|
||||
__has_attribute(type_tag_for_datatype)
|
||||
# define ATTR_MPI_PWT(buffer_idx, type_idx) __attribute__((pointer_with_type_tag(mpi,buffer_idx,type_idx)))
|
||||
/* ... other macros ... */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(ATTR_MPI_PWT)
|
||||
#define ATTR_MPI_PWT(buffer_idx, type_idx)
|
||||
#endif
|
||||
|
||||
int MPI_Send(void *buf, int count, MPI_Datatype datatype /*, other args omitted */)
|
||||
ATTR_MPI_PWT(1,3);
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h3 id="argument_with_type_tag"><tt>argument_with_type_tag(...)</tt></h3>
|
||||
|
||||
<p>Use <tt>__attribute__((argument_with_type_tag(arg_kind, arg_idx,
|
||||
type_tag_idx)))</tt> on a function declaration to specify that the function
|
||||
accepts a type tag that determines the type of some other argument.
|
||||
<tt>arg_kind</tt> is an identifier that should be used when annotating all
|
||||
applicable type tags.</p>
|
||||
|
||||
<p>This attribute is primarily useful for checking arguments of variadic
|
||||
functions (<tt>pointer_with_type_tag</tt> can be used in most of non-variadic
|
||||
cases).</p>
|
||||
|
||||
<p>For example:</p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
int fcntl(int fd, int cmd, ...)
|
||||
__attribute__(( argument_with_type_tag(fcntl,3,2) ));
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h3 id="pointer_with_type_tag"><tt>pointer_with_type_tag(...)</tt></h3>
|
||||
|
||||
<p>Use <tt>__attribute__((pointer_with_type_tag(ptr_kind, ptr_idx,
|
||||
type_tag_idx)))</tt> on a function declaration to specify that the
|
||||
function a type tag that determines the pointee type of some other pointer
|
||||
argument.</p>
|
||||
|
||||
<p>For example:</p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
int MPI_Send(void *buf, int count, MPI_Datatype datatype /*, other args omitted */)
|
||||
__attribute__(( pointer_with_type_tag(mpi,1,3) ));
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h3 id="type_tag_for_datatype"><tt>type_tag_for_datatype(...)</tt></h3>
|
||||
|
||||
<p>Clang supports annotating type tags of two forms.</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Type tag that is an expression containing a reference to some declared
|
||||
identifier.</b> Use <tt>__attribute__((type_tag_for_datatype(kind, type)))</tt>
|
||||
on a declaration with that identifier:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
extern struct mpi_datatype mpi_datatype_int
|
||||
__attribute__(( type_tag_for_datatype(mpi,int) ));
|
||||
#define MPI_INT ((MPI_Datatype) &mpi_datatype_int)
|
||||
</pre>
|
||||
</blockquote></li>
|
||||
|
||||
<li><b>Type tag that is an integral literal.</b> Introduce a <tt>static
|
||||
const</tt> variable with a corresponding initializer value and attach
|
||||
<tt>__attribute__((type_tag_for_datatype(kind, type)))</tt> on that
|
||||
declaration, for example:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
#define MPI_INT ((MPI_Datatype) 42)
|
||||
static const MPI_Datatype mpi_datatype_int
|
||||
__attribute__(( type_tag_for_datatype(mpi,int) )) = 42
|
||||
</pre>
|
||||
</blockquote></li>
|
||||
</ul>
|
||||
|
||||
<p>The attribute also accepts an optional third argument that determines how
|
||||
the expression is compared to the type tag. There are two supported flags:</p>
|
||||
|
||||
<ul><li><tt>layout_compatible</tt> will cause types to be compared according to
|
||||
layout-compatibility rules (C++11 [class.mem] p 17, 18). This is
|
||||
implemented to support annotating types like <tt>MPI_DOUBLE_INT</tt>.
|
||||
|
||||
<p>For example:</p>
|
||||
<blockquote>
|
||||
<pre>
|
||||
/* In mpi.h */
|
||||
struct internal_mpi_double_int { double d; int i; };
|
||||
extern struct mpi_datatype mpi_datatype_double_int
|
||||
__attribute__(( type_tag_for_datatype(mpi, struct internal_mpi_double_int,
|
||||
layout_compatible) ));
|
||||
|
||||
#define MPI_DOUBLE_INT ((MPI_Datatype) &mpi_datatype_double_int)
|
||||
|
||||
/* In user code */
|
||||
struct my_pair { double a; int b; };
|
||||
struct my_pair *buffer;
|
||||
MPI_Send(buffer, 1, MPI_DOUBLE_INT /*, ... */); // no warning
|
||||
|
||||
struct my_int_pair { int a; int b; }
|
||||
struct my_int_pair *buffer2;
|
||||
MPI_Send(buffer2, 1, MPI_DOUBLE_INT /*, ... */); // warning: actual buffer element
|
||||
// type 'struct my_int_pair'
|
||||
// doesn't match specified MPI_Datatype
|
||||
</pre>
|
||||
</blockquote>
|
||||
</li>
|
||||
|
||||
<li><tt>must_be_null</tt> specifies that the expression should be a null
|
||||
pointer constant, for example:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
/* In mpi.h */
|
||||
extern struct mpi_datatype mpi_datatype_null
|
||||
__attribute__(( type_tag_for_datatype(mpi, void, must_be_null) ));
|
||||
|
||||
#define MPI_DATATYPE_NULL ((MPI_Datatype) &mpi_datatype_null)
|
||||
|
||||
/* In user code */
|
||||
MPI_Send(buffer, 1, MPI_DATATYPE_NULL /*, ... */); // warning: MPI_DATATYPE_NULL
|
||||
// was specified but buffer
|
||||
// is not a null pointer
|
||||
</pre>
|
||||
</blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*));
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue