diff options
author | Jeffrey Yasskin <jyasskin@google.com> | 2011-01-25 20:08:12 +0000 |
---|---|---|
committer | Jeffrey Yasskin <jyasskin@google.com> | 2011-01-25 20:08:12 +0000 |
commit | c60e13aeff96515d27638129154b1308c15ded3d (patch) | |
tree | f170899d1f2267e8b075786da203ee1633ba29c0 | |
parent | 48a4ce7d484a448490edfe9e1d47b806cee85f30 (diff) |
Add an attribute to forbid temporary instances of a type. This allows class
authors to write
class __attribute__((forbid_temporaries)) Name { ... };
when they want to force users to name all variables of the type. This protects
people from doing things like creating a scoped_lock that only lives for a
single statement instead of an entire scope.
The warning produced by this attribute can be disabled by
-Wno-forbid-temporaries.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124217 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | docs/LanguageExtensions.html | 28 | ||||
-rw-r--r-- | include/clang/Basic/Attr.td | 5 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 3 | ||||
-rw-r--r-- | include/clang/Sema/AttributeList.h | 1 | ||||
-rw-r--r-- | lib/Sema/AttributeList.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 20 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 5 | ||||
-rw-r--r-- | test/SemaCXX/forbid-temporaries.cpp | 50 |
8 files changed, 112 insertions, 1 deletions
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 78c3cd50bf..06b01db760 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -25,6 +25,7 @@ td { <li><a href="#vectors">Vectors and Extended Vectors</a></li> <li><a href="#deprecated">Messages on <tt>deprecated</tt> and <tt>unavailable</tt> attributes</a></li> <li><a href="#attributes-on-enumerators">Attributes on enumerators</a></li> +<li><a href="#forbid-temporaries-attribute">Attribute to forbid temporaries of a type</a></li> <li><a href="#checking_language_features">Checks for Standard Language Features</a></li> <ul> <li><a href="#cxx_exceptions">C++ exceptions</a></li> @@ -341,6 +342,33 @@ individual enumerators.</p> <p>Query for this feature with <tt>__has_feature(enumerator_attributes)</tt>.</p> <!-- ======================================================================= --> +<h2 id="forbid-temporaries-attribute">Attribute to forbid temporaries of a type</h2> +<!-- ======================================================================= --> + +<p>Clang provides a <tt>forbid_temporaries</tt> attribute to forbid +temporaries of a particular type.</p> + +<blockquote> +<pre>class __attribute__((forbid_temporaries)) scoped_lock { + ... +};</pre> +</blockquote> + +<p>This prevents mistakes like</p> + +<blockquote> +<pre>void foo() { + scoped_lock(my_mutex); + // Forgot the local variable name, so destructor runs here. + code_that_needs_lock_held(); + // User expects destructor to run here. +};</pre> +</blockquote> + +<p>Query for this feature with <tt>__has_attribute(forbid_temporaries)</tt>. +Use <tt>-Wno-forbid-temporaries</tt> to disable the resulting warning.</p> + +<!-- ======================================================================= --> <h2 id="checking_language_features">Checks for Standard Language Features</h2> <!-- ======================================================================= --> diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 9b0982a29b..010736112d 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -232,6 +232,11 @@ def Final : InheritableAttr { let Spellings = []; } +def ForbidTemporaries : Attr { + let Spellings = ["forbid_temporaries"]; + let Subjects = [CXXRecord]; +} + def Format : InheritableAttr { let Spellings = ["format"]; let Args = [StringArgument<"Type">, IntArgument<"FormatIdx">, diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index b18294eb3f..f33285c7d4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -856,6 +856,9 @@ def err_temp_copy_deleted : Error< "of type %1 invokes deleted constructor">; def err_temp_copy_incomplete : Error< "copying a temporary object of incomplete type %0">; +def warn_temporaries_forbidden : Warning< + "must not create temporaries of type %0">, + InGroup<DiagGroup<"forbid-temporaries">>; // C++0x decltype def err_cannot_determine_declared_type_of_overloaded_function : Error< diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 91389a4d98..7ed6ffcdaf 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -104,6 +104,7 @@ public: AT_dllimport, AT_ext_vector_type, AT_fastcall, + AT_forbid_temporaries, AT_format, AT_format_arg, AT_global, diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index 77d962542b..c3efda9a7a 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -85,6 +85,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { .Case("deprecated", AT_deprecated) .Case("visibility", AT_visibility) .Case("destructor", AT_destructor) + .Case("forbid_temporaries", AT_forbid_temporaries) .Case("format_arg", AT_format_arg) .Case("gnu_inline", AT_gnu_inline) .Case("weak_import", AT_weak_import) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 474c7cb82f..21d0f46528 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -885,6 +885,24 @@ static void HandleVecReturnAttr(Decl *d, const AttributeList &Attr, d->addAttr(::new (S.Context) VecReturnAttr(Attr.getLoc(), S.Context)); } +static void HandleForbidTemporariesAttr(Decl *d, const AttributeList &Attr, + Sema &S) { + assert(Attr.isInvalid() == false); + + if (Attr.getNumArgs() != 0) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + return; + } + + if (!isa<TypeDecl>(d)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << 9 /*class*/; + return; + } + + d->addAttr(::new (S.Context) ForbidTemporariesAttr(Attr.getLoc(), S.Context)); +} + static void HandleDependencyAttr(Decl *d, const AttributeList &Attr, Sema &S) { if (!isFunctionOrMethod(d) && !isa<ParmVarDecl>(d)) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) @@ -2674,6 +2692,8 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, case AttributeList::AT_ext_vector_type: HandleExtVectorTypeAttr(scope, D, Attr, S); break; + case AttributeList::AT_forbid_temporaries: + HandleForbidTemporariesAttr(D, Attr, S); break; case AttributeList::AT_format: HandleFormatAttr (D, Attr, S); break; case AttributeList::AT_format_arg: HandleFormatArgAttr (D, Attr, S); break; case AttributeList::AT_global: HandleGlobalAttr (D, Attr, S); break; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 0b7a051365..cb02be546e 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -3188,9 +3188,12 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { } } + CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); + if (RD->getAttr<ForbidTemporariesAttr>()) + Diag(E->getExprLoc(), diag::warn_temporaries_forbidden) << E->getType(); + // That should be enough to guarantee that this type is complete. // If it has a trivial destructor, we can avoid the extra copy. - CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); if (RD->isInvalidDecl() || RD->hasTrivialDestructor()) return Owned(E); diff --git a/test/SemaCXX/forbid-temporaries.cpp b/test/SemaCXX/forbid-temporaries.cpp new file mode 100644 index 0000000000..cbe47aed5e --- /dev/null +++ b/test/SemaCXX/forbid-temporaries.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#if !__has_attribute(forbid_temporaries) +#error "Should support forbid_temporaries attribute" +#endif + +class __attribute__((forbid_temporaries)) NotATemporary { +}; + +class __attribute__((forbid_temporaries(1))) ShouldntHaveArguments { // expected-error {{attribute requires 0 argument(s)}} +}; + +void bad_function() __attribute__((forbid_temporaries)); // expected-warning {{'forbid_temporaries' attribute only applies to classes}} + +int var __attribute__((forbid_temporaries)); // expected-warning {{'forbid_temporaries' attribute only applies to classes}} + +void bar(const NotATemporary&); + +void foo() { + NotATemporary this_is_fine; + bar(NotATemporary()); // expected-warning {{must not create temporaries of type 'NotATemporary'}} + NotATemporary(); // expected-warning {{must not create temporaries of type 'NotATemporary'}} +} + + +// Check that the above restrictions work for templates too. +template<typename T> +class __attribute__((forbid_temporaries)) NotATemporaryTpl { +}; + +template<typename T> +void bar_tpl(const NotATemporaryTpl<T>&); + +void tpl_user() { + NotATemporaryTpl<int> this_is_fine; + bar_tpl(NotATemporaryTpl<int>()); // expected-warning {{must not create temporaries of type 'NotATemporaryTpl<int>'}} + NotATemporaryTpl<int>(); // expected-warning {{must not create temporaries of type 'NotATemporaryTpl<int>'}} +} + + +// Test that a specialization can override the template's default. +struct TemporariesOk; +template<> class NotATemporaryTpl<TemporariesOk> { +}; + +void specialization_user() { + NotATemporaryTpl<TemporariesOk> this_is_fine; + bar_tpl(NotATemporaryTpl<TemporariesOk>()); + NotATemporaryTpl<TemporariesOk>(); +} |