https://github.com/kelbon created 
https://github.com/llvm/llvm-project/pull/204318

None

>From 45008909811ddd60e14a6d0069f0d4a1847b9fcd Mon Sep 17 00:00:00 2001
From: kelbon <[email protected]>
Date: Wed, 17 Jun 2026 13:55:16 +0500
Subject: [PATCH] clang::drop attribute

---
 clang/include/clang/Basic/Attr.td             |  8 +++++
 clang/include/clang/Basic/AttrDocs.td         | 22 ++++++++++++
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +++
 clang/include/clang/Sema/Sema.h               |  5 +++
 clang/lib/Sema/SemaDecl.cpp                   |  2 +-
 clang/lib/Sema/SemaExpr.cpp                   | 25 ++++++++++++-
 clang/test/SemaCXX/attr-drop.cpp              | 35 +++++++++++++++++++
 7 files changed, 99 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/SemaCXX/attr-drop.cpp

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);
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to