https://github.com/philnik777 updated 
https://github.com/llvm/llvm-project/pull/169815

>From fe84b8ea3d85d7ea56e0e23d5d32a14309525535 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <[email protected]>
Date: Wed, 9 Jul 2025 15:08:44 +0200
Subject: [PATCH] [clang] Add [[clang::returns]]

---
 clang/include/clang/Basic/Attr.td             |  7 +++
 clang/include/clang/Basic/AttrDocs.td         | 16 ++++++
 .../clang/Basic/DiagnosticSemaKinds.td        |  5 ++
 clang/lib/CodeGen/CGCall.cpp                  |  9 ++++
 clang/lib/Sema/SemaDeclAttr.cpp               | 51 +++++++++++++++++++
 .../test/CodeGenCXX/attr-returns-argument.cpp | 41 +++++++++++++++
 ...a-attribute-supported-attributes-list.test |  1 +
 clang/test/SemaCXX/attr-returns-argument.cpp  | 18 +++++++
 8 files changed, 148 insertions(+)
 create mode 100644 clang/test/CodeGenCXX/attr-returns-argument.cpp
 create mode 100644 clang/test/SemaCXX/attr-returns-argument.cpp

diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 8e5f7ef0bb82d..c90f29fb2011c 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2682,6 +2682,13 @@ def NoEscape : Attr {
   let Documentation = [NoEscapeDocs];
 }
 
+def ReturnsArgument : InheritableAttr {
+  let Spellings = [Clang<"returns_argument">];
+  let Subjects = SubjectList<[Function]>;
+  let Args = [ParamIdxArgument<"ReturnedVal">];
+  let Documentation = [ReturnsArgumentDocs];
+}
+
 def MaybeUndef : InheritableAttr {
   let Spellings = [Clang<"maybe_undef">];
   let Subjects = SubjectList<[ParmVar]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index c1b1510f363d4..3e9853567599b 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -287,6 +287,22 @@ applies to copies of the block. For example:
   }];
 }
 
+def ReturnsArgumentDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``returns_argument`` attribute can be placed on a function to indicate when
+an argument is returned identically. This allows the compiler to use the
+returned value instead of the argument, potentially decreasing register
+pressure. This attribute can currently only be applied to arguments that are
+pointers or references to the same type as the returned pointer or reference.
+
+Sample usage:
+.. code-block:: c
+
+  [[clang::returns_argument(1)]] void* memcpy(void*, const void*, size_t);
+  }];
+}
+
 def MaybeUndefDocs : Documentation {
   let Category = DocCatVariable;
   let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4a145fd71eedd..d203380acccbd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3439,6 +3439,9 @@ def err_attribute_only_once_per_parameter : Error<
   "%0 attribute can only be applied once per parameter">;
 def err_mismatched_uuid : Error<"uuid does not match previous declaration">;
 def note_previous_uuid : Note<"previous uuid specified here">;
+def err_return_type_mismatch : Error<
+  "returned parameter has to reference the same type as the return type">;
+def note_return_type : Note<"return type is %0">;
 def warn_attribute_pointers_only : Warning<
   "%0 attribute only applies to%select{| constant}1 pointer arguments">,
   InGroup<IgnoredAttributes>;
@@ -3473,6 +3476,8 @@ def warn_attribute_return_pointers_refs_only : Warning<
 def warn_attribute_pointer_or_reference_only : Warning<
   "%0 attribute only applies to a pointer or reference (%1 is invalid)">,
   InGroup<IgnoredAttributes>;
+def err_attribute_pointer_or_reference_only : Error<
+  warn_attribute_pointer_or_reference_only.Summary>;
 def err_attribute_no_member_pointers : Error<
   "%0 attribute cannot be used with pointers to members">;
 def err_attribute_invalid_implicit_this_argument : Error<
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index efacb3cc04c01..02471fbf718cb 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -3009,6 +3009,15 @@ void CodeGenModule::ConstructAttributeList(StringRef 
Name,
             getLLVMContext(), llvm::AttributeSet::get(getLLVMContext(), 
Attrs));
     }
   }
+
+  if (TargetDecl) {
+    if (const auto *Returns = TargetDecl->getAttr<ReturnsArgumentAttr>()) {
+      llvm::AttributeSet &Attrs =
+          ArgAttrs[Returns->getReturnedVal().getLLVMIndex()];
+      Attrs = Attrs.addAttribute(getLLVMContext(), llvm::Attribute::Returned);
+    }
+  }
+
   assert(ArgNo == FI.arg_size());
 
   ArgNo = 0;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index e3af5023c74d0..d264c9481cc38 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1375,6 +1375,54 @@ static void handleNoEscapeAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
   D->addAttr(::new (S.Context) NoEscapeAttr(S.Context, AL));
 }
 
+static void handleReturnsArgumentAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  auto *FD = cast<FunctionDecl>(D);
+
+  ParamIdx Index;
+  if (!S.checkFunctionOrMethodParameterIndex(
+          D, AL, 1, AL.getArgAsExpr(0), Index, /*CanIndexImplicitThis=*/true))
+    return;
+
+  QualType ArgType;
+
+  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
+      MD && MD->isInstance() && Index.getLLVMIndex() == 0) {
+    ArgType = MD->getThisType();
+  } else {
+    ArgType = MD->getParamDecl(Index.getASTIndex())->getType();
+  }
+
+  if (!ArgType->isPointerOrReferenceType()) {
+    S.Diag(D->getBeginLoc(), diag::err_attribute_pointer_or_reference_only)
+        << AL << ArgType;
+    return;
+  }
+
+  ArgType = ArgType->isPointerType() ? ArgType->getPointeeType()
+                                     : ArgType.getNonReferenceType();
+
+  QualType ReturnType = FD->getReturnType();
+
+  if (!ReturnType->isPointerOrReferenceType()) {
+    S.Diag(D->getBeginLoc(), diag::err_return_type_mismatch);
+    S.Diag(FD->getReturnTypeSourceRange().getBegin(), diag::note_return_type)
+        << FD->getReturnType();
+    return;
+  }
+
+  ReturnType = ReturnType->isPointerType() ? ReturnType->getPointeeType()
+                                           : ReturnType.getNonReferenceType();
+
+  if (!S.Context.hasSameType(ReturnType, ArgType)) {
+    S.Diag(D->getBeginLoc(), diag::err_return_type_mismatch);
+    S.Diag(FD->getReturnTypeSourceRange().getBegin(), diag::note_return_type)
+        << FD->getReturnType();
+    return;
+  }
+
+  D->addAttr(ReturnsArgumentAttr::Create(S.Context, Index, AL));
+}
+
 static void handleAssumeAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   Expr *E = AL.getArgAsExpr(0),
        *OE = AL.getNumArgs() > 1 ? AL.getArgAsExpr(1) : nullptr;
@@ -7361,6 +7409,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 
const ParsedAttr &AL,
   case ParsedAttr::AT_NoEscape:
     handleNoEscapeAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_ReturnsArgument:
+    handleReturnsArgumentAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_MaybeUndef:
     handleSimpleAttribute<MaybeUndefAttr>(S, D, AL);
     break;
diff --git a/clang/test/CodeGenCXX/attr-returns-argument.cpp 
b/clang/test/CodeGenCXX/attr-returns-argument.cpp
new file mode 100644
index 0000000000000..7d1f61c24f0d1
--- /dev/null
+++ b/clang/test/CodeGenCXX/attr-returns-argument.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 -triple=x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | 
FileCheck %s
+
+[[clang::returns_argument(1)]] int* test3(int* i) {
+  // CHECK: @_Z5test3Pi(ptr noundef returned
+  return i;
+}
+
+[[clang::returns_argument(1)]] int& test4(int& i) {
+  // CHECK: @_Z5test4Ri(ptr noundef nonnull returned
+  return i;
+}
+
+[[clang::returns_argument(1)]] int* test5(int* const i) {
+  // CHECK: _Z5test5Pi(ptr noundef returned
+  return i;
+}
+
+[[clang::returns_argument(2)]] int* test6(int*, int* i) {
+  // CHECK: @_Z5test6PiS_(ptr noundef %{{.*}}, ptr noundef returned
+  return i;
+}
+
+[[clang::returns_argument(1)]] int& test7(int* i) {
+  //CHECK: @_Z5test7Pi(ptr noundef returned
+  return *i;
+}
+
+struct S {
+  [[clang::returns_argument(1)]] S& func1();
+  [[clang::returns_argument(1)]] static S& func4(S*);
+};
+
+S& S::func1() {
+  // CHECK: @_ZN1S5func1Ev(ptr noundef nonnull returned
+  return *this;
+}
+
+S& S::func4(S* i) {
+  // CHECK: @_ZN1S5func4EPS_(ptr noundef returned
+  return *i;
+}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test 
b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 747eb17446c87..59b2501f103a7 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -185,6 +185,7 @@
 // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function)
 // CHECK-NEXT: Restrict (SubjectMatchRule_function)
 // CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, 
SubjectMatchRule_variable_is_parameter)
+// CHECK-NEXT: ReturnsArgument (SubjectMatchRule_function)
 // CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, 
SubjectMatchRule_function)
 // CHECK-NEXT: ReturnsTwice (SubjectMatchRule_function)
 // CHECK-NEXT: SYCLExternal (SubjectMatchRule_function)
diff --git a/clang/test/SemaCXX/attr-returns-argument.cpp 
b/clang/test/SemaCXX/attr-returns-argument.cpp
new file mode 100644
index 0000000000000..ed0960bddf9e7
--- /dev/null
+++ b/clang/test/SemaCXX/attr-returns-argument.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+[[clang::returns_argument(1)]] int test1(int); // expected-error 
{{'clang::returns_argument' attribute only applies to a pointer or reference 
('int' is invalid)}}
+[[clang::returns_argument(1)]] int* test2(long*); // expected-error {{returned 
parameter has to reference the same type as the return type}} \
+                                            expected-note {{return type is 
'int *'}}
+[[clang::returns_argument(1)]] int* test3(int*);
+[[clang::returns_argument(1)]] int& test4(int&);
+[[clang::returns_argument(1)]] int* test5(int* const);
+[[clang::returns_argument(1)]] int& test6(int*);
+[[clang::returns_argument(2)]] int& test7(int*); // expected-error 
{{'clang::returns_argument' attribute parameter 1 is out of bounds}}
+
+struct S {
+  [[clang::returns_argument(1)]] S& func1();
+  [[clang::returns_argument(2)]] S& func2(); // expected-error 
{{'clang::returns_argument' attribute parameter 1 is out of bounds}}
+  [[clang::returns_argument(1)]] int* func3(int*); // expected-error 
{{returned parameter has to reference the same type as the return type}} \
+                                             expected-note {{return type is 
'int *'}}
+  [[clang::returns_argument(1)]] static S& func4(S*);
+};

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

Reply via email to