https://github.com/yujincheng08 created 
https://github.com/llvm/llvm-project/pull/229355

Support copy attribute like GCC.

>From 38f77cde7bc653cbd3d20c74199d1da9f0f5c873 Mon Sep 17 00:00:00 2001
From: LoveSy <[email protected]>
Date: Tue, 6 Oct 2026 17:41:05 +0800
Subject: [PATCH] [Clang] Support the GNU copy attribute

Add __attribute__((copy(expr))) and [[gnu::copy(expr)]] to propagate
attributes from referenced declarations and types. This lets aliases
share attributes with their targets and lets captureless lambdas adopt
a callback's calling convention.

Reapply copied attributes through existing Sema handlers to preserve
subject, argument, and conflict checks. Exclude attributes that should
not propagate, including alias, visibility, weak, inlining controls,
deprecated, and target_clones.

Handle dependent sources and preserve copied function type attributes
across template instantiation. Add semantic, AST, code generation, and
PCH tests, along with attribute documentation and a release note.

Validated with an assertions-enabled Clang build: 500 selected attribute,
calling-convention, and lambda tests passed; 3 were unsupported.
---
 clang/docs/ReleaseNotes.md                    |   4 +
 clang/include/clang/Basic/Attr.td             |  10 +
 clang/include/clang/Basic/AttrDocs.td         |  41 +++
 .../clang/Basic/DiagnosticSemaKinds.td        |   9 +
 clang/include/clang/Sema/Sema.h               |   4 +
 clang/lib/Sema/SemaDeclAttr.cpp               | 260 ++++++++++++++++++
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  21 ++
 clang/lib/Sema/SemaType.cpp                   |  48 ++++
 clang/test/AST/attr-copy.c                    | 105 +++++++
 clang/test/CodeGen/attr-copy.c                |  34 +++
 .../CodeGenCXX/attr-copy-calling-conv.cpp     |   8 +
 clang/test/PCH/attr-copy.cpp                  |  31 +++
 clang/test/Sema/attr-copy-calling-conv.c      |  24 ++
 clang/test/Sema/attr-copy.c                   |  97 +++++++
 clang/test/SemaCXX/attr-copy-calling-conv.cpp |  30 ++
 clang/test/SemaTemplate/attr-copy.cpp         |  60 ++++
 clang/utils/TableGen/ClangAttrEmitter.cpp     |  59 +++-
 17 files changed, 844 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/AST/attr-copy.c
 create mode 100644 clang/test/CodeGen/attr-copy.c
 create mode 100644 clang/test/CodeGenCXX/attr-copy-calling-conv.cpp
 create mode 100644 clang/test/PCH/attr-copy.cpp
 create mode 100644 clang/test/Sema/attr-copy-calling-conv.c
 create mode 100644 clang/test/Sema/attr-copy.c
 create mode 100644 clang/test/SemaCXX/attr-copy-calling-conv.cpp
 create mode 100644 clang/test/SemaTemplate/attr-copy.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7b878a0abbab3c..1aed07bbcdfc1c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -304,6 +304,10 @@ features cannot lower the translation-unit ABI level;
 
 ### Attribute Changes in Clang
 
+- Clang now supports `__attribute__((copy(expression)))` and
+  `[[gnu::copy(expression)]]` for copying GNU declaration attributes from
+  another function, variable, or type.
+
 - Clang now properly propagates attributes on class and variable templates to 
their redeclarations, which will result in redeclarations not interfering with 
diagnostics. (#GH209812)
 
 - Clang now recognizes the `[[gnu::flag_enum]]` attribute and treats it 
equivalent to `[[clang::flag_enum]]`
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 4ef0bd2b5ca7c1..a78c350d5d4e6b 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -878,6 +878,16 @@ def Alias : Attr {
   let Documentation = [Undocumented];
 }
 
+def Copy : InheritableAttr {
+  let Spellings = [GCC<"copy">];
+  let Args = [ExprArgument<"Source">];
+  let Subjects = SubjectList<[Function, Var, Field, Tag, TypedefName]>;
+  let ParseArgumentsAsUnevaluated = 1;
+  let TemplateDependent = 1;
+  let MeaningfulToClassTemplateDefinition = 1;
+  let Documentation = [CopyDocs];
+}
+
 def BuiltinAlias : Attr {
   let Spellings = [CXX11<"clang", "builtin_alias">,
                    C23<"clang", "builtin_alias">,
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 0ab1429ff09d67..11569376cdccb2 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -42,6 +42,47 @@ This page lists the attributes currently supported by Clang.
 }];
 }
 
+def CopyDocs : Documentation {
+  let Category = DocCatDecl;
+  let Content = [{
+The `copy(expression)` attribute applies the GNU declaration attributes of the
+entity or type referenced by `expression` to another declaration. The 
expression
+is unevaluated. For example:
+
+```c
+void *allocate(unsigned long) __attribute__((malloc, alloc_size(1), nothrow));
+extern __typeof__(allocate) allocate_alias
+    __attribute__((alias("allocate"), copy(allocate)));
+```
+
+The argument can name a function or variable, take its address, or refer to a
+type through an expression such as `(struct A *)0`. A function's attributes
+cannot be copied to a variable, or vice versa. Copied attributes are checked
+against the destination just as if they had been written there explicitly.
+
+Attributes controlling linkage, inlining, or visibility, including `alias`,
+`ifunc`, `always_inline`, `noinline`, `gnu_inline`, `visibility`, `weak`,
+`weakref`, `dllimport`, and `dllexport`, are not copied.
+Neither `deprecated` nor `target_clones` is copied. The attribute does not copy
+the source's type; `__typeof__`, as in the example, can be used when the types
+must also match.
+
+Function type attributes, such as calling conventions, can also be copied. For
+example, a captureless lambda can use `copy` to match a callback's calling
+convention:
+
+```cpp
+using Callback = int (__attribute__((ms_abi)) *)(int);
+Callback callback = [](int x) __attribute__((copy((Callback)nullptr))) {
+  return x;
+};
+```
+
+The calling convention is applied to the lambda's call operator and its
+conversion to a function pointer.
+  }];
+}
+
 def SectionDocs : Documentation {
   let Category = DocCatVariable;
   let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9208aba1445d7e..2616cda0b6fda3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4056,6 +4056,15 @@ def err_attribute_weakref_without_alias : Error<
   "weakref declaration of %0 must also have an alias attribute">;
 def err_alias_not_supported_on_darwin : Error <
   "aliases are not supported on darwin">;
+def err_attribute_copy_invalid_argument : Error<
+  "'copy' attribute requires an expression referring to a function, variable, "
+  "or type">;
+def warn_attribute_copy_kind : Warning<
+  "'copy' attribute ignored on a declaration of a different kind than its "
+  "argument">, InGroup<IgnoredAttributes>;
+def warn_attribute_copy_self : Warning<
+  "'copy' attribute ignored on a declaration referring to itself">,
+  InGroup<IgnoredAttributes>;
 def warn_attribute_non_character_array : Warning<
   "%0%select{ attribute|}1 only applies to fields or variables of character "
   "array type; type is %2">, InGroup<IgnoredAttributes>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7fbcc4aae7d6c5..e662471831630d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5050,6 +5050,10 @@ class Sema final : public SemaBase {
   /// treat a reference type as valid.
   bool isValidPointerAttrType(QualType T, bool RefOkay = false);
 
+  void AddCopyAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,
+                   bool TypeAttrsOnly = false);
+  bool ProcessCopiedTypeAttribute(Decl *D, ParsedAttr &AL);
+
   /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular
   /// declaration.
   void AddAssumeAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index eb4a8c2ab9ae01..8f882fd67ee66b 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7802,6 +7802,9 @@ ProcessDeclAttribute(Sema &S, Decl *D, const ParsedAttr 
&AL,
   case ParsedAttr::AT_Constructor:
       handleConstructorAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_Copy:
+    S.AddCopyAttr(D, AL, AL.getArgAsExpr(0));
+    break;
   case ParsedAttr::AT_Deprecated:
     handleDeprecatedAttr(S, D, AL);
     break;
@@ -8552,6 +8555,263 @@ static void checkAMDGPUReqdWorkGroupSize(Sema &S, Decl 
*D) {
   }
 }
 
+static ArgsUnion createCopyAttrArgument(ASTContext &Context, SourceLocation 
Loc,
+                                        Expr *E) {
+  return E;
+}
+
+static ArgsUnion createCopyAttrArgument(ASTContext &Context, SourceLocation 
Loc,
+                                        unsigned Value) {
+  return IntegerLiteral::Create(Context, llvm::APInt(32, Value),
+                                Context.UnsignedIntTy, Loc);
+}
+
+static ArgsUnion createCopyAttrArgument(ASTContext &Context, SourceLocation 
Loc,
+                                        ParamIdx Index) {
+  return createCopyAttrArgument(Context, Loc, Index.getSourceIndex());
+}
+
+static ArgsUnion createCopyAttrArgument(ASTContext &Context, SourceLocation 
Loc,
+                                        StringRef Value) {
+  return StringLiteral::Create(
+      Context, Value, StringLiteralKind::Ordinary, false,
+      Context.getStringLiteralArrayType(Context.CharTy, Value.size()), Loc);
+}
+
+static ArgsUnion createCopyAttrArgument(ASTContext &Context, SourceLocation 
Loc,
+                                        const IdentifierInfo *II) {
+  return new (Context) IdentifierLoc(Loc, const_cast<IdentifierInfo *>(II));
+}
+
+static ArgsUnion createCopyAttrArgument(ASTContext &Context, SourceLocation 
Loc,
+                                        FunctionDecl *FD) {
+  return DeclRefExpr::Create(Context, NestedNameSpecifierLoc(),
+                             SourceLocation(), FD, false, Loc, FD->getType(),
+                             VK_LValue);
+}
+
+#define WANT_DECL_COPY_LOGIC
+#include "clang/Sema/AttrParsedAttrImpl.inc"
+#undef WANT_DECL_COPY_LOGIC
+
+static bool isCopyableAttr(const Attr *A) {
+  // copy propagates GNU declaration attributes, not implicit attributes or
+  // attributes from other vendors. In particular, an implicit BuiltinAttr must
+  // not turn the destination into a different builtin function.
+  if (A->isImplicit() || (!A->isGNUAttribute() && !A->isGNUScope()))
+    return false;
+
+  switch (A->getKind()) {
+  case attr::Alias:
+  case attr::AlwaysInline:
+  case attr::Copy:
+  case attr::Deprecated:
+  case attr::DLLExport:
+  case attr::DLLImport:
+  case attr::GNUInline:
+  case attr::IFunc:
+  case attr::NoInline:
+  case attr::SelectAny:
+  case attr::TargetClones:
+  case attr::Visibility:
+  case attr::Weak:
+  case attr::WeakRef:
+    return false;
+  default:
+    return ParsedAttrInfo::get(*A).IsKnownToGCC;
+  }
+}
+
+void Sema::AddCopyAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,
+                       bool TypeAttrsOnly) {
+  if (E->isTypeDependent() || E->isValueDependent()) {
+    auto *A = ::new (Context) CopyAttr(Context, CI, E);
+    A->setImplicit(TypeAttrsOnly);
+    D->addAttr(A);
+    return;
+  }
+
+  Expr *SourceExpr = E->IgnoreParenCasts();
+  if (const auto *UO = dyn_cast<UnaryOperator>(SourceExpr);
+      UO && (UO->getOpcode() == UO_AddrOf || UO->getOpcode() == UO_Deref))
+    SourceExpr = UO->getSubExpr()->IgnoreParenCasts();
+
+  const Decl *Source = nullptr;
+  if (const auto *DRE = dyn_cast<DeclRefExpr>(SourceExpr))
+    Source = DRE->getDecl();
+  else if (const auto *ME = dyn_cast<MemberExpr>(SourceExpr))
+    Source = ME->getMemberDecl();
+
+  // Attributes are processed before a new function or variable declaration is
+  // linked into its redeclaration chain.
+  const auto *SourceVD = dyn_cast_or_null<ValueDecl>(Source);
+  const auto *DestVD = dyn_cast<ValueDecl>(D);
+  bool IsRedeclaration =
+      SourceVD && DestVD && SourceVD->getDeclName() == DestVD->getDeclName() &&
+      SourceVD->getDeclContext()->Equals(DestVD->getDeclContext()) &&
+      Context.hasSameType(SourceVD->getType(), DestVD->getType());
+  // Nested block scopes share a DeclContext, but can declare distinct local
+  // variables with the same name and type.
+  if (const auto *VD = dyn_cast_or_null<VarDecl>(Source))
+    IsRedeclaration &= !VD->isLocalVarDecl();
+  if (Source && (Source->getCanonicalDecl() == D->getCanonicalDecl() ||
+                 IsRedeclaration)) {
+    Diag(CI.getLoc(), diag::warn_attribute_copy_self);
+    return;
+  }
+
+  if (Source && !isa<FunctionDecl, VarDecl, FieldDecl>(Source)) {
+    Diag(E->getExprLoc(), diag::err_attribute_copy_invalid_argument);
+    return;
+  }
+
+  if (isa<StringLiteral>(SourceExpr->IgnoreParenCasts()) ||
+      (!Source && E->getType()->isArithmeticType() &&
+       E->isEvaluatable(Context)) ||
+      E->getType() == Context.OverloadTy) {
+    Diag(E->getExprLoc(), diag::err_attribute_copy_invalid_argument);
+    return;
+  }
+
+  QualType Ty = E->getType();
+  if (const auto *FD = dyn_cast_or_null<FunctionDecl>(Source))
+    Ty = FD->getType();
+  if (Source && isa<FunctionDecl>(D) != isa<FunctionDecl>(Source) &&
+      isa<FunctionDecl, VarDecl, FieldDecl>(D)) {
+    Diag(CI.getLoc(), diag::warn_attribute_copy_kind);
+    return;
+  }
+
+  SmallVector<const Decl *, 4> Sources;
+  SmallVector<const Attr *, 8> SourceAttrs;
+  if (Source)
+    Sources.push_back(Source->getMostRecentDecl());
+
+  // Keep typedef attributes as well as attributes on the underlying tag. Do
+  // not canonicalize Ty before walking it: that would discard typedefs.
+  bool SawPointer = false;
+  bool HasCallingConventionAttr = false;
+  while (true) {
+    if (const auto *TT = dyn_cast<TypedefType>(Ty.getTypePtr()))
+      Sources.push_back(TT->getDecl());
+    if (const auto *AT = dyn_cast<AttributedType>(Ty.getTypePtr())) {
+      if (const Attr *A = AT->getAttr()) {
+        SourceAttrs.push_back(A);
+        HasCallingConventionAttr |= AT->isCallingConv();
+      }
+    }
+    QualType Next = Ty.getSingleStepDesugaredType(Context);
+    if (Next != Ty) {
+      Ty = Next;
+    } else if (!SawPointer && Ty->isPointerType()) {
+      Ty = Ty->getPointeeType();
+      SawPointer = true;
+    } else {
+      break;
+    }
+  }
+  // Naming a pointer to a class template specialization need not instantiate
+  // it, but its attributes may depend on its template arguments.
+  if (Ty->getAsCXXRecordDecl())
+    isCompleteType(CI.getLoc(), Ty);
+  if (const auto *TT = Ty->getAs<TagType>())
+    Sources.push_back(TT->getDecl()->getMostRecentDecl());
+
+  AttributeFactory Factory;
+  ParsedAttributes Copied(Factory);
+  // Some function attributes are represented only in the function type's
+  // extended information, without an Attr or AttributedType node.
+  if (const auto *FT = Ty->getAs<FunctionType>()) {
+    auto Add = [&](StringRef Name, MutableArrayRef<ArgsUnion> Args = {}) {
+      Copied.addNew(&Context.Idents.get(Name), CI.getRange(), {}, Args.data(),
+                    Args.size(), AttributeCommonInfo::Form::GNU());
+    };
+    FunctionType::ExtInfo EI = FT->getExtInfo();
+    // Substituting a template type argument can discard the AttributedType
+    // sugar while retaining the calling convention in the canonical type.
+    if (!HasCallingConventionAttr) {
+      switch (EI.getCC()) {
+      case CC_X86StdCall:
+      case CC_X86FastCall:
+      case CC_X86ThisCall:
+      case CC_Win64:
+      case CC_X86_64SysV:
+      case CC_X86RegCall:
+        Add(FunctionType::getNameForCallConv(EI.getCC()));
+        break;
+      case CC_AAPCS:
+      case CC_AAPCS_VFP: {
+        ArgsUnion Arg = createCopyAttrArgument(
+            Context, CI.getLoc(), 
FunctionType::getNameForCallConv(EI.getCC()));
+        Add("pcs", Arg);
+        break;
+      }
+      default:
+        break;
+      }
+    }
+    if (EI.getNoReturn())
+      Add("noreturn");
+    if (EI.getNoCallerSavedRegs())
+      Add("no_caller_saved_registers");
+    if (EI.getNoCfCheck())
+      Add("nocf_check");
+    if (EI.getHasRegParm()) {
+      ArgsUnion Arg =
+          createCopyAttrArgument(Context, CI.getLoc(), EI.getRegParm());
+      Add("regparm", Arg);
+    }
+    if (const auto *FPT = dyn_cast<FunctionProtoType>(FT))
+      if (FPT->getExceptionSpecType() == EST_NoThrow)
+        Add("nothrow");
+  }
+  for (const Decl *From : Sources) {
+    // A type expression can refer to the very type being declared.
+    if (From->getCanonicalDecl() == D->getCanonicalDecl()) {
+      Diag(CI.getLoc(), diag::warn_attribute_copy_self);
+      continue;
+    }
+    SourceAttrs.append(From->attr_begin(), From->attr_end());
+  }
+  for (const Attr *A : SourceAttrs) {
+    if (!isCopyableAttr(A))
+      continue;
+
+    ArgsVector Args;
+    if (const auto *AA = dyn_cast<AlignedAttr>(A)) {
+      if (AA->isAlignmentExpr() && AA->getAlignmentExpr())
+        Args.push_back(AA->getAlignmentExpr());
+    } else if (!getAttributeCopyArgs(Context, A, CI.getLoc(), Args)) {
+      continue;
+    }
+
+    Copied.addNew(&Context.Idents.get(A->getSpelling()), CI.getRange(),
+                  AttributeScopeInfo(A->getScopeName(), CI.getLoc()),
+                  Args.data(), Args.size(), A->getForm());
+  }
+  ParsedAttributesView DeclAttrs;
+  bool CopiedType = false;
+  for (ParsedAttr &AL : Copied) {
+    if (ProcessCopiedTypeAttribute(D, AL))
+      CopiedType = true;
+    else if (!TypeAttrsOnly)
+      DeclAttrs.addAtEnd(&AL);
+  }
+  // Template instantiation rebuilds types from their written TypeSourceInfo,
+  // which does not contain attributes introduced by copy. An implicit copy
+  // attribute replays only the type attributes: declaration attributes are
+  // already expanded and will be instantiated normally.
+  if (CopiedType && D->isTemplated()) {
+    auto *A = ::new (Context) CopyAttr(Context, CI, E);
+    A->setImplicit(true);
+    D->addAttr(A);
+  }
+  // The enclosing attribute list may still contain attributes needed by a
+  // group check (for example, alias after weakref and copy).
+  for (const ParsedAttr &AL : DeclAttrs)
+    ProcessDeclAttribute(*this, D, AL, ProcessDeclAttributeOptions());
+}
+
 void Sema::ProcessDeclAttributeList(
     Scope *S, Decl *D, const ParsedAttributesView &AttrList,
     const ProcessDeclAttributeOptions &Options) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 5e72f05c863a0d..77d7f33273369b 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -88,6 +88,17 @@ bool TemplateDeclInstantiator::SubstQualifier(const TagDecl 
*OldDecl,
 // Include attribute instantiation code.
 #include "clang/Sema/AttrTemplateInstantiate.inc"
 
+static void
+instantiateDependentCopyAttr(Sema &S,
+                             const MultiLevelTemplateArgumentList 
&TemplateArgs,
+                             const CopyAttr *A, Decl *New) {
+  EnterExpressionEvaluationContext Unevaluated(
+      S, Sema::ExpressionEvaluationContext::Unevaluated);
+  ExprResult E = S.SubstExpr(A->getSource(), TemplateArgs);
+  if (!E.isInvalid())
+    S.AddCopyAttr(New, *A, E.get(), A->isImplicit());
+}
+
 static void instantiateDependentAlignedAttr(
     Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
     const AlignedAttr *Aligned, Decl *New, bool IsPackExpansion) {
@@ -847,6 +858,11 @@ void Sema::InstantiateAttrsForDecl(
       if (!isRelevantAttr(*this, New, TmplAttr))
         continue;
 
+      if (const auto *A = dyn_cast<CopyAttr>(TmplAttr)) {
+        instantiateDependentCopyAttr(*this, TemplateArgs, A, New);
+        continue;
+      }
+
       // FIXME: If any of the special case versions from InstantiateAttrs 
become
       // applicable to template declaration, we'll need to add them here.
       CXXThisScopeRAII ThisScope(
@@ -889,6 +905,11 @@ void Sema::InstantiateAttrs(const 
MultiLevelTemplateArgumentList &TemplateArgs,
     if (!isRelevantAttr(*this, New, TmplAttr))
       continue;
 
+    if (const auto *A = dyn_cast<CopyAttr>(TmplAttr)) {
+      instantiateDependentCopyAttr(*this, TemplateArgs, A, New);
+      continue;
+    }
+
     // FIXME: This should be generalized to more than just the AlignedAttr.
     const AlignedAttr *Aligned = dyn_cast<AlignedAttr>(TmplAttr);
     if (Aligned && Aligned->isAlignmentDependent()) {
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index e1481b437df9e0..a70505549508fa 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8468,6 +8468,54 @@ static bool handleFunctionTypeAttr(TypeProcessingState 
&state, ParsedAttr &attr,
   return true;
 }
 
+bool Sema::ProcessCopiedTypeAttribute(Decl *D, ParsedAttr &AL) {
+  switch (AL.getKind()) {
+  case ParsedAttr::AT_NoThrow:
+    if (!getLangOpts().CPlusPlus)
+      return false;
+    [[fallthrough]];
+  FUNCTION_TYPE_ATTRS_CASELIST:
+    break;
+  default:
+    return false;
+  }
+
+  QualType T;
+  if (auto *VD = dyn_cast<ValueDecl>(D))
+    T = VD->getType();
+  else if (auto *TD = dyn_cast<TypedefNameDecl>(D))
+    T = TD->getUnderlyingType();
+  else
+    return false;
+
+  if (checkCommonAttributeFeatures(D, AL))
+    return true;
+
+  // Reuse the function type attribute checks, including calling convention
+  // conflicts and restrictions on variadic functions. No declarator chunks
+  // need to be processed: the destination's type has already been built.
+  AttributeFactory Factory;
+  DeclSpec DS(Factory);
+  Declarator Declarator(DS, ParsedAttributesView::none(),
+                        DeclaratorContext::TypeName);
+  TypeProcessingState State(*this, Declarator);
+  CUDAFunctionTarget CFT = CUDAFunctionTarget::Host;
+  if (getLangOpts().CUDA)
+    if (auto *FD = dyn_cast<FunctionDecl>(D))
+      CFT = CUDA().IdentifyTarget(FD);
+  if (!handleFunctionTypeAttr(State, AL, T, CFT))
+    diagnoseBadTypeAttribute(*this, AL, T);
+  else if (!AL.isInvalid()) {
+    if (auto *VD = dyn_cast<ValueDecl>(D))
+      VD->setType(T);
+    else {
+      auto *TD = cast<TypedefNameDecl>(D);
+      TD->setModedTypeSourceInfo(TD->getTypeSourceInfo(), T);
+    }
+  }
+  return true;
+}
+
 bool Sema::hasExplicitCallingConv(QualType T) {
   const AttributedType *AT;
 
diff --git a/clang/test/AST/attr-copy.c b/clang/test/AST/attr-copy.c
new file mode 100644
index 00000000000000..725891fc3dc22a
--- /dev/null
+++ b/clang/test/AST/attr-copy.c
@@ -0,0 +1,105 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu 
-Wno-deprecated-declarations -verify -ast-dump %s | FileCheck %s
+
+void *source(int) __attribute__((malloc, alloc_size(1), nothrow, 
returns_nonnull,
+                                deprecated, weak, visibility("hidden")));
+
+// CHECK-LABEL: FunctionDecl {{.*}} copied 'void *(int)'
+// CHECK: RestrictAttr
+// CHECK: AllocSizeAttr {{.*}} 1
+// CHECK: NoThrowAttr
+// CHECK: ReturnsNonNullAttr
+// CHECK-NOT: DeprecatedAttr
+// CHECK-NOT: WeakAttr
+// CHECK-NOT: VisibilityAttr
+// CHECK-NOT: CopyAttr
+void *copied(int) __attribute__((copy(source)));
+
+// CHECK-LABEL: FunctionDecl {{.*}} chain 'void *(int)'
+// CHECK: RestrictAttr
+// CHECK: AllocSizeAttr {{.*}} 1
+// CHECK: NoThrowAttr
+// CHECK: ReturnsNonNullAttr
+void *chain(int) __attribute__((copy(copied)));
+
+void target(void) {}
+void alias_source(void) __attribute__((alias("target"), cold));
+// CHECK-LABEL: FunctionDecl {{.*}} alias_copy 'void (void)'
+// CHECK-NEXT: ColdAttr
+// CHECK-NOT: AliasAttr
+void alias_copy(void) __attribute__((copy(alias_source)));
+
+void multiversion(void) __attribute__((target_clones("default", "sse4.2"), 
nothrow));
+// CHECK-LABEL: FunctionDecl {{.*}} multiversion_copy 'void (void)'
+// CHECK-NEXT: NoThrowAttr
+// CHECK-NOT: TargetClonesAttr
+void multiversion_copy(void) __attribute__((copy(multiversion)));
+
+int source_var __attribute__((aligned(32), section("data"), weak));
+// CHECK-LABEL: VarDecl {{.*}} copied_var 'int'
+// CHECK: AlignedAttr
+// CHECK: IntegerLiteral {{.*}} 32
+// CHECK: SectionAttr {{.*}} "data"
+// CHECK-NOT: WeakAttr
+int copied_var __attribute__((copy(&source_var)));
+
+void deallocate(void *);
+// expected-warning@+1 {{'malloc' attribute ignored because Clang does not yet 
support this attribute signature}}
+void *allocate(int, int) __attribute__((malloc(deallocate, 1), alloc_size(1, 
2),
+                                      alloc_align(1), assume_aligned(32, 4)));
+// CHECK-LABEL: FunctionDecl {{.*}} allocate_copy 'void *(int, int)'
+// CHECK: RestrictAttr
+// CHECK: AllocSizeAttr {{.*}} 1 2
+// CHECK: AllocAlignAttr {{.*}} 1
+// CHECK: AssumeAlignedAttr
+void *allocate_copy(int, int) __attribute__((copy(allocate))); // 
expected-warning {{'malloc' attribute ignored because Clang does not yet 
support this attribute signature}}
+
+void zero_source(void) __attribute__((zero_call_used_regs("used")));
+// CHECK-LABEL: FunctionDecl {{.*}} zero_copy 'void (void)'
+// CHECK-NEXT: ZeroCallUsedRegsAttr {{.*}} Used
+void zero_copy(void) __attribute__((copy(zero_source)));
+
+void cleanup(int *);
+void local(void) {
+  int source_local __attribute__((cleanup(cleanup)));
+  // CHECK: VarDecl {{.*}} copy_local 'int'
+  // CHECK-NEXT: CleanupAttr {{.*}} 'cleanup'
+  int copy_local __attribute__((copy(source_local)));
+}
+
+void always_inline_source(void) __attribute__((always_inline));
+void noinline_source(void) __attribute__((noinline));
+inline void gnu_inline_source(void) __attribute__((gnu_inline));
+
+// Inlining attributes are excluded from copy even when the destination has no
+// conflicting attribute. This is not an explicit-attribute precedence rule.
+// CHECK-LABEL: FunctionDecl {{.*}} plain_always_inline_copy 'void (void)'
+// CHECK-NOT: Attr
+void plain_always_inline_copy(void) 
__attribute__((copy(always_inline_source)));
+// CHECK-LABEL: FunctionDecl {{.*}} plain_noinline_copy 'void (void)'
+// CHECK-NOT: Attr
+void plain_noinline_copy(void) __attribute__((copy(noinline_source)));
+// CHECK-LABEL: FunctionDecl {{.*}} plain_gnu_inline_copy 'void (void)'
+// CHECK-NOT: Attr
+void plain_gnu_inline_copy(void) __attribute__((copy(gnu_inline_source)));
+
+// CHECK-LABEL: FunctionDecl {{.*}} explicit_noinline_first 'void (void)'
+// CHECK-NEXT: NoInlineAttr
+// CHECK-NOT: Attr
+void explicit_noinline_first(void)
+    __attribute__((noinline, copy(always_inline_source)));
+// CHECK-LABEL: FunctionDecl {{.*}} explicit_noinline_last 'void (void)'
+// CHECK-NEXT: NoInlineAttr
+// CHECK-NOT: Attr
+void explicit_noinline_last(void)
+    __attribute__((copy(always_inline_source), noinline));
+
+// CHECK-LABEL: FunctionDecl {{.*}} explicit_always_inline_first 'void (void)'
+// CHECK-NEXT: AlwaysInlineAttr
+// CHECK-NOT: Attr
+void explicit_always_inline_first(void)
+    __attribute__((always_inline, copy(noinline_source)));
+// CHECK-LABEL: FunctionDecl {{.*}} explicit_always_inline_last 'void (void)'
+// CHECK-NEXT: AlwaysInlineAttr
+// CHECK-NOT: Attr
+void explicit_always_inline_last(void)
+    __attribute__((copy(noinline_source), always_inline));
diff --git a/clang/test/CodeGen/attr-copy.c b/clang/test/CodeGen/attr-copy.c
new file mode 100644
index 00000000000000..304b4cf1667996
--- /dev/null
+++ b/clang/test/CodeGen/attr-copy.c
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | 
FileCheck %s --implicit-check-not='@allocate(' --implicit-check-not='@stop('
+
+void *allocate(unsigned long) __attribute__((malloc, alloc_size(1), nothrow));
+void *copied(unsigned long) __attribute__((copy(allocate)));
+
+void *use(unsigned long n) {
+  return copied(n);
+}
+
+int aligned_source __attribute__((aligned(64)));
+// CHECK-DAG: @aligned_copy = {{.*}}global i32 0, align 64
+int aligned_copy __attribute__((copy(aligned_source)));
+
+// CHECK-DAG: @strong_alias = {{.*}}alias ptr (i64), ptr @target
+void *target(unsigned long) __attribute__((malloc, alloc_size(1), nothrow));
+void *target(unsigned long n) { return (void *)0; }
+extern __typeof__(target) strong_alias
+    __attribute__((alias("target"), copy(target)));
+
+// CHECK: declare noalias ptr @copied(i64 noundef) #[[ALLOC:[0-9]+]]
+
+void stop(void) __attribute__((noreturn));
+void copied_stop(void) __attribute__((copy(stop)));
+void use_stop(void) {
+  copied_stop();
+}
+// CHECK: declare void @copied_stop() #[[STOP:[0-9]+]]
+
+void *use_alias(unsigned long n) {
+  return strong_alias(n);
+}
+// CHECK: call noalias ptr @strong_alias(i64 noundef
+// CHECK: attributes #[[ALLOC]] = { nounwind allocsize(0)
+// CHECK: attributes #[[STOP]] = { noreturn
diff --git a/clang/test/CodeGenCXX/attr-copy-calling-conv.cpp 
b/clang/test/CodeGenCXX/attr-copy-calling-conv.cpp
new file mode 100644
index 00000000000000..9ef8200c91aadc
--- /dev/null
+++ b/clang/test/CodeGenCXX/attr-copy-calling-conv.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 -emit-llvm -o - 
%s | FileCheck %s
+
+using Callback = int (__attribute__((ms_abi)) *)(int);
+Callback callback = [](int x) __attribute__((copy((Callback)nullptr))) { 
return x; };
+
+// CHECK: define internal win64cc noundef i32 {{.*}}__invoke{{.*}}(i32 noundef
+// CHECK: call win64cc noundef i32 {{.*}}clEi
+// CHECK: define internal win64cc noundef i32 {{.*}}clEi
diff --git a/clang/test/PCH/attr-copy.cpp b/clang/test/PCH/attr-copy.cpp
new file mode 100644
index 00000000000000..838c6d8d44b5ed
--- /dev/null
+++ b/clang/test/PCH/attr-copy.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -include %s 
-fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -emit-pch -o %t 
%s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -include-pch %t 
-fsyntax-only -verify %s
+
+#ifndef HEADER
+#define HEADER
+
+void source(int *) __attribute__((nonnull(1)));
+void copied(int *) __attribute__((copy(source)));
+template <void (*F)(int *)> struct FunctionCopy {
+  static void function(int *) __attribute__((copy(F)));
+};
+using Callback = int (__attribute__((ms_abi)) *)(int);
+template <typename T> Callback make_callback() {
+  return [](int x) __attribute__((copy((Callback)nullptr))) { return x; };
+}
+
+#else
+
+void copied_from_pch(int *) __attribute__((copy(source)));
+Callback lambda_from_pch =
+    [](int x) __attribute__((copy((Callback)nullptr))) { return x; };
+Callback instantiated_from_pch = make_callback<int>();
+
+void use() {
+  copied(nullptr); // expected-warning {{null passed to a callee that requires 
a non-null argument}}
+  copied_from_pch(nullptr); // expected-warning {{null passed to a callee that 
requires a non-null argument}}
+  FunctionCopy<source>::function(nullptr); // expected-warning {{null passed 
to a callee that requires a non-null argument}}
+}
+
+#endif
diff --git a/clang/test/Sema/attr-copy-calling-conv.c 
b/clang/test/Sema/attr-copy-calling-conv.c
new file mode 100644
index 00000000000000..977bddf43d44e3
--- /dev/null
+++ b/clang/test/Sema/attr-copy-calling-conv.c
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -triple i386-unknown-linux-gnu -fsyntax-only -verify %s
+
+typedef int __attribute__((stdcall)) Callback(int);
+int copied(int) __attribute__((copy((Callback *)0)));
+_Static_assert(__builtin_types_compatible_p(__typeof__(copied), Callback),
+               "copied calling convention");
+
+typedef int __attribute__((regparm(2))) Registers(int, int);
+int registers(int, int) __attribute__((copy((Registers *)0)));
+_Static_assert(__builtin_types_compatible_p(__typeof__(registers), Registers),
+               "copied register arguments");
+
+typedef int Plain(int);
+typedef Plain CopiedType __attribute__((copy((Callback *)0)));
+_Static_assert(__builtin_types_compatible_p(CopiedType, Callback),
+               "calling convention on a typedef");
+
+Callback *pointer_source;
+int (*pointer_copy)(int) __attribute__((copy(pointer_source)));
+_Static_assert(__builtin_types_compatible_p(__typeof__(pointer_copy), Callback 
*),
+               "calling convention on a function pointer");
+
+int __attribute__((fastcall, copy((Registers *)0))) conflict(int, int); // 
expected-error {{fastcall and regparm attributes are not compatible}}
+int variadic(int, ...) __attribute__((copy((Callback *)0))); // 
expected-warning {{stdcall calling convention is not supported on variadic 
function}}
diff --git a/clang/test/Sema/attr-copy.c b/clang/test/Sema/attr-copy.c
new file mode 100644
index 00000000000000..b6599041bcf10e
--- /dev/null
+++ b/clang/test/Sema/attr-copy.c
@@ -0,0 +1,97 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=gnu11 -fsyntax-only 
-verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -fsyntax-only 
-verify %s
+
+#if !__has_attribute(copy) || !__has_attribute(__copy__)
+#error copy is not supported
+#endif
+#if __STDC_VERSION__ >= 202311L
+#if !__has_c_attribute(gnu::copy)
+#error gnu::copy is not supported
+#endif
+#endif
+
+void source(int *, int *) __attribute__((nonnull(2)));
+void copied(int *, int *) __attribute__((copy(source)));
+void address(int *, int *) __attribute__((__copy__(&source)));
+void chained(int *, int *) __attribute__((copy(copied)));
+
+void use(void) {
+  copied((void *)0, (void *)0); // expected-warning {{null passed to a callee 
that requires a non-null argument}}
+  address((void *)0, (void *)0); // expected-warning {{null passed to a callee 
that requires a non-null argument}}
+  chained((void *)0, (void *)0); // expected-warning {{null passed to a callee 
that requires a non-null argument}}
+}
+
+// The destination must still satisfy the copied attribute's requirements.
+void bad_index(int *) __attribute__((copy(source))); // expected-error 
{{'nonnull' attribute parameter 1 is out of bounds}}
+void bad_type(int, int) __attribute__((copy(source))); // expected-warning 
{{'nonnull' attribute only applies to pointer arguments}} expected-warning 
{{'nonnull' attribute applied to function with no pointer arguments}}
+void *allocator(int) __attribute__((alloc_size(1)));
+void *bad_alloc(void *) __attribute__((copy(allocator))); // expected-error 
{{'alloc_size' attribute argument may only refer to a function parameter of 
integer type}}
+
+int print_source(const char *, ...) __attribute__((format(printf, 1, 2)));
+int print_copy(const char *, ...) __attribute__((copy(print_source)));
+void use_format(void) {
+  print_copy("%d", "wrong"); // expected-warning {{format specifies type 'int' 
but the argument has type 'char *'}}
+}
+
+void sentinel_source(int, ...) __attribute__((sentinel));
+void sentinel_copy(int, ...) __attribute__((copy(sentinel_source))); // 
expected-note {{function has been explicitly marked sentinel here}}
+void use_sentinel(void) {
+  sentinel_copy(0, 1); // expected-warning {{missing sentinel in function 
call}}
+}
+
+void cold_source(void) __attribute__((cold));
+// Excluding inline attributes from copy must not relax explicit conflicts.
+void explicit_conflict(void) __attribute__((noinline, always_inline)); // 
expected-error {{'always_inline' and 'noinline' attributes are not compatible}} 
expected-note {{conflicting attribute is here}}
+void conflict(void) __attribute__((hot, copy(cold_source))); // expected-error 
{{'cold' and 'hot' attributes are not compatible}} expected-note {{conflicting 
attribute is here}}
+static void weak_alias(void)
+    __attribute__((weakref, copy(cold_source), alias("cold_source")));
+
+int variable __attribute__((aligned(32)));
+int variable_copy __attribute__((copy(variable)));
+int variable_address __attribute__((copy(&variable)));
+_Static_assert(__alignof__(variable_copy) == 32, "variable alignment");
+_Static_assert(__alignof__(variable_address) == 32, "variable address");
+
+void shadow(void) {
+  int local __attribute__((aligned(32)));
+  {
+    int local __attribute__((copy(local)));
+    _Static_assert(__alignof__(local) == 32, "copy from an enclosing scope");
+  }
+}
+
+struct __attribute__((packed, aligned(16))) A { char c; int i; };
+struct __attribute__((copy((struct A *)0))) B { char c; int i; };
+_Static_assert(_Alignof(struct B) == 16, "type alignment");
+_Static_assert(__builtin_offsetof(struct B, i) == 1, "packed layout");
+
+typedef int aligned_int __attribute__((aligned(32)));
+typedef int copied_int __attribute__((copy((aligned_int *)0)));
+_Static_assert(_Alignof(copied_int) == 32, "typedef alignment");
+
+// Expressions whose types have no attributes are valid, and are unevaluated.
+int no_attributes __attribute__((copy((int *)0)));
+int unevaluated __attribute__((copy(variable++)));
+
+void (*pointer_source)(void) __attribute__((aligned(32)));
+void (*pointer_copy)(void) __attribute__((copy(pointer_source)));
+_Static_assert(__alignof__(pointer_copy) == 32, "function pointer variable");
+void pointer_kind(void) __attribute__((copy(*pointer_source))); // 
expected-warning {{'copy' attribute ignored on a declaration of a different 
kind than its argument}}
+
+int no_argument __attribute__((copy)); // expected-error {{'copy' attribute 
takes one argument}}
+int two_arguments __attribute__((copy(variable, variable))); // expected-error 
{{'copy' attribute takes one argument}}
+int integer __attribute__((copy(1))); // expected-error {{'copy' attribute 
requires an expression referring to a function, variable, or type}}
+int string __attribute__((copy("variable"))); // expected-error {{'copy' 
attribute requires an expression referring to a function, variable, or type}}
+int missing __attribute__((copy(undeclared))); // expected-error {{use of 
undeclared identifier 'undeclared'}}
+int wrong_kind __attribute__((copy(source))); // expected-warning {{'copy' 
attribute ignored on a declaration of a different kind than its argument}}
+void wrong_function(void) __attribute__((copy(variable))); // expected-warning 
{{'copy' attribute ignored on a declaration of a different kind than its 
argument}}
+
+extern int self;
+extern int self __attribute__((copy(self))); // expected-warning {{'copy' 
attribute ignored on a declaration referring to itself}}
+
+#if __STDC_VERSION__ >= 202311L
+[[gnu::copy(source)]] void standard_copy(int *, int *);
+void use_standard(void) {
+  standard_copy((void *)0, (void *)0); // expected-warning {{null passed to a 
callee that requires a non-null argument}}
+}
+#endif
diff --git a/clang/test/SemaCXX/attr-copy-calling-conv.cpp 
b/clang/test/SemaCXX/attr-copy-calling-conv.cpp
new file mode 100644
index 00000000000000..65aebff50d3fd1
--- /dev/null
+++ b/clang/test/SemaCXX/attr-copy-calling-conv.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 -fsyntax-only 
-verify %s
+
+using Callback = int (__attribute__((ms_abi)) *)(int);
+
+// An ordinary lambda uses the target's default calling convention.
+Callback bad = [](int x) { return x; }; // expected-error {{no viable 
conversion}} expected-note {{candidate function}}
+
+// copy must change both the call operator and the function pointer conversion.
+Callback good = [](int x) __attribute__((copy((Callback)nullptr))) { return x; 
};
+Callback standard = [] [[gnu::copy((Callback)nullptr)]](int x) { return x; };
+Callback generic = [](auto x) __attribute__((copy((Callback)nullptr))) { 
return x; };
+
+int __attribute__((ms_abi)) function(int);
+Callback from_function = [](int x) __attribute__((copy(function))) { return x; 
};
+
+// Explicit, incompatible calling conventions are still diagnosed.
+int __attribute__((sysv_abi, copy(function))) conflict(int); // expected-error 
{{ms_abi and cdecl attributes are not compatible}}
+
+template <typename T> Callback make_callback() {
+  return [](int x) __attribute__((copy((Callback)nullptr))) { return x; };
+}
+Callback instantiated = make_callback<int>();
+
+template <typename T> T make_dependent_callback() {
+  return [](int x) __attribute__((copy((T)nullptr))) { return x; };
+}
+Callback dependent = make_dependent_callback<Callback>();
+
+template <int N> __attribute__((copy(function))) int templated(int x) { return 
x; }
+Callback function_template = templated<0>;
diff --git a/clang/test/SemaTemplate/attr-copy.cpp 
b/clang/test/SemaTemplate/attr-copy.cpp
new file mode 100644
index 00000000000000..b627653b8e3597
--- /dev/null
+++ b/clang/test/SemaTemplate/attr-copy.cpp
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+#if !__has_cpp_attribute(gnu::copy)
+#error gnu::copy is not supported
+#endif
+
+[[gnu::nonnull(1)]] void source(int *);
+[[gnu::copy(source)]] void copied(int *);
+void use() {
+  copied(nullptr); // expected-warning {{null passed to a callee that requires 
a non-null argument}}
+}
+
+void nothrow_source() __attribute__((nothrow));
+void nothrow_copy() __attribute__((copy(nothrow_source)));
+static_assert(noexcept(nothrow_copy()), "copied nothrow");
+
+namespace overloads {
+void source(int *) __attribute__((nonnull(1)));
+void source(double *);
+void copied(int *) __attribute__((copy(static_cast<void (*)(int *)>(source))));
+void use() {
+  copied(nullptr); // expected-warning {{null passed to a callee that requires 
a non-null argument}}
+}
+}
+
+template <typename T> struct Source {
+  static int value __attribute__((aligned(32)));
+  static void function(int *) __attribute__((nonnull(1)));
+};
+
+template <typename T> struct Destination {
+  int value __attribute__((copy(Source<T>::value)));
+  static void function(int *) __attribute__((copy(Source<T>::function)));
+};
+
+static_assert(alignof(Destination<int>) == 32, "dependent variable");
+void use_template() {
+  Destination<int>::function(nullptr); // expected-warning {{null passed to a 
callee that requires a non-null argument}}
+}
+
+struct __attribute__((aligned(64))) Aligned {};
+template <typename T> struct __attribute__((copy((T *)0))) TypeCopy {};
+static_assert(alignof(TypeCopy<Aligned>) == 64, "dependent type");
+
+template <typename T> struct __attribute__((aligned(sizeof(T) * 32))) 
AlignedTemplate {};
+struct __attribute__((copy((AlignedTemplate<int> *)0))) SpecializationCopy {};
+static_assert(alignof(SpecializationCopy) == sizeof(int) * 32, "source 
specialization");
+
+template <typename T> struct Bad {
+  static void function() __attribute__((copy(Source<T>::function))); // 
expected-error {{'nonnull' attribute parameter 1 is out of bounds}}
+};
+Bad<int> bad; // expected-note {{in instantiation of template class 'Bad<int>' 
requested here}}
+
+template <void (*F)(int *)> struct FunctionCopy {
+  static void function(int *) __attribute__((copy(F)));
+};
+void use_nttp() {
+  FunctionCopy<source>::function(nullptr); // expected-warning {{null passed 
to a callee that requires a non-null argument}}
+}
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp 
b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 59aac4c507772b..ccc809170d1b03 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -5006,12 +5006,66 @@ static bool IsKnownToGCC(const Record &Attr) {
                 [](const FlattenedSpelling &S) { return S.knownToGCC(); });
 }
 
+// Reconstruct the arguments of GNU declaration attributes so that copy can run
+// the ordinary attribute handlers on the destination declaration. Cloning an
+// Attr would bypass subject, parameter-index, and mutual-exclusion checks.
+static void GenerateAttributeCopyArgs(const RecordKeeper &Records,
+                                      raw_ostream &OS) {
+  OS << "static bool getAttributeCopyArgs(ASTContext &Context, const Attr *At, 
"
+        "SourceLocation Loc, SmallVectorImpl<ArgsUnion> &Args) {\n"
+        "  switch (At->getKind()) {\n"
+        "  default: return false;\n";
+  for (const Record *R : Records.getAllDerivedDefinitions("Attr")) {
+    if (!R->getValueAsBit("ASTNode") || !R->getValueAsBit("SemaHandler") ||
+        R->isSubClassOf("TypeAttr") || !IsKnownToGCC(*R) ||
+        R->getName() == "Aligned")
+      continue;
+
+    OS << "  case attr::" << R->getName() << ": {\n";
+    auto Args = R->getValueAsListOfDefs("Args");
+    if (!Args.empty())
+      OS << "    const auto *A = cast<" << R->getName() << "Attr>(At);\n";
+    for (const Record *Arg : Args) {
+      if (Arg->getValueAsBit("Fake"))
+        continue;
+      auto A = createArgument(*Arg, R->getName());
+      std::string Value = "A->get" + A->getUpperName().str() + "()";
+      if (A->isVariadic()) {
+        OS << "    for (auto V : A->" << A->getLowerName() << "())\n";
+        Value = "V";
+      } else if (Arg->getValueAsBit("Optional")) {
+        if (Arg->isSubClassOf("ExprArgument") ||
+            Arg->isSubClassOf("IdentifierArgument"))
+          OS << "    if (" << Value << ")\n";
+        else if (Arg->isSubClassOf("ParamIdxArgument"))
+          OS << "    if (" << Value << ".isValid())\n";
+        else if (Arg->isSubClassOf("StringArgument"))
+          OS << "    if (!" << Value << ".empty())\n";
+      }
+      if (A->isEnumArg() || A->isVariadicEnumArg()) {
+        StringRef Type = Arg->getValueAsString("Type");
+        Type = Type.substr(Type.rfind(':') + 1);
+        Value = R->getName().str() + "Attr::Convert" + Type.str() + "ToStr(" +
+                Value + ")";
+        if (Arg->getValueAsBit("IsString"))
+          Value = "StringRef(" + Value + ")";
+        else
+          Value = "&Context.Idents.get(" + Value + ")";
+      }
+      OS << "      Args.push_back(createCopyAttrArgument(Context, Loc, "
+         << Value << "));\n";
+    }
+    OS << "    return true;\n  }\n";
+  }
+  OS << "  }\n}\n\n";
+}
+
 /// Emits the parsed attribute helpers
 void EmitClangAttrParsedAttrImpl(const RecordKeeper &Records, raw_ostream &OS) 
{
   emitSourceFileHeader("Parsed attribute helpers", OS, Records);
 
   OS << "#if !defined(WANT_DECL_MERGE_LOGIC) && "
-     << "!defined(WANT_STMT_MERGE_LOGIC)\n";
+     << "!defined(WANT_STMT_MERGE_LOGIC) && !defined(WANT_DECL_COPY_LOGIC)\n";
   PragmaClangAttributeSupport &PragmaAttributeSupport =
       getPragmaAttributeSupport(Records);
 
@@ -5146,6 +5200,9 @@ void EmitClangAttrParsedAttrImpl(const RecordKeeper 
&Records, raw_ostream &OS) {
   // Generate the attribute match rules.
   emitAttributeMatchRules(PragmaAttributeSupport, OS);
 
+  OS << "#elif defined(WANT_DECL_COPY_LOGIC)\n\n";
+  GenerateAttributeCopyArgs(Records, OS);
+
   OS << "#elif defined(WANT_DECL_MERGE_LOGIC)\n\n";
 
   // Write out the declaration merging check logic.

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

Reply via email to