Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions clang/lib/Sema/CheckExprLifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,20 @@ static bool isNormalAssignmentOperator(const FunctionDecl *FD) {
return false;
}

static const FunctionDecl *
getDeclWithMergedLifetimeBoundAttrs(const FunctionDecl *FD) {
return FD != nullptr ? FD->getMostRecentDecl() : nullptr;
}

static const CXXMethodDecl *
getDeclWithMergedLifetimeBoundAttrs(const CXXMethodDecl *CMD) {
const FunctionDecl *FD = CMD;
return cast_if_present<CXXMethodDecl>(
getDeclWithMergedLifetimeBoundAttrs(FD));
}

bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
FD = getDeclWithMergedLifetimeBoundAttrs(FD);
const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
if (!TSI)
return false;
Expand Down Expand Up @@ -645,21 +658,22 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
}
}

for (unsigned I = 0,
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
I != N; ++I) {
const FunctionDecl *CanonCallee = getDeclWithMergedLifetimeBoundAttrs(Callee);
unsigned NP = std::min(Callee->getNumParams(), CanonCallee->getNumParams());
for (unsigned I = 0, N = std::min<unsigned>(NP, Args.size()); I != N; ++I) {
Expr *Arg = Args[I];
RevertToOldSizeRAII RAII(Path);
if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) {
Path.push_back(
{IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()});
Arg = DAE->getExpr();
}
if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
if (CheckCoroCall ||
CanonCallee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg);
else if (const auto *CaptureAttr =
Callee->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>();
CaptureAttr && isa<CXXConstructorDecl>(Callee) &&
CanonCallee->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>();
CaptureAttr && isa<CXXConstructorDecl>(CanonCallee) &&
llvm::any_of(CaptureAttr->params(), [](int ArgIdx) {
return ArgIdx == LifetimeCaptureByAttr::THIS;
}))
Expand All @@ -676,11 +690,11 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
// `lifetimebound` and shares the same code path. This implies the emitted
// diagnostics will be emitted under `-Wdangling`, not
// `-Wdangling-capture`.
VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg);
else if (EnableGSLAnalysis && I == 0) {
// Perform GSL analysis for the first argument
if (shouldTrackFirstArgument(Callee)) {
VisitGSLPointerArg(Callee, Arg);
if (shouldTrackFirstArgument(CanonCallee)) {
VisitGSLPointerArg(CanonCallee, Arg);
} else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Call);
Ctor && shouldTrackFirstArgumentForConstructor(Ctor)) {
VisitGSLPointerArg(Ctor->getConstructor(), Arg);
Expand Down Expand Up @@ -1243,7 +1257,8 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
return Report;
}

static bool isAssignmentOperatorLifetimeBound(CXXMethodDecl *CMD) {
static bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) {
CMD = getDeclWithMergedLifetimeBoundAttrs(CMD);
return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
CMD->getParamDecl(0)->hasAttr<LifetimeBoundAttr>();
}
Expand Down
68 changes: 48 additions & 20 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3227,6 +3227,42 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
if (!foundAny) New->dropAttrs();
}

// Returns the number of added attributes.
template <class T>
static unsigned propagateAttribute(ParmVarDecl *To, const ParmVarDecl *From,
Sema &S) {
unsigned found = 0;
for (const auto *I : From->specific_attrs<T>()) {
if (!DeclHasAttr(To, I)) {
T *newAttr = cast<T>(I->clone(S.Context));
newAttr->setInherited(true);
To->addAttr(newAttr);
++found;
}
}
return found;
}

template <class F>
static void propagateAttributes(ParmVarDecl *To, const ParmVarDecl *From,
F &&propagator) {
if (!From->hasAttrs()) {
return;
}

bool foundAny = To->hasAttrs();

// Ensure that any moving of objects within the allocated map is
// done before we process them.
if (!foundAny)
To->setAttrs(AttrVec());

foundAny |= std::forward<F>(propagator)(To, From) != 0;

if (!foundAny)
To->dropAttrs();
}

/// mergeParamDeclAttributes - Copy attributes from the old parameter
/// to the new one.
static void mergeParamDeclAttributes(ParmVarDecl *newDecl,
Expand All @@ -3250,26 +3286,17 @@ static void mergeParamDeclAttributes(ParmVarDecl *newDecl,
diag::note_carries_dependency_missing_first_decl) << 1/*Param*/;
}

if (!oldDecl->hasAttrs())
return;

bool foundAny = newDecl->hasAttrs();

// Ensure that any moving of objects within the allocated map is
// done before we process them.
if (!foundAny) newDecl->setAttrs(AttrVec());

for (const auto *I : oldDecl->specific_attrs<InheritableParamAttr>()) {
if (!DeclHasAttr(newDecl, I)) {
InheritableAttr *newAttr =
cast<InheritableParamAttr>(I->clone(S.Context));
newAttr->setInherited(true);
newDecl->addAttr(newAttr);
foundAny = true;
}
}

if (!foundAny) newDecl->dropAttrs();
propagateAttributes(
newDecl, oldDecl, [&S](ParmVarDecl *To, const ParmVarDecl *From) {
unsigned found = 0;
found += propagateAttribute<InheritableParamAttr>(To, From, S);
// Propagate the lifetimebound attribute from parameters to the
// most recent declaration. Note that this doesn't include the implicit
// 'this' parameter, as the attribute is applied to the function type in
// that case.
found += propagateAttribute<LifetimeBoundAttr>(To, From, S);
return found;
});
}

static bool EquivalentArrayTypes(QualType Old, QualType New,
Expand Down Expand Up @@ -6948,6 +6975,7 @@ static void checkInheritableAttr(Sema &S, NamedDecl &ND) {
static void checkLifetimeBoundAttr(Sema &S, NamedDecl &ND) {
// Check the attributes on the function type and function params, if any.
if (const auto *FD = dyn_cast<FunctionDecl>(&ND)) {
FD = FD->getMostRecentDecl();
// Don't declare this variable in the second operand of the for-statement;
// GCC miscompiles that by ending its lifetime before evaluating the
// third operand. See gcc.gnu.org/PR86769.
Expand Down
5 changes: 5 additions & 0 deletions clang/test/SemaCXX/attr-lifetimebound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ namespace usage_invalid {
namespace usage_ok {
struct IntRef { int *target; };

const int &crefparam(const int &param); // Omitted in first decl
const int &crefparam(const int &param); // Omitted in second decl
const int &crefparam(const int &param [[clang::lifetimebound]]); // Add LB
const int &crefparam(const int &param) { return param; } // Omit in impl
int &refparam(int &param [[clang::lifetimebound]]);
int &classparam(IntRef param [[clang::lifetimebound]]);

Expand Down Expand Up @@ -48,6 +52,7 @@ namespace usage_ok {
int *p = A().class_member(); // expected-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
int *q = A(); // expected-warning {{temporary whose address is used as value of local variable 'q' will be destroyed at the end of the full-expression}}
int *r = A(1); // expected-warning {{temporary whose address is used as value of local variable 'r' will be destroyed at the end of the full-expression}}
const int& s = crefparam(2); // expected-warning {{temporary bound to local reference 's' will be destroyed at the end of the full-expression}}

void test_assignment() {
p = A().class_member(); // expected-warning {{object backing the pointer p will be destroyed at the end of the full-expression}}
Expand Down
Loading