Skip to content

[clang] Output an error when [[lifetimebound]] attribute is applied on a function parameter while the function returns void #113460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 25, 2024
9 changes: 9 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,15 @@ C++ Specific Potentially Breaking Changes
// Fixed version:
unsigned operator""_udl_name(unsigned long long);

- Clang will now produce an error diagnostic when [[clang::lifetimebound]] is
applied on a parameter of a function that returns void. This was previously
ignored and had no effect. (#GH107556)

.. code-block:: c++

// Now diagnoses with an error.
void f(int& i [[clang::lifetimebound]]);

ABI Changes in This Version
---------------------------

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10097,6 +10097,9 @@ def err_lifetimebound_no_object_param : Error<
def err_lifetimebound_ctor_dtor : Error<
"'lifetimebound' attribute cannot be applied to a "
"%select{constructor|destructor}0">;
def err_lifetimebound_void_return_type : Error<
"'lifetimebound' attribute cannot be applied to a parameter of a function "
"that returns void">;

// CHECK: returning address/reference of stack memory
def warn_ret_stack_addr_ref : Warning<
Expand Down
14 changes: 13 additions & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6940,7 +6940,7 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) {
}
}

// Check the attributes on the function type, if any.
// Check the attributes on the function type and function params, if any.
if (const auto *FD = dyn_cast<FunctionDecl>(&ND)) {
// Don't declare this variable in the second operand of the for-statement;
// GCC miscompiles that by ending its lifetime before evaluating the
Expand Down Expand Up @@ -6970,6 +6970,18 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) {
}
}
}

for (unsigned int I = 0; I < FD->getNumParams(); ++I) {
const ParmVarDecl *P = FD->getParamDecl(I);

// The [[lifetimebound]] attribute can be applied to a function parameter
// only if the function returns a value.
if (auto *A = P->getAttr<LifetimeBoundAttr>()) {
if (!isa<CXXConstructorDecl>(FD) && FD->getReturnType()->isVoidType()) {
S.Diag(A->getLocation(), diag::err_lifetimebound_void_return_type);
}
}
}
}
}

Expand Down
12 changes: 10 additions & 2 deletions clang/test/SemaCXX/attr-lifetimebound.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// RUN: %clang_cc1 -std=c++23 -verify %s

namespace usage_invalid {
// FIXME: Should we diagnose a void return type?
void voidreturn(int &param [[clang::lifetimebound]]);
void void_return(int &param [[clang::lifetimebound]]); // expected-error {{'lifetimebound' attribute cannot be applied to a parameter of a function that returns void}}

int *not_class_member() [[clang::lifetimebound]]; // expected-error {{non-member function has no implicit object parameter}}
struct A {
Expand All @@ -12,6 +11,8 @@ namespace usage_invalid {
int *explicit_object(this A&) [[clang::lifetimebound]]; // expected-error {{explicit object member function has no implicit object parameter}}
int not_function [[clang::lifetimebound]]; // expected-error {{only applies to parameters and implicit object parameters}}
int [[clang::lifetimebound]] also_not_function; // expected-error {{cannot be applied to types}}
// FIXME: Should diagnose a void return type.
void void_return_member() [[clang::lifetimebound]];
};
int *attr_with_param(int &param [[clang::lifetimebound(42)]]); // expected-error {{takes no arguments}}
}
Expand All @@ -31,6 +32,13 @@ namespace usage_ok {
return *(int*)param;
}

template <class T, class R = void> R dependent_void(const T& t [[clang::lifetimebound]]);
void dependent_void_instantiation() {
dependent_void<int>(1); // OK: Returns void.
int x = dependent_void<int, int>(1); // expected-warning {{temporary whose address is used as value of local variable 'x' will be destroyed at the end of the full-expression}}
dependent_void<int, int>(1); // OK: Returns an unused value.
}

struct A {
A();
A(int);
Expand Down
Loading