diff options
-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; + |