-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[clang] Infer lifetime_capture_by for STL containers #117122
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "CheckExprLifetime.h" | ||
| #include "clang/AST/ASTConsumer.h" | ||
| #include "clang/AST/Attr.h" | ||
| #include "clang/AST/Expr.h" | ||
|
|
@@ -268,6 +269,44 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) { | |
| } | ||
| } | ||
|
|
||
| static bool isPointerLikeType(QualType QT) { | ||
| QT = QT.getNonReferenceType(); | ||
| if (QT->isPointerType()) | ||
| return true; | ||
| auto *RD = QT->getAsCXXRecordDecl(); | ||
| if (!RD) | ||
| return false; | ||
| if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) | ||
| RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); | ||
| return RD->hasAttr<PointerAttr>(); | ||
| } | ||
|
|
||
| void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { | ||
| if (!FD) | ||
| return; | ||
| auto *MD = dyn_cast<CXXMethodDecl>(FD); | ||
| if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) | ||
| return; | ||
| // FIXME: Infer for operator[] for map-like containers. For example: | ||
| // std::map<string_view, ...> m; | ||
| // m[ReturnString(..)] = ...; | ||
| static const llvm::StringSet<> CapturingMethods{"insert", "push", | ||
usx95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "push_front", "push_back"}; | ||
| if (!CapturingMethods.contains(MD->getName())) | ||
| return; | ||
| // Do not infer if any parameter is explicitly annotated. | ||
| for (ParmVarDecl *PVD : MD->parameters()) | ||
| if (PVD->hasAttr<LifetimeCaptureByAttr>()) | ||
| return; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is return intentional here? Do we want an explicit attr to turn off the inference for the rest of the params? If that is the case, I think we want to turn off the inference for the params before the explicit attr as well, not just after.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this was intentional. Good catch. Disabled if any of the parameters is explicitly annotated. |
||
| for (ParmVarDecl *PVD : MD->parameters()) { | ||
| if (isPointerLikeType(PVD->getType())) { | ||
| int CaptureByThis[] = {LifetimeCaptureByAttr::THIS}; | ||
| PVD->addAttr( | ||
| LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) { | ||
| static const llvm::StringSet<> Nullable{ | ||
| "auto_ptr", "shared_ptr", "unique_ptr", "exception_ptr", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s unclear to me why this case needs to be handled separately, and we already have one in
CheckExprLifetime.cppThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We fail to detect pointer types which are templates:
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a better fix would be to propagate this annotation to the template specialization in clang.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the example. I think I understand what’s happening here.
The primary template Span is annotated with the gsl::Pointer attribute, so all its specializations should inherit this attribute.
However, in this example, at the point where we infer the
lifetime_capture_byattribute forvector's method, we don’t yet have a fully completedClassTemplateSpecializationDeclforSpan<int>(and therefore, noPointerAttr). This is likely because the instantiation ofSpan<int>isn’t required for the statementstd::vector<Span<int>> spans;.I think the following case would work without this special handling.
I don’t have a better suggestion for addressing this issue directly. However, I think we should have a comment explaining it. (Should we do the same thing for the one in
CheckExprLifetime.cpp?)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, your example still doesn't work show the warning with the lines removed:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, this is strange. I tested it locally, it works for me.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I can reproduce with
<vector>include but not with handcraftedvectorin lit tests.But I think you are right. I also suspected that we are just inspecting the template spec decl earlier than expected.