[clang] [clang] Add add attribute [[clang::builtin]] (PR #78071)

Nikolas Klauser via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 13 14:54:40 PST 2024


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/78071

None

>From f073a827ddbe728ced53a82431f517da58753776 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 11 Dec 2023 12:51:07 +0100
Subject: [PATCH] [clang] Add add attribute [[clang::builtin]]

---
 clang/include/clang/Basic/Attr.td             |  5 +-
 clang/include/clang/Basic/DiagnosticGroups.td |  3 +
 .../clang/Basic/DiagnosticSemaKinds.td        | 11 +++
 clang/lib/AST/Decl.cpp                        |  6 ++
 clang/lib/AST/ExprConstant.cpp                | 15 ++-
 clang/lib/CodeGen/CGExpr.cpp                  |  6 +-
 clang/lib/Headers/mmintrin.h                  |  9 +-
 clang/lib/Sema/SemaDecl.cpp                   |  4 +-
 clang/lib/Sema/SemaDeclAttr.cpp               | 96 +++++++++++++++++++
 clang/lib/Sema/SemaExpr.cpp                   |  3 +
 clang/test/CodeGenCXX/attr-builtin.cpp        | 71 ++++++++++++++
 ...a-attribute-supported-attributes-list.test |  1 +
 clang/test/SemaCXX/attr-builtin-debug.cpp     |  4 +
 clang/test/SemaCXX/attr-builtin.cpp           | 65 +++++++++++++
 14 files changed, 285 insertions(+), 14 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/attr-builtin.cpp
 create mode 100644 clang/test/SemaCXX/attr-builtin-debug.cpp
 create mode 100644 clang/test/SemaCXX/attr-builtin.cpp

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index b0a8ef10c500a7..fb435eac80bdd8 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4157,11 +4157,10 @@ def DiagnoseAsBuiltin : InheritableAttr {
 }
 
 def Builtin : InheritableAttr {
-  let Spellings = [];
+  let Spellings = [Clang<"builtin">];
   let Args = [UnsignedArgument<"ID">];
   let Subjects = SubjectList<[Function]>;
-  let SemaHandler = 0;
-  let Documentation = [InternalOnly];
+  let Documentation = [Undocumented];
 }
 
 def EnforceTCB : InheritableAttr {
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index caee2dc6daadb6..2390a487b0bcb8 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -771,6 +771,8 @@ def NSobjectAttribute : DiagGroup<"NSObject-attribute">;
 def NSConsumedMismatch : DiagGroup<"nsconsumed-mismatch">;
 def NSReturnsMismatch : DiagGroup<"nsreturns-mismatch">;
 
+def UnsupportedBuiltin : DiagGroup<"unsupported-builtin">;
+
 def IndependentClassAttribute : DiagGroup<"IndependentClass-attribute">;
 def UnknownAttributes : DiagGroup<"unknown-attributes">;
 def IgnoredAttributes : DiagGroup<"ignored-attributes">;
@@ -1063,6 +1065,7 @@ def Most : DiagGroup<"most", [
     PrivateExtern,
     SelTypeCast,
     ExternCCompat,
+    UnsupportedBuiltin,
     UserDefinedWarnings
  ]>;
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 28d95ca9b13893..3c096c07bd4708 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5759,6 +5759,17 @@ def warn_unavailable_def : Warning<
 def warn_deprecated_builtin : Warning<
   "builtin %0 is deprecated; use %1 instead">,
   InGroup<DeprecatedBuiltins>;
+def err_unknown_builtin : Error<"unknown builtin">;
+def warn_unsupported_builtin : Warning<"builtin is not supported">,
+  InGroup<UnsupportedBuiltin>;
+def warn_unsupported_on_member_function : Warning<
+  "attribute 'builtin' is not supported on member functions">,
+  InGroup<UnsupportedBuiltin>;
+def err_unexpected_param_count : Error<
+  "expected %0 argument%select{s|}1 but got %2">;
+def err_signature_mismatch : Error<
+  "function signature does not match the signature of the builtin">;
+def note_expected_signature : Note<"expected signature is %0">;
 def err_unavailable : Error<"%0 is unavailable">;
 def err_property_method_unavailable :
     Error<"property access is using %0 method which is unavailable">;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index c5c2edf1bfe3ab..63be2447cca308 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3575,6 +3575,12 @@ unsigned FunctionDecl::getBuiltinID(bool ConsiderWrapperFunctions) const {
     BuiltinID = BAA->getBuiltinName()->getBuiltinID();
   } else if (const auto *A = getAttr<BuiltinAttr>()) {
     BuiltinID = A->getID();
+
+    // This is an explicit attribute, which means that this has been declared as
+    // a builtin by the user. In this case we can assume that the function is
+    // equivalent to the specified builtin.
+    if (!A->isImplicit())
+      return BuiltinID;
   }
 
   if (!BuiltinID)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f035c1419f4c98..52b45ba0385b88 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -7661,8 +7661,19 @@ class ExprEvaluatorBase
 
   bool IsConstantEvaluatedBuiltinCall(const CallExpr *E) {
     unsigned BuiltinOp = E->getBuiltinCallee();
-    return BuiltinOp != 0 &&
-           Info.Ctx.BuiltinInfo.isConstantEvaluated(BuiltinOp);
+
+    if (BuiltinOp == 0 || !Info.Ctx.BuiltinInfo.isConstantEvaluated(BuiltinOp))
+      return false;
+
+    if (const auto* CD = E->getCalleeDecl()) {
+      if (const auto* FD = CD->getAsFunction()) {
+        if (FD->isConstexpr())
+          return true;
+        if (const auto* BA = FD->getAttr<BuiltinAttr>())
+          return BA->isImplicit();
+      }
+    }
+    return true;
   }
 
 public:
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 69cf7f76be9a70..d514bf238a0096 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5183,11 +5183,12 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
 
   // A CXXOperatorCallExpr is created even for explicit object methods, but
   // these should be treated like static function call.
-  if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+  if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E)) {
     if (const auto *MD =
             dyn_cast_if_present<CXXMethodDecl>(CE->getCalleeDecl());
         MD && MD->isImplicitObjectMemberFunction())
       return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue);
+  }
 
   CGCallee callee = EmitCallee(E->getCallee());
 
@@ -5224,7 +5225,8 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
   const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
 
   if (auto builtinID = FD->getBuiltinID()) {
-    std::string NoBuiltinFD = ("no-builtin-" + FD->getName()).str();
+    std::string NoBuiltinFD =
+        ("no-builtin-" + CGF.getContext().BuiltinInfo.getName(builtinID)).str();
     std::string NoBuiltins = "no-builtins";
 
     StringRef Ident = CGF.CGM.getMangledName(GD);
diff --git a/clang/lib/Headers/mmintrin.h b/clang/lib/Headers/mmintrin.h
index 08849f01071aea..6f0b595a320482 100644
--- a/clang/lib/Headers/mmintrin.h
+++ b/clang/lib/Headers/mmintrin.h
@@ -33,11 +33,9 @@ typedef char __v8qi __attribute__((__vector_size__(8)));
 ///
 /// This intrinsic corresponds to the <c> EMMS </c> instruction.
 ///
-static __inline__ void __attribute__((__always_inline__, __nodebug__,
-                                      __target__("mmx,no-evex512")))
-_mm_empty(void) {
-  __builtin_ia32_emms();
-}
+static __inline__ void
+    __attribute__((__always_inline__, __nodebug__, __target__("mmx,no-evex512"),
+                   __builtin__("__builtin_ia32_emms"))) _mm_empty(void);
 
 /// Constructs a 64-bit integer vector, setting the lower 32 bits to the
 ///    value of the 32-bit integer parameter and setting the upper 32 bits to 0.
@@ -1561,4 +1559,3 @@ _mm_setr_pi8(char __b0, char __b1, char __b2, char __b3, char __b4, char __b5,
 #define _m_pcmpgtd _mm_cmpgt_pi32
 
 #endif /* __MMINTRIN_H */
-
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 19d972ed8ab2d8..ace3f664eae872 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15622,7 +15622,9 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
   // Builtin functions cannot be defined.
   if (unsigned BuiltinID = FD->getBuiltinID()) {
     if (!Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID) &&
-        !Context.BuiltinInfo.isPredefinedRuntimeFunction(BuiltinID)) {
+        !Context.BuiltinInfo.isPredefinedRuntimeFunction(BuiltinID) &&
+        FD->getAttr<BuiltinAttr>() &&
+        FD->getAttr<BuiltinAttr>()->isImplicit()) {
       Diag(FD->getLocation(), diag::err_builtin_definition) << FD;
       FD->setInvalidDecl();
     }
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 59e456fd9f7298..3df09d836513b5 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5921,6 +5921,98 @@ static void handleBuiltinAliasAttr(Sema &S, Decl *D,
   D->addAttr(::new (S.Context) BuiltinAliasAttr(S.Context, AL, Ident));
 }
 
+static void handleBuiltinAttr(Sema &S, Decl *D,
+                                          const ParsedAttr &AL) {
+  if (!AL.checkExactlyNumArgs(S, 1))
+    return;
+
+  StringRef BuiltinName;
+  if (!S.checkStringLiteralArgumentAttr(AL, 0, BuiltinName))
+    return;
+
+  bool IsInStdNamespace = BuiltinName.consume_front("std::");
+
+  unsigned ID =
+      S.getPreprocessor().getIdentifierTable().get(BuiltinName).getBuiltinID();
+
+  auto& BuiltinInfo = S.Context.BuiltinInfo;
+
+  if (ID == 0 || BuiltinInfo.isInStdNamespace(ID) != IsInStdNamespace) {
+    S.Diag(AL.getLoc(), diag::err_unknown_builtin);
+    return;
+  }
+
+  if (BuiltinInfo.isUnevaluated(ID) || BuiltinInfo.hasCustomTypechecking(ID)) {
+    S.Diag(AL.getLoc(), diag::warn_unsupported_builtin);
+    return;
+  }
+
+  ASTContext::GetBuiltinTypeError Error;
+  QualType Signature = S.Context.GetBuiltinType(ID, Error);
+  if (Error || Signature.isNull()) {
+    S.Diag(AL.getLoc(), diag::warn_unsupported_builtin);
+    return;
+  }
+
+  auto hasCompatibleSignature = [&] {
+    auto isTypeCompatible = [&](QualType LHS, QualType RHS) {
+      if (S.Context.hasSameType(LHS, RHS))
+        return true;
+      if (LHS->isPointerType() && RHS->isPointerType()) {
+        auto LHSPointee = LHS->getPointeeType();
+        auto RHSPointee = RHS->getPointeeType();
+        if (LHSPointee->isVoidType())
+          return RHSPointee.isAtLeastAsQualifiedAs(LHSPointee);
+      }
+      return false;
+    };
+
+    if (!isTypeCompatible(
+            cast<FunctionProtoType>(Signature)->getReturnType(),
+            cast<FunctionProtoType>(D->getFunctionType())->getReturnType()))
+      return false;
+
+    auto LHSParams = cast<FunctionProtoType>(Signature)->getParamTypes();
+    auto RHSParams =
+        cast<FunctionProtoType>(D->getFunctionType())->getParamTypes();
+
+    if (LHSParams.size() != RHSParams.size())
+      return false;
+
+    return llvm::all_of(llvm::zip(LHSParams, RHSParams), [&](auto tuple) {
+      return isTypeCompatible(std::get<0>(tuple), std::get<1>(tuple));
+    });
+  };
+
+  if (const auto* MD = dyn_cast<CXXMethodDecl>(D)) {
+    if (!MD->isStatic()) {
+      S.Diag(AL.getLoc(), diag::warn_unsupported_on_member_function);
+      return;
+    }
+  }
+
+  if (BuiltinInfo.allowTypeMismatch(ID)) {
+    auto ExpectedParamCount =
+        cast<FunctionProtoType>(Signature)->getNumParams();
+    auto ActualParamCount =
+        cast<FunctionProtoType>(D->getFunctionType())->getNumParams();
+    if (ExpectedParamCount != ActualParamCount) {
+      S.Diag(AL.getLoc(), diag::err_unexpected_param_count)
+          << ExpectedParamCount << (ExpectedParamCount == 1)
+          << ActualParamCount;
+      return;
+    }
+  } else if (QualType FuncSig = cast<FunctionDecl>(D)->getType();
+             !S.Context.hasSameFunctionTypeIgnoringExceptionSpec(Signature,
+                                                                 FuncSig) &&
+             !hasCompatibleSignature()) {
+    S.Diag(AL.getLoc(), diag::err_signature_mismatch);
+    S.Diag(AL.getLoc(), diag::note_expected_signature) << Signature;
+  }
+
+  D->addAttr(BuiltinAttr::Create(S.Context, ID));
+}
+
 static void handlePreferredTypeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   if (!AL.hasParsedType()) {
     S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
@@ -9717,6 +9809,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
     handleBuiltinAliasAttr(S, D, AL);
     break;
 
+  case ParsedAttr::AT_Builtin:
+    handleBuiltinAttr(S, D, AL);
+    break;
+
   case ParsedAttr::AT_PreferredType:
     handlePreferredTypeAttr(S, D, AL);
     break;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d629be083d8c38..e3ba57cfff6a7d 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7147,6 +7147,9 @@ static void DiagnosedUnqualifiedCallsToStdFunctions(Sema &S,
   if (BuiltinID != Builtin::BImove && BuiltinID != Builtin::BIforward)
     return;
 
+  if (!FD->isInStdNamespace())
+    return;
+
   S.Diag(DRE->getLocation(), diag::warn_unqualified_call_to_std_cast_function)
       << FD->getQualifiedNameAsString()
       << FixItHint::CreateInsertion(DRE->getLocation(), "std::");
diff --git a/clang/test/CodeGenCXX/attr-builtin.cpp b/clang/test/CodeGenCXX/attr-builtin.cpp
new file mode 100644
index 00000000000000..4da0354f43d3ff
--- /dev/null
+++ b/clang/test/CodeGenCXX/attr-builtin.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 %s -S -emit-llvm -triple x86_64-unknown-linux-gnu -o - -Wno-c++23-extensions | FileCheck %s
+
+[[clang::builtin("memcpy")]] void* my_memcpy(void*, const void*, unsigned long);
+
+void call_memcpy(int i) {
+  int j;
+  my_memcpy(&j, &i, sizeof(int));
+
+  // CHECK:      define dso_local void @_Z11call_memcpyi(i32 noundef %i) #0 {
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT:   %i.addr = alloca i32, align 4
+  // CHECK-NEXT:   %j = alloca i32, align 4
+  // CHECK-NEXT:   store i32 %i, ptr %i.addr, align 4
+  // CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %j, ptr align 4 %i.addr, i64 4, i1 false)
+  // CHECK-NEXT:   ret void
+  // CHECK-NEXT: }
+}
+
+[[clang::builtin("memmove")]] char* typed_memmove(char*, const char*, unsigned long);
+
+char* call_typed_memmove(char* dst, const char* src, unsigned long count) {
+  return typed_memmove(dst, src, count);
+
+  // CHECK:      define dso_local noundef ptr @_Z18call_typed_memmovePcPKcm(ptr noundef %dst, ptr noundef %src, i64 noundef %count) #0 {
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT:   %dst.addr = alloca ptr, align 8
+  // CHECK-NEXT:   %src.addr = alloca ptr, align 8
+  // CHECK-NEXT:   %count.addr = alloca i64, align 8
+  // CHECK-NEXT:   store ptr %dst, ptr %dst.addr, align 8
+  // CHECK-NEXT:   store ptr %src, ptr %src.addr, align 8
+  // CHECK-NEXT:   store i64 %count, ptr %count.addr, align 8
+  // CHECK-NEXT:   %0 = load ptr, ptr %dst.addr, align 8
+  // CHECK-NEXT:   %1 = load ptr, ptr %src.addr, align 8
+  // CHECK-NEXT:   %2 = load i64, ptr %count.addr, align 8
+  // CHECK-NEXT:   call void @llvm.memmove.p0.p0.i64(ptr align 1 %0, ptr align 1 %1, i64 %2, i1 false)
+  // CHECK-NEXT:   ret ptr %0
+  // CHECK-NEXT: }
+}
+
+template <class T>
+[[clang::builtin("std::move")]] __remove_reference_t(T)&& my_move(T&&);
+
+void call_move() {
+  int i = my_move(0);
+
+  // CHECK:      define dso_local void @_Z9call_movev() #0 {
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT:   %i = alloca i32, align 4
+  // CHECK-NEXT:   %ref.tmp = alloca i32, align 4
+  // CHECK-NEXT:   store i32 0, ptr %ref.tmp, align 4
+  // CHECK-NEXT:   %0 = load i32, ptr %ref.tmp, align 4
+  // CHECK-NEXT:   store i32 %0, ptr %i, align 4
+  // CHECK-NEXT:   ret void
+  // CHECK-NEXT: }
+}
+
+struct identity {
+  template <class T>
+  [[clang::builtin("std::forward")]] static T&& operator()(T&&) noexcept;
+};
+
+void call_identity() {
+  (void)identity()(1);
+
+  // CHECK:      define dso_local void @_Z13call_identityv() #0 {
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT:   %ref.tmp = alloca i32, align 4
+  // CHECK-NEXT:   store i32 1, ptr %ref.tmp, align 4
+  // CHECK-NEXT:   ret void
+  // CHECK-NEXT: }
+}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index bdfda430eea86c..96fb221e66eba3 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -25,6 +25,7 @@
 // CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record)
 // CHECK-NEXT: BPFPreserveStaticOffset (SubjectMatchRule_record)
 // CHECK-NEXT: BTFDeclTag (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record, SubjectMatchRule_field, SubjectMatchRule_type_alias)
+// CHECK-NEXT: Builtin (SubjectMatchRule_function)
 // CHECK-NEXT: BuiltinAlias (SubjectMatchRule_function)
 // CHECK-NEXT: CFAuditedTransfer (SubjectMatchRule_function)
 // CHECK-NEXT: CFConsumed (SubjectMatchRule_variable_is_parameter)
diff --git a/clang/test/SemaCXX/attr-builtin-debug.cpp b/clang/test/SemaCXX/attr-builtin-debug.cpp
new file mode 100644
index 00000000000000..c81e663137b0db
--- /dev/null
+++ b/clang/test/SemaCXX/attr-builtin-debug.cpp
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -ffreestanding -Wno-c++23-extensions
+
+[[clang::builtin("memcpy")]] void func(); // expected-error {{function signature does not match the signature of the builtin}} \
+                                             expected-note {{expected signature is 'void *(void *, const void *, unsigned long)'}}
diff --git a/clang/test/SemaCXX/attr-builtin.cpp b/clang/test/SemaCXX/attr-builtin.cpp
new file mode 100644
index 00000000000000..0fa72600737364
--- /dev/null
+++ b/clang/test/SemaCXX/attr-builtin.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-c++23-extensions
+// RUN: %clang_cc1 -fsyntax-only -verify %s -ffreestanding -Wno-c++23-extensions
+
+[[clang::builtin]] void func(); // expected-error {{'builtin' attribute takes one argument}}
+[[clang::builtin("unknown_builtin")]] void func(); // expected-warning {{builtin is not supported}}
+[[clang::builtin("memcpy")]] void func(); // expected-error {{function signature does not match the signature of the builtin}} \
+                                             expected-note {{expected signature is 'void *(void *, const void *, unsigned long)'}}
+[[clang::builtin("move")]] void func(); // expected-warning {{builtin is not supported}}
+
+// has unevaluated parameters
+[[clang::builtin("__builtin_constant_p")]] void constant_p(); // expected-warning {{builtin is not supported}}
+
+[[clang::builtin("memcpy")]] void* my_memcpy(void*, const void*, unsigned long);
+[[clang::builtin("memcpy")]] char* my_memcpy(char*, const char*, unsigned long);
+[[clang::builtin("memcpy")]] char* my_memcpy(char*, const char*); // expected-error {{function signature does not match the signature of the builtin}} \
+                                                                     expected-note {{expected signature is}}
+
+[[clang::builtin("memmove")]] char* typed_memmove(char*, const char*, unsigned long); // expected-note {{candidate function}}
+
+void call_memmove(void* ptr) {
+  typed_memmove(ptr, ptr, 1); // expected-error {{no matching function for call to 'typed_memmove'}}
+}
+
+[[clang::builtin("__builtin_memmove")]] void* non_constexpr_memmove(void*, const void*, unsigned long);
+
+constexpr void call_non_constexpr_memmove() { // expected-error {{constexpr function never produces a constant expression}}
+  int i = 0;
+  int j = 0;
+  non_constexpr_memmove(&j, &i, sizeof(int)); // expected-note {{subexpression not valid in a constant expression}}
+}
+
+[[clang::builtin("__builtin_memmove")]] constexpr void* constexpr_memmove(void*, const void*, unsigned long);
+
+constexpr void call_constexpr_memmove() {
+  int i = 0;
+  int j = 0;
+  constexpr_memmove(&j, &i, sizeof(int));
+}
+
+// allows type mismatches
+[[clang::builtin("std::move")]] void my_move(); // expected-error {{expected 1 argument but got 0}}
+[[clang::builtin("std::move")]] void my_move(int);
+
+void call_move() {
+  my_move(1); // expected-error {{unsupported signature for 'my_move'}}
+}
+
+// has custom type checking
+[[clang::builtin("__builtin_operator_new")]] void* my_operator_new(unsigned long); // expected-warning {{builtin is not supported}}
+
+// canonical types are compared
+using size_t = decltype(sizeof(int));
+[[clang::builtin("__builtin_memcmp")]] int my_memcmp(const char*, const char*, size_t);
+
+struct reject_on_member_functions {
+  template<class T>
+  [[clang::builtin("std::forward")]] T&& operator()(T&&) noexcept; // expected-warning {{attribute 'builtin' is not supported on member functions}}
+  [[clang::builtin("memchr")]] const void* memchr(const void*, int, unsigned long); // expected-warning {{attribute 'builtin' is not supported on member functions}}
+};
+
+struct accept_on_static_member_functions {
+  template <class T>
+  [[clang::builtin("std::forward")]] static T&& operator()(T&&) noexcept;
+  [[clang::builtin("memchr")]] static const void* memchr(const void*, int, unsigned long);
+};



More information about the cfe-commits mailing list