aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/LanguageExtensions.html162
-rw-r--r--include/clang/Basic/Attr.td21
-rw-r--r--include/clang/Basic/DiagnosticGroups.td2
-rw-r--r--include/clang/Basic/DiagnosticParseKinds.td4
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td22
-rw-r--r--include/clang/Parse/Parser.h4
-rw-r--r--include/clang/Sema/AttributeList.h107
-rw-r--r--include/clang/Sema/Sema.h36
-rw-r--r--lib/Parse/ParseDecl.cpp70
-rw-r--r--lib/Sema/AttributeList.cpp2
-rw-r--r--lib/Sema/SemaChecking.cpp414
-rw-r--r--lib/Sema/SemaDecl.cpp36
-rw-r--r--lib/Sema/SemaDeclAttr.cpp128
-rw-r--r--test/Sema/128bitint.c19
-rw-r--r--test/Sema/warn-type-safety-mpi-hdf5.c307
-rw-r--r--test/Sema/warn-type-safety.c152
-rw-r--r--test/Sema/warn-type-safety.cpp71
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) &amp;&amp; \
+ __has_attribute(pointer_with_type_tag) &amp;&amp; \
+ __has_attribute(type_tag_for_datatype)
+# define ATTR_MPI_PWT(buffer_idx, type_idx) __attribute__((pointer_with_type_tag(mpi,buffer_idx,type_idx)))
+/* ... other macros ... */
+# endif
+#endif
+
+#if !defined(ATTR_MPI_PWT)
+#define ATTR_MPI_PWT(buffer_idx, type_idx)
+#endif
+
+int MPI_Send(void *buf, int count, MPI_Datatype datatype /*, other args omitted */)
+ ATTR_MPI_PWT(1,3);
+</pre>
+</blockquote>
+
+<h3 id="argument_with_type_tag"><tt>argument_with_type_tag(...)</tt></h3>
+
+<p>Use <tt>__attribute__((argument_with_type_tag(arg_kind, arg_idx,
+type_tag_idx)))</tt> on a function declaration to specify that the function
+accepts a type tag that determines the type of some other argument.
+<tt>arg_kind</tt> is an identifier that should be used when annotating all
+applicable type tags.</p>
+
+<p>This attribute is primarily useful for checking arguments of variadic
+functions (<tt>pointer_with_type_tag</tt> can be used in most of non-variadic
+cases).</p>
+
+<p>For example:</p>
+<blockquote>
+<pre>
+int fcntl(int fd, int cmd, ...)
+ __attribute__(( argument_with_type_tag(fcntl,3,2) ));
+</pre>
+</blockquote>
+
+<h3 id="pointer_with_type_tag"><tt>pointer_with_type_tag(...)</tt></h3>
+
+<p>Use <tt>__attribute__((pointer_with_type_tag(ptr_kind, ptr_idx,
+type_tag_idx)))</tt> on a function declaration to specify that the
+function a type tag that determines the pointee type of some other pointer
+argument.</p>
+
+<p>For example:</p>
+<blockquote>
+<pre>
+int MPI_Send(void *buf, int count, MPI_Datatype datatype /*, other args omitted */)
+ __attribute__(( pointer_with_type_tag(mpi,1,3) ));
+</pre>
+</blockquote>
+
+<h3 id="type_tag_for_datatype"><tt>type_tag_for_datatype(...)</tt></h3>
+
+<p>Clang supports annotating type tags of two forms.</p>
+
+<ul>
+<li><b>Type tag that is an expression containing a reference to some declared
+identifier.</b> Use <tt>__attribute__((type_tag_for_datatype(kind, type)))</tt>
+on a declaration with that identifier:
+
+<blockquote>
+<pre>
+extern struct mpi_datatype mpi_datatype_int
+ __attribute__(( type_tag_for_datatype(mpi,int) ));
+#define MPI_INT ((MPI_Datatype) &amp;mpi_datatype_int)
+</pre>
+</blockquote></li>
+
+<li><b>Type tag that is an integral literal.</b> Introduce a <tt>static
+const</tt> variable with a corresponding initializer value and attach
+<tt>__attribute__((type_tag_for_datatype(kind, type)))</tt> on that
+declaration, for example:
+
+<blockquote>
+<pre>
+#define MPI_INT ((MPI_Datatype) 42)
+static const MPI_Datatype mpi_datatype_int
+ __attribute__(( type_tag_for_datatype(mpi,int) )) = 42
+</pre>
+</blockquote></li>
+</ul>
+
+<p>The attribute also accepts an optional third argument that determines how
+the expression is compared to the type tag. There are two supported flags:</p>
+
+<ul><li><tt>layout_compatible</tt> will cause types to be compared according to
+layout-compatibility rules (C++11 [class.mem] p&nbsp;17, 18). This is
+implemented to support annotating types like <tt>MPI_DOUBLE_INT</tt>.
+
+<p>For example:</p>
+<blockquote>
+<pre>
+/* In mpi.h */
+struct internal_mpi_double_int { double d; int i; };
+extern struct mpi_datatype mpi_datatype_double_int
+ __attribute__(( type_tag_for_datatype(mpi, struct internal_mpi_double_int,
+ layout_compatible) ));
+
+#define MPI_DOUBLE_INT ((MPI_Datatype) &amp;mpi_datatype_double_int)
+
+/* In user code */
+struct my_pair { double a; int b; };
+struct my_pair *buffer;
+MPI_Send(buffer, 1, MPI_DOUBLE_INT /*, ... */); // no warning
+
+struct my_int_pair { int a; int b; }
+struct my_int_pair *buffer2;
+MPI_Send(buffer2, 1, MPI_DOUBLE_INT /*, ... */); // warning: actual buffer element
+ // type 'struct my_int_pair'
+ // doesn't match specified MPI_Datatype
+</pre>
+</blockquote>
+</li>
+
+<li><tt>must_be_null</tt> specifies that the expression should be a null
+pointer constant, for example:
+
+<blockquote>
+<pre>
+/* In mpi.h */
+extern struct mpi_datatype mpi_datatype_null
+ __attribute__(( type_tag_for_datatype(mpi, void, must_be_null) ));
+
+#define MPI_DATATYPE_NULL ((MPI_Datatype) &amp;mpi_datatype_null)
+
+/* In user code */
+MPI_Send(buffer, 1, MPI_DATATYPE_NULL /*, ... */); // warning: MPI_DATATYPE_NULL
+ // was specified but buffer
+ // is not a null pointer
+</pre>
+</blockquote>
+</li>
+</ul>
+
</div>
</body>
</html>
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;
+