nickdesaulniers created this revision.
nickdesaulniers added reviewers: rsmith, aaron.ballman, craig.topper, efriedma, 
lebedev.ri, jdoerfert, arsenm.
Herald added subscribers: dexonsmith, hiraditya.
nickdesaulniers requested review of this revision.
Herald added subscribers: llvm-commits, cfe-commits, wdng.
Herald added projects: clang, LLVM.

Add support for the GNU C style __attribute__((error(""))) and
__attribute__((warning(""))). These attributes are meant to be put on
definitions of functions whom should not be called.

They are frequently used to provide compile time diagnostics similar to
_Static_assert, but which may rely on non-ICE conditions (ie. relying on
compiler optimizations). While users may instead simply call undefined
functions in such cases to get a linkage failure from the linker, these
provide a much more ergonomic and actionable diagnostic to users and do
so at compile time rather than at link time.

These are used throughout the Linux kernel in its implementation of
BUILD_BUG and BUILD_BUG_ON macros. These macros generally cannot be
converted to use _Static_assert because many of the parameters are not
ICEs. The Linux kernel still needs to be modified to make use of these
when building with Clang; I have a patch that does so I will send once
this feature is landed.

To do so, we create a new IR level Function attribute,
"user-diagnostic"="" which contains the string parameter from source
language function attributes (both error and warning boil down to one IR
Fn Attr). Then, similar to calls to inline asm, we attach a !srcloc
Metadata node to call sites of such attributed callees.

The backend diagnoses these before instruction selection, while we still
know that a call is a call (vs say a JMP that's a tail call) in an arch
agnostic manner.

The frontend then reconstructs the SourceLocation from that Metadata,
and determines whether to emit an error or warning based on the callee's
attribute.

Some things to iron out TODO:

- Is user-diagnostic the best identifier for the new fn attr? Maybe 
"diagnose-if-called"? I also use `UserDiagnostic` throughout much of this patch 
for C++ class names; surely there's something better?
- When is the best time for the backend to produce such diagnostics? If we wait 
until post-ISEL, then we need to diagnose instructions which may be JMPs, not 
just CALLs.

Link: https://bugs.llvm.org/show_bug.cgi?id=16428
Link: https://github.com/ClangBuiltLinux/linux/issues/1173


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D106030

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticFrontendKinds.td
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/CodeGen/CodeGenAction.cpp
  clang/lib/CodeGen/CodeGenModule.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/test/CodeGen/attr-error.c
  clang/test/CodeGen/attr-warning.c
  clang/test/Frontend/backend-attribute-error-warning-optimize.c
  clang/test/Frontend/backend-attribute-error-warning.c
  clang/test/Frontend/backend-attribute-error-warning.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/attr-error.c
  clang/test/Sema/attr-warning.c
  llvm/docs/LangRef.rst
  llvm/include/llvm/IR/DiagnosticInfo.h
  llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
  llvm/lib/IR/DiagnosticInfo.cpp
  llvm/test/CodeGen/Generic/attr-user-diagnostic.ll

Index: llvm/test/CodeGen/Generic/attr-user-diagnostic.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/Generic/attr-user-diagnostic.ll
@@ -0,0 +1,9 @@
+; RUN: not llc -stop-after=pre-isel-intrinsic-lowering %s 2>&1 | FileCheck %s
+
+declare void @foo() "user-diagnostic"="oh no"
+define void @bar() {
+  call void @foo()
+  ret void
+}
+
+; CHECK: error: call to foo: oh no
Index: llvm/lib/IR/DiagnosticInfo.cpp
===================================================================
--- llvm/lib/IR/DiagnosticInfo.cpp
+++ llvm/lib/IR/DiagnosticInfo.cpp
@@ -401,3 +401,7 @@
 
 void OptimizationRemarkAnalysisFPCommute::anchor() {}
 void OptimizationRemarkAnalysisAliasing::anchor() {}
+
+void DiagnosticInfoUser::print(DiagnosticPrinter &DP) const {
+  DP << "call to " << getFunctionName() << ": " << getMsgStr();
+}
Index: llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
===================================================================
--- llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
+++ llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
@@ -14,6 +14,7 @@
 #include "llvm/CodeGen/PreISelIntrinsicLowering.h"
 #include "llvm/Analysis/ObjCARCInstKind.h"
 #include "llvm/CodeGen/Passes.h"
+#include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Instructions.h"
@@ -204,6 +205,25 @@
       Changed |= lowerObjCCall(F, "objc_sync_exit");
       break;
     }
+    // TODO: is this the best pass for this?
+    for (BasicBlock &BB : F) {
+      for (Instruction &I : BB) {
+        if (CallBase *CB = dyn_cast<CallBase>(&I)) {
+          if (Function *Callee = CB->getCalledFunction()) {
+            if (Callee->hasFnAttribute("user-diagnostic")) {
+              unsigned LocCookie = 0;
+              if (MDNode *MD = CB->getMetadata("srcloc"))
+                LocCookie = mdconst::extract<ConstantInt>(MD->getOperand(0))
+                                ->getZExtValue();
+              DiagnosticInfoUser DU(
+                  Callee->getFnAttribute("user-diagnostic").getValueAsString(),
+                  Callee->getName(), LocCookie);
+              F.getContext().diagnose(DU);
+            }
+          }
+        }
+      }
+    }
   }
   return Changed;
 }
Index: llvm/include/llvm/IR/DiagnosticInfo.h
===================================================================
--- llvm/include/llvm/IR/DiagnosticInfo.h
+++ llvm/include/llvm/IR/DiagnosticInfo.h
@@ -79,6 +79,7 @@
   DK_PGOProfile,
   DK_Unsupported,
   DK_SrcMgr,
+  DK_UserDiagnostic,
   DK_FirstPluginKind // Must be last value to work with
                      // getNextAvailablePluginDiagnosticKind
 };
@@ -1070,6 +1071,25 @@
   }
 };
 
+class DiagnosticInfoUser : public DiagnosticInfo {
+  StringRef MsgStr;
+  StringRef CalleeName;
+  unsigned LocCookie;
+
+public:
+  DiagnosticInfoUser(StringRef MsgStr, StringRef CalleeName, unsigned LocCookie,
+                     DiagnosticSeverity Severity = DS_Error)
+      : DiagnosticInfo(DK_UserDiagnostic, Severity), MsgStr(MsgStr),
+        CalleeName(CalleeName), LocCookie(LocCookie) {}
+  StringRef getMsgStr() const { return MsgStr; }
+  StringRef getFunctionName() const { return CalleeName; }
+  unsigned getLocCookie() const { return LocCookie; }
+  void print(DiagnosticPrinter &DP) const override;
+  static bool classof(const DiagnosticInfo *DI) {
+    return DI->getKind() == DK_UserDiagnostic;
+  }
+};
+
 } // end namespace llvm
 
 #endif // LLVM_IR_DIAGNOSTICINFO_H
Index: llvm/docs/LangRef.rst
===================================================================
--- llvm/docs/LangRef.rst
+++ llvm/docs/LangRef.rst
@@ -2037,6 +2037,12 @@
     show that no exceptions passes by it. This is normally the case for
     the ELF x86-64 abi, but it can be disabled for some compilation
     units.
+``"user-diagnostic"="<diagnostic string>"``
+    This attribute provides a diagnostic string to emit when a call of a
+    function with this attribute is not eliminated via optimization. Front ends
+    can provide optional ``srcloc`` metadata nodes on call sites of such
+    callees to attach information about where in the source language such a
+    call came from.
 ``nocf_check``
     This attribute indicates that no control-flow check will be performed on
     the attributed entity. It disables -fcf-protection=<> for a specific
Index: clang/test/Sema/attr-warning.c
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-warning.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+#if !__has_attribute(warning)
+#warning "warning attribute missing"
+#endif
+
+__attribute__((warning("don't call me!"))) int foo(void);
+
+__attribute__((warning)) // expected-error {{'warning' attribute takes one argument}}
+int
+bad0(void);
+
+int bad1(__attribute__((warning("bad1"))) int param); // expected-warning {{'warning' attribute only applies to functions}}
+
+int bad2(void) {
+  __attribute__((warning("bad2"))); // expected-error {{'warning' attribute cannot be applied to a statement}}
+}
+
+__attribute__((warning(3))) // expected-error {{'warning' attribute requires a string}}
+int
+bad3(void);
Index: clang/test/Sema/attr-error.c
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-error.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+#if !__has_attribute(error)
+#error "error attribute missing"
+#endif
+
+__attribute__((error("don't call me!"))) int foo(void);
+
+__attribute__((error)) // expected-error {{'error' attribute takes one argument}}
+int
+bad0(void);
+
+int bad1(__attribute__((error("bad1"))) int param); // expected-error {{'error' attribute only applies to functions}}
+
+int bad2(void) {
+  __attribute__((error("bad2"))); // expected-error {{'error' attribute cannot be applied to a statement}}
+}
+
+__attribute__((error(3))) // expected-error {{'error' attribute requires a string}}
+int
+bad3(void);
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -61,6 +61,7 @@
 // CHECK-NEXT: EnforceTCB (SubjectMatchRule_function)
 // CHECK-NEXT: EnforceTCBLeaf (SubjectMatchRule_function)
 // CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
+// CHECK-NEXT: Error (SubjectMatchRule_function)
 // CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
 // CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
 // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
@@ -177,6 +178,7 @@
 // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function)
 // CHECK-NEXT: WarnUnused (SubjectMatchRule_record)
 // CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType)
+// CHECK-NEXT: Warning (SubjectMatchRule_function)
 // CHECK-NEXT: Weak (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
 // CHECK-NEXT: WeakRef (SubjectMatchRule_variable, SubjectMatchRule_function)
 // CHECK-NEXT: WebAssemblyExportName (SubjectMatchRule_function)
Index: clang/test/Frontend/backend-attribute-error-warning.cpp
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning.cpp
@@ -0,0 +1,29 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+  return 8 % 2 == 1;
+}
+
+__attribute__((warning("oh no quux"))) void quux(void);
+
+__attribute__((error("demangle me"))) void __compiletime_assert_455(void);
+
+template <typename T>
+__attribute__((error("demangle me, too")))
+T
+nocall(T t);
+
+void baz(void) {
+  // Test that we demangle correctly in the diagnostic for C++.
+  foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+  if (x())
+    bar();                    // expected-error {{call to bar declared with attribute error: oh no bar}}
+  quux();                     // expected-warning {{call to quux declared with attribute warning: oh no quux}}
+  __compiletime_assert_455(); // expected-error {{call to __compiletime_assert_455 declared with attribute error: demangle me}}
+  nocall<int>(42);            // expected-error {{call to nocall declared with attribute error: demangle me, too}}
+}
Index: clang/test/Frontend/backend-attribute-error-warning.c
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning.c
@@ -0,0 +1,22 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+  return 8 % 2 == 1;
+}
+
+__attribute__((warning("oh no quux"))) void quux(void);
+
+__attribute__((error("demangle me"))) void __compiletime_assert_455(void);
+
+void baz(void) {
+  foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+  if (x())
+    bar();                    // expected-error {{call to bar declared with attribute error: oh no bar}}
+  quux();                     // expected-warning {{call to quux declared with attribute warning: oh no quux}}
+  __compiletime_assert_455(); // expected-error {{call to __compiletime_assert_455 declared with attribute error: demangle me}}
+}
Index: clang/test/Frontend/backend-attribute-error-warning-optimize.c
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning-optimize.c
@@ -0,0 +1,15 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -O2 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+  return 8 % 2 == 1;
+}
+void baz(void) {
+  foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+  if (x())
+    bar();
+}
Index: clang/test/CodeGen/attr-warning.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/attr-warning.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+__attribute__((warning("oh no"))) void foo(void);
+
+void bar(void) {
+  foo();
+}
+
+// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]]
+// CHECK: declare void @foo() [[ATTR:#[0-9]+]]
+// CHECK: attributes [[ATTR]] = {{{.*}}"user-diagnostic"="oh no"
+// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}}
Index: clang/test/CodeGen/attr-error.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/attr-error.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+__attribute__((error("oh no"))) void foo(void);
+
+void bar(void) {
+  foo();
+}
+
+// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]]
+// CHECK: declare void @foo() [[ATTR:#[0-9]+]]
+// CHECK: attributes [[ATTR]] = {{{.*}}"user-diagnostic"="oh no"
+// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}}
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -946,6 +946,19 @@
     D->addAttr(::new (S.Context) EnableIfAttr(S.Context, AL, Cond, Msg));
 }
 
+static void handleErrorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  StringRef Str;
+  if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+    return;
+  D->addAttr(::new (S.Context) ErrorAttr(S.Context, AL, Str));
+}
+static void handleWarningAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  StringRef Str;
+  if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+    return;
+  D->addAttr(::new (S.Context) WarningAttr(S.Context, AL, Str));
+}
+
 namespace {
 /// Determines if a given Expr references any of the given function's
 /// ParmVarDecls, or the function's implicit `this` parameter (if applicable).
@@ -7843,6 +7856,9 @@
   case ParsedAttr::AT_EnableIf:
     handleEnableIfAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_Error:
+    handleErrorAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_DiagnoseIf:
     handleDiagnoseIfAttr(S, D, AL);
     break;
@@ -8058,6 +8074,9 @@
   case ParsedAttr::AT_TypeVisibility:
     handleVisibilityAttr(S, D, AL, true);
     break;
+  case ParsedAttr::AT_Warning:
+    handleWarningAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_WarnUnusedResult:
     handleWarnUnusedResult(S, D, AL);
     break;
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -2172,6 +2172,13 @@
                                                CalleeIdx, PayloadIndices,
                                                /* VarArgsArePassed */ false)}));
   }
+
+  // TODO: move to CodeGenModule::ConstructAttributeList() or
+  // CodeGenFunction::EmitCall() ???
+  if (const auto *EA = FD->getAttr<ErrorAttr>())
+    F->addFnAttr("user-diagnostic", EA->getUserDiagnostic());
+  if (const auto *WA = FD->getAttr<WarningAttr>())
+    F->addFnAttr("user-diagnostic", WA->getUserDiagnostic());
 }
 
 void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) {
Index: clang/lib/CodeGen/CodeGenAction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenAction.cpp
+++ clang/lib/CodeGen/CodeGenAction.cpp
@@ -401,6 +401,7 @@
         const llvm::OptimizationRemarkAnalysisAliasing &D);
     void OptimizationFailureHandler(
         const llvm::DiagnosticInfoOptimizationFailure &D);
+    void UserDiagnosticHandler(const DiagnosticInfoUser &D);
   };
 
   void BackendConsumer::anchor() {}
@@ -758,6 +759,32 @@
   EmitOptimizationMessage(D, diag::warn_fe_backend_optimization_failure);
 }
 
+void BackendConsumer::UserDiagnosticHandler(const DiagnosticInfoUser &D) {
+  unsigned DiagID = diag::err_fe_backend_user_diagnostic;
+  llvm::StringRef CalleeName = D.getFunctionName();
+  SourceLocation LocCookie =
+      SourceLocation::getFromRawEncoding(D.getLocCookie());
+
+  if (const Decl *D = Gen->GetDeclForMangledName(CalleeName)) {
+    if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+      assert((FD->hasAttr<ErrorAttr>() || FD->hasAttr<WarningAttr>()) &&
+             "expected error or warning function attribute");
+
+      if (FD->hasAttr<WarningAttr>()) {
+        llvm::errs() << "should be a warning diag\n";
+        DiagID = diag::warn_fe_backend_user_diagnostic;
+      }
+
+      CalleeName = FD->getName();
+    }
+  }
+
+  if (LocCookie.isValid())
+    Diags.Report(LocCookie, DiagID) << CalleeName << D.getMsgStr();
+  else
+    Diags.Report(DiagID) << CalleeName << D.getMsgStr();
+}
+
 /// This function is invoked when the backend needs
 /// to report something to the user.
 void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
@@ -833,6 +860,9 @@
   case llvm::DK_Unsupported:
     UnsupportedDiagHandler(cast<DiagnosticInfoUnsupported>(DI));
     return;
+  case llvm::DK_UserDiagnostic:
+    UserDiagnosticHandler(cast<DiagnosticInfoUser>(DI));
+    return;
   default:
     // Plugin IDs are not bound to any value as they are set dynamically.
     ComputeDiagRemarkID(Severity, backend_plugin, DiagID);
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -5304,6 +5304,17 @@
       TargetDecl->hasAttr<MSAllocatorAttr>())
     getDebugInfo()->addHeapAllocSiteMetadata(CI, RetTy->getPointeeType(), Loc);
 
+  // Add metadata if calling an __attribute__((error(""))) or warning fn.
+  if (TargetDecl)
+    if (TargetDecl->hasAttr<ErrorAttr>() ||
+        TargetDecl->hasAttr<WarningAttr>()) {
+      llvm::ConstantInt *Line =
+          llvm::ConstantInt::get(Int32Ty, Loc.getRawEncoding());
+      llvm::ConstantAsMetadata *MD = llvm::ConstantAsMetadata::get(Line);
+      llvm::MDTuple *MDT = llvm::MDNode::get(getLLVMContext(), {MD});
+      CI->setMetadata("srcloc", MDT);
+    }
+
   // 4. Finish the call.
 
   // If the call doesn't return, finish the basic block and clear the
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ clang/include/clang/Basic/DiagnosticGroups.td
@@ -1202,6 +1202,7 @@
 def BackendOptimizationRemarkMissed : DiagGroup<"pass-missed">;
 def BackendOptimizationRemarkAnalysis : DiagGroup<"pass-analysis">;
 def BackendOptimizationFailure : DiagGroup<"pass-failed">;
+def BackendUserDiagnostic : DiagGroup<"user-diagnostic">;
 
 // Instrumentation based profiling warnings.
 def ProfileInstrMissing : DiagGroup<"profile-instr-missing">;
Index: clang/include/clang/Basic/DiagnosticFrontendKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -72,6 +72,11 @@
 def err_fe_backend_unsupported : Error<"%0">, BackendInfo;
 def warn_fe_backend_unsupported : Warning<"%0">, BackendInfo;
 
+def err_fe_backend_user_diagnostic :
+  Error<"call to %0 declared with attribute error: %1">, BackendInfo;
+def warn_fe_backend_user_diagnostic :
+  Warning<"call to %0 declared with attribute warning: %1">, BackendInfo, InGroup<BackendUserDiagnostic>;
+
 def err_fe_invalid_code_complete_file : Error<
     "cannot locate code-completion file %0">, DefaultFatal;
 def err_fe_dependency_file_requires_MT : Error<
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -6015,3 +6015,24 @@
   - ``enforce_tcb_leaf(Name)`` indicates that this function is a part of the TCB named ``Name``
   }];
 }
+
+def ErrorAttrDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+    The ``error`` function attribute can be used to specify a custom diagnostic
+    to be emitted when a call to such a function is not eliminated via
+    optimizations. This can be used to create compile time assertions that
+    depend on optimizations, while providing diagnostics pointing to precise
+    locations of the call site in the source.
+  }];
+}
+def WarningAttrDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+    The ``warning`` function attribute can be used to specify a custom
+    diagnostic to be emitted when a call to such a function is not eliminated
+    via optimizations. This can be used to create compile time assertions that
+    depend on optimizations, while providing diagnostics pointing to precise
+    locations of the call site in the source.
+  }];
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -3812,3 +3812,17 @@
   let Documentation = [EnforceTCBLeafDocs];
   bit InheritEvenIfAlreadyPresent = 1;
 }
+
+def Error : Attr {
+  let Spellings = [GCC<"error">];
+  let Args = [StringArgument<"UserDiagnostic">];
+  let Subjects = SubjectList<[Function], ErrorDiag>;
+  let Documentation = [ErrorAttrDocs];
+}
+
+def Warning : Attr {
+  let Spellings = [GCC<"warning">];
+  let Args = [StringArgument<"UserDiagnostic">];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [WarningAttrDocs];
+}
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -122,7 +122,8 @@
 C Language Changes in Clang
 ---------------------------
 
-- ...
+- Support for ``__attribute__((error("")))`` and
+  ``__attribute__((warning("")))`` function attributes have been added.
 
 C++ Language Changes in Clang
 -----------------------------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to