llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: kelbon <details> <summary>Changes</summary> --- Full diff: https://github.com/llvm/llvm-project/pull/204318.diff 7 Files Affected: - (modified) clang/include/clang/Basic/Attr.td (+8) - (modified) clang/include/clang/Basic/AttrDocs.td (+22) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4) - (modified) clang/include/clang/Sema/Sema.h (+5) - (modified) clang/lib/Sema/SemaDecl.cpp (+1-1) - (modified) clang/lib/Sema/SemaExpr.cpp (+24-1) - (added) clang/test/SemaCXX/attr-drop.cpp (+35) ``````````diff diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 1745c5a4f4c2a..d4c23cee76560 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2128,6 +2128,14 @@ def ExplicitInit : InheritableAttr { let SimpleHandler = 1; } +def Drop : InheritableAttr { + let Spellings = [Clang<"drop">]; + let Subjects = SubjectList<[ParmVar], ErrorDiag>; + let Documentation = [DropDocs]; + let LangOpts = [CPlusPlus]; + let SimpleHandler = 1; +} + def LifetimeBound : DeclOrTypeAttr { let Spellings = [Clang<"lifetimebound", 0>]; let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 04362de2d5be2..91b578a35739a 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4633,6 +4633,28 @@ It is only supported when using the Microsoft C++ ABI. }]; } +def DropDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``drop`` attribute, applied to a function parameter, causes any local +variable passed by name as that argument to become unusable in the calling +function from that point on. Any subsequent reference to the variable's +name is diagnosed as an error. +The variable's destructor still runs normally at the end of its scope; +this attribute only affects name lookup for further uses, not code generation. + +.. code-block:: c++ + + void drop([[clang::drop]] T value); + + void foo() { + int x = 42; + drop(x); + bar(x); // error: use of 'x' after it was dropped + } + }]; +} + def LifetimeBoundDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f84cd8dca6d4c..deec1b9dff9a5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7646,6 +7646,10 @@ def warn_cxx20_compat_use_of_unaddressable_function : Warning< "taking address of non-addressable standard library function " "is incompatible with C++20">, InGroup<CXX20Compat>; +def err_use_of_dropped_var : Error< + "use of %0 after it was dropped">; +def note_dropped_here : Note<"%0 dropped here">; + def warn_redundant_move_on_return : Warning< "redundant move in return statement">, InGroup<RedundantMove>, DefaultIgnore; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b8d760e7e0975..62eaaf7fcd658 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2072,6 +2072,11 @@ class Sema final : public SemaBase { // #pragma strict_gs_check. PragmaStack<bool> StrictGuardStackCheckStack; + /// local dropped variables ([[clang::drop]]) + llvm::DenseMap<const VarDecl *, SourceLocation> DroppedVars; + + void DiagnoseUseOfDroppedDecl(ValueDecl *D, SourceLocation Loc); + // This stack tracks the current state of Sema.CurFPFeatures. PragmaStack<FPOptionsOverride> FpPragmaStack; FPOptionsOverride CurFPFeatureOverrides() { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index cddcf3a010279..0d1a81747c4b9 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16050,7 +16050,7 @@ Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Declarator &D, if (!Bases.empty()) OpenMP().ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(Dcl, Bases); - + DroppedVars.clear(); return Dcl; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index f2745425588f5..65b59d7bb2d76 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2463,12 +2463,24 @@ NonOdrUseReason Sema::getNonOdrUseReasonInCurrentContext(ValueDecl *D) { return NOUR_None; } +void Sema::DiagnoseUseOfDroppedDecl(ValueDecl *D, SourceLocation Loc) { + auto *VD = dyn_cast<VarDecl>(D); + if (!VD) + return; + auto It = DroppedVars.find(VD); + if (It == DroppedVars.end()) + return; + Diag(Loc, diag::err_use_of_dropped_var) << VD; + Diag(It->second, diag::note_dropped_here) << VD; +} + DeclRefExpr * Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, const DeclarationNameInfo &NameInfo, NestedNameSpecifierLoc NNS, NamedDecl *FoundD, SourceLocation TemplateKWLoc, const TemplateArgumentListInfo *TemplateArgs) { + DiagnoseUseOfDroppedDecl(D, NameInfo.getLoc()); bool RefersToCapturedVariable = isa<VarDecl, BindingDecl>(D) && NeedToCaptureVariable(D, NameInfo.getLoc()); @@ -7393,7 +7405,18 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, if (CheckOtherCall(TheCall, Proto)) return ExprError(); } - + if (FunctionDecl *FDecl = TheCall->getDirectCallee()) { + unsigned N = std::min(TheCall->getNumArgs(), FDecl->getNumParams()); + for (unsigned I = 0; I < N; ++I) { + if (!FDecl->getParamDecl(I)->hasAttr<DropAttr>()) + continue; + if (auto *DRE = dyn_cast<DeclRefExpr>( + TheCall->getArg(I)->IgnoreParenImpCasts())) { + if (auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) + DroppedVars.try_emplace(VD,TheCall->getBeginLoc()); + } + } + } return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); } diff --git a/clang/test/SemaCXX/attr-drop.cpp b/clang/test/SemaCXX/attr-drop.cpp new file mode 100644 index 0000000000000..3d98a6e294e16 --- /dev/null +++ b/clang/test/SemaCXX/attr-drop.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s + +template<typename T> +T drop([[clang::drop]] T& value); + +void foo() { + int x = 42; + drop(x); // expected-note {{'x' dropped here}} + int y = x; // expected-error{{use of 'x' after it was dropped}} +} + +template<typename A, typename B> +void bar(A, B); + +void foo2() { + int x = 42; + bar(x, drop(x)); + int y = 42; + bar(drop(y), y); // expected-error {{use of 'y' after it was dropped}} expected-note {{'y' dropped here}} +} + +struct type { + int x = 42; +}; + +int& baz(); + +void drop_nondropable() { + drop(baz()); + int x = 42; + type a; + drop(a.x); + drop(a.x); + drop(x); +} `````````` </details> https://github.com/llvm/llvm-project/pull/204318 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
