From: Jeffrey Yasskin
Date: Tue, 25 Jan 2011 20:08:12 +0000 (+0000)
Subject: Add an attribute to forbid temporary instances of a type. This allows class
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c60e13aeff96515d27638129154b1308c15ded3d;p=clang
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
---
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 {
Vectors and Extended Vectors
Messages on deprecated and unavailable attributes
Attributes on enumerators
+Attribute to forbid temporaries of a type
Checks for Standard Language Features
Query for this feature with __has_feature(enumerator_attributes).
+
+Attribute to forbid temporaries of a type
+
+
+Clang provides a forbid_temporaries attribute to forbid
+temporaries of a particular type.
+
+
+class __attribute__((forbid_temporaries)) scoped_lock {
+ ...
+};
+
+
+This prevents mistakes like
+
+
+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.
+};
+
+
+Query for this feature with __has_attribute(forbid_temporaries).
+Use -Wno-forbid-temporaries to disable the resulting warning.
+
Checks for Standard Language Features
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>;
// 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(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(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(RT->getDecl());
+ if (RD->getAttr())
+ 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(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
+class __attribute__((forbid_temporaries)) NotATemporaryTpl {
+};
+
+template
+void bar_tpl(const NotATemporaryTpl&);
+
+void tpl_user() {
+ NotATemporaryTpl this_is_fine;
+ bar_tpl(NotATemporaryTpl()); // expected-warning {{must not create temporaries of type 'NotATemporaryTpl'}}
+ NotATemporaryTpl(); // expected-warning {{must not create temporaries of type 'NotATemporaryTpl'}}
+}
+
+
+// Test that a specialization can override the template's default.
+struct TemporariesOk;
+template<> class NotATemporaryTpl {
+};
+
+void specialization_user() {
+ NotATemporaryTpl this_is_fine;
+ bar_tpl(NotATemporaryTpl());
+ NotATemporaryTpl();
+}