diff options
author | Dmitri Gribenko <gribozavr@gmail.com> | 2012-08-17 00:08:38 +0000 |
---|---|---|
committer | Dmitri Gribenko <gribozavr@gmail.com> | 2012-08-17 00:08:38 +0000 |
commit | 0d5a069f66df09b3308ccfdce84a88170034c657 (patch) | |
tree | ac4d8f199efea6a265fa37b6990e7b99cb6849e0 | |
parent | 8bf4ab319e232f185e9965c5bb417dee62706c8f (diff) |
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().
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162067 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | docs/LanguageExtensions.html | 162 | ||||
-rw-r--r-- | include/clang/Basic/Attr.td | 21 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticGroups.td | 2 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticParseKinds.td | 4 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 22 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 4 | ||||
-rw-r--r-- | include/clang/Sema/AttributeList.h | 107 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 36 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 70 | ||||
-rw-r--r-- | lib/Sema/AttributeList.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 414 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 36 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 128 | ||||
-rw-r--r-- | test/Sema/128bitint.c | 19 | ||||
-rw-r--r-- | test/Sema/warn-type-safety-mpi-hdf5.c | 307 | ||||
-rw-r--r-- | test/Sema/warn-type-safety.c | 152 | ||||
-rw-r--r-- | test/Sema/warn-type-safety.cpp | 71 |
17 files changed, 1551 insertions, 6 deletions
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index eac3c69997..40477b82f5 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -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> diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 99180e450e..fade83ef93 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -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 { diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index cc234dd5bf..d8632dd8c3 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -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, diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 8cb82fd4a9..b1c16fa852 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -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< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index bff9a34318..2d63dc42fd 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 353b59e0ae..4ef92f7dbc 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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); diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 5239044e67..bf358862b0 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -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 = diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d1a7017fb0..acc88c0578 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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. diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index a50f42bf8b..cb865cc9c2 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -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 diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index 0f209fd7d6..7c79879d97 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -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*)); } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 3702861c1a..2559f00f71 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -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(); +} + diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 01b2f2c839..75b8095a66 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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 diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 29cb4f5ca4..caa7b2f65a 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -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(); diff --git a/test/Sema/128bitint.c b/test/Sema/128bitint.c index ddad835547..600c25a630 100644 --- a/test/Sema/128bitint.c +++ b/test/Sema/128bitint.c @@ -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}} +} + diff --git a/test/Sema/warn-type-safety-mpi-hdf5.c b/test/Sema/warn-type-safety-mpi-hdf5.c new file mode 100644 index 0000000000..9c2ee96586 --- /dev/null +++ b/test/Sema/warn-type-safety-mpi-hdf5.c @@ -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); +} + diff --git a/test/Sema/warn-type-safety.c b/test/Sema/warn-type-safety.c new file mode 100644 index 0000000000..6f548aa256 --- /dev/null +++ b/test/Sema/warn-type-safety.c @@ -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); +} + + diff --git a/test/Sema/warn-type-safety.cpp b/test/Sema/warn-type-safety.cpp new file mode 100644 index 0000000000..d053fbaa21 --- /dev/null +++ b/test/Sema/warn-type-safety.cpp @@ -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); +} + |