forked from OSchip/llvm-project
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. llvm-svn: 124217
This commit is contained in:
parent
1c5a53e200
commit
08a4f8f197
|
@ -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>
|
||||
|
@ -340,6 +341,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>
|
||||
<!-- ======================================================================= -->
|
||||
|
|
|
@ -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">,
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -104,6 +104,7 @@ public:
|
|||
AT_dllimport,
|
||||
AT_ext_vector_type,
|
||||
AT_fastcall,
|
||||
AT_forbid_temporaries,
|
||||
AT_format,
|
||||
AT_format_arg,
|
||||
AT_global,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>();
|
||||
}
|
Loading…
Reference in New Issue