llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: LoveSy (yujincheng08)

<details>
<summary>Changes</summary>

Support copy attribute like GCC.

---

Patch is 42.98 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/229355.diff


17 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+4) 
- (modified) clang/include/clang/Basic/Attr.td (+10) 
- (modified) clang/include/clang/Basic/AttrDocs.td (+41) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+9) 
- (modified) clang/include/clang/Sema/Sema.h (+4) 
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+260) 
- (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+21) 
- (modified) clang/lib/Sema/SemaType.cpp (+48) 
- (added) clang/test/AST/attr-copy.c (+105) 
- (added) clang/test/CodeGen/attr-copy.c (+34) 
- (added) clang/test/CodeGenCXX/attr-copy-calling-conv.cpp (+8) 
- (added) clang/test/PCH/attr-copy.cpp (+31) 
- (added) clang/test/Sema/attr-copy-calling-conv.c (+24) 
- (added) clang/test/Sema/attr-copy.c (+97) 
- (added) clang/test/SemaCXX/attr-copy-calling-conv.cpp (+30) 
- (added) clang/test/SemaTemplate/attr-copy.cpp (+60) 
- (modified) clang/utils/TableGen/ClangAttrEmitter.cpp (+58-1) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7b878a0abbab3..1aed07bbcdfc1 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 4ef0bd2b5ca7c..a78c350d5d4e6 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 0ab1429ff09d6..11569376cdccb 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 9208aba1445d7..2616cda0b6fda 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 7fbcc4aae7d6c..e662471831630 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 eb4a8c2ab9ae0..8f882fd67ee66 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 5e72f05c863a0..77d7f33273369 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 e1481b437df9e..a70505549508f 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 0000000000000..725891fc3dc22
--- /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: Restrict...
[truncated]

``````````

</details>


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

Reply via email to