This revision was automatically updated to reflect the committed changes. Closed by commit rG846e562dcc6a: [Clang] add support for error+warning fn attrs (authored by nickdesaulniers).
Changed prior to commit: https://reviews.llvm.org/D106030?vs=366717&id=368678#toc Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D106030/new/ 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/include/clang/Sema/Sema.h clang/lib/CodeGen/CGCall.cpp clang/lib/CodeGen/CodeGenAction.cpp clang/lib/CodeGen/CodeGenModule.cpp clang/lib/Sema/SemaDecl.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/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/GlobalISel/IRTranslator.cpp llvm/lib/CodeGen/SelectionDAG/FastISel.cpp llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp llvm/lib/IR/DiagnosticInfo.cpp llvm/test/CodeGen/X86/attr-dontcall.ll llvm/test/ThinLTO/X86/dontcall.ll
Index: llvm/test/ThinLTO/X86/dontcall.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/dontcall.ll @@ -0,0 +1,33 @@ +; RUN: split-file %s %t +; RUN: opt -module-summary %t/a.s -o %t/a.bc +; RUN: opt -module-summary %t/b.s -o %t/b.bc +; RUN: not llvm-lto2 run %t/a.bc %t/b.bc -o %t/out -save-temps 2>&1 \ +; RUN: -r=%t/a.bc,callee,px \ +; RUN: -r=%t/b.bc,callee,x \ +; RUN: -r=%t/b.bc,caller,px + +; TODO: As part of LTO, we check that types match, but *we don't yet check that +; attributes match!!! What should happen if we remove "dontcall" from the +; definition or declaration of @callee? + +; CHECK: call to callee marked "dontcall" + +;--- a.s +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i32 @callee() "dontcall" noinline { + ret i32 42 +} + +;--- b.s +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare i32 @callee() "dontcall" + +define i32 @caller() { +entry: + %0 = call i32 @callee() + ret i32 %0 +} Index: llvm/test/CodeGen/X86/attr-dontcall.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/attr-dontcall.ll @@ -0,0 +1,11 @@ +; RUN: not llc -global-isel=0 -fast-isel=0 -stop-after=finalize-isel %s 2>&1 | FileCheck %s +; RUN: not llc -global-isel=0 -fast-isel=1 -stop-after=finalize-isel %s 2>&1 | FileCheck %s +; RUN: not llc -global-isel=1 -fast-isel=0 -stop-after=irtranslator %s 2>&1 | FileCheck %s + +declare void @foo() "dontcall" +define void @bar() { + call void @foo() + ret void +} + +; CHECK: error: call to foo marked "dontcall" 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 DiagnosticInfoDontCall::print(DiagnosticPrinter &DP) const { + DP << "call to " << getFunctionName() << " marked \"dontcall\""; +} Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -69,6 +69,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/InlineAsm.h" @@ -7952,6 +7953,15 @@ } if (Function *F = I.getCalledFunction()) { + if (F->hasFnAttribute("dontcall")) { + unsigned LocCookie = 0; + if (MDNode *MD = I.getMetadata("srcloc")) + LocCookie = + mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue(); + DiagnosticInfoDontCall D(F->getName(), LocCookie); + DAG.getContext()->diagnose(D); + } + if (F->isDeclaration()) { // Is this an LLVM intrinsic or a target-specific intrinsic? unsigned IID = F->getIntrinsicID(); Index: llvm/lib/CodeGen/SelectionDAG/FastISel.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -75,6 +75,7 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/GlobalValue.h" @@ -1151,6 +1152,16 @@ CLI.setCallee(RetTy, FuncTy, CI->getCalledOperand(), std::move(Args), *CI) .setTailCall(IsTailCall); + if (const Function *F = CI->getCalledFunction()) + if (F->hasFnAttribute("dontcall")) { + unsigned LocCookie = 0; + if (MDNode *MD = CI->getMetadata("srcloc")) + LocCookie = + mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue(); + DiagnosticInfoDontCall D(F->getName(), LocCookie); + F->getContext().diagnose(D); + } + return lowerCallTo(CLI); } Index: llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -47,6 +47,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" #include "llvm/IR/GetElementPtrTypeIterator.h" #include "llvm/IR/InlineAsm.h" @@ -2333,6 +2334,15 @@ if (CI.isInlineAsm()) return translateInlineAsm(CI, MIRBuilder); + if (F && F->hasFnAttribute("dontcall")) { + unsigned LocCookie = 0; + if (MDNode *MD = CI.getMetadata("srcloc")) + LocCookie = + mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue(); + DiagnosticInfoDontCall D(F->getName(), LocCookie); + F->getContext().diagnose(D); + } + Intrinsic::ID ID = Intrinsic::not_intrinsic; if (F && F->isIntrinsic()) { ID = F->getIntrinsicID(); 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_DontCall, DK_FirstPluginKind // Must be last value to work with // getNextAvailablePluginDiagnosticKind }; @@ -1070,6 +1071,22 @@ } }; +class DiagnosticInfoDontCall : public DiagnosticInfo { + StringRef CalleeName; + unsigned LocCookie; + +public: + DiagnosticInfoDontCall(StringRef CalleeName, unsigned LocCookie) + : DiagnosticInfo(DK_DontCall, DS_Error), CalleeName(CalleeName), + LocCookie(LocCookie) {} + 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_DontCall; + } +}; + } // end namespace llvm #endif // LLVM_IR_DIAGNOSTICINFO_H Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -1594,7 +1594,12 @@ ``disable_sanitizer_instrumentation`` disables all kinds of instrumentation, taking precedence over the ``sanitize_<name>`` attributes and other compiler flags. - +``"dontcall"`` + This attribute denotes that a diagnostic should be emitted 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. ``"frame-pointer"`` This attribute tells the code generator whether the function should keep the frame pointer. The code generator may emit the frame pointer Index: clang/test/Sema/attr-warning.c =================================================================== --- /dev/null +++ clang/test/Sema/attr-warning.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +#if !__has_attribute(warning) +#warning "warning attribute missing" +#endif + +__attribute__((warning("don't call me!"))) int good0(void); + +__attribute__((warning)) // expected-error {{'warning' attribute takes one argument}} +int +bad0(void); + +int bad1(__attribute__((warning("bad1"))) int param); // expected-error {{'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); + +__attribute__((warning("foo"), warning("foo"))) int good1(void); +__attribute__((warning("foo"))) int good1(void); +__attribute__((warning("foo"))) int good1(void) {} + +__attribute__((warning("foo"), error("foo"))) // expected-error {{'error' and 'warning' attributes are not compatible}} +int +bad4(void); +// expected-note@-3 {{conflicting attribute is here}} + +/* + * Note: we differ from GCC here; rather than support redeclarations that add + * or remove this fn attr, we diagnose such differences. + */ + +void foo(void); // expected-note {{previous declaration is here}} +__attribute__((warning("oh no foo"))) void foo(void); // expected-error {{'warning' attribute does not appear on the first declaration}} Index: clang/test/Sema/attr-error.c =================================================================== --- /dev/null +++ clang/test/Sema/attr-error.c @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +#if !__has_attribute(error) +#error "error attribute missing" +#endif + +__attribute__((error("don't call me!"))) int good0(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); + +__attribute__((error("foo"), error("foo"))) int good1(void); +__attribute__((error("foo"))) int good1(void); +__attribute__((error("foo"))) int good1(void) {} + +__attribute__((error("foo"), warning("foo"))) // expected-error {{'warning' and 'error' attributes are not compatible}} +int +bad4(void); +// expected-note@-3 {{conflicting attribute is here}} + +__attribute__((error("foo"))) int bad5(void); // expected-note {{conflicting attribute is here}} +__attribute__((warning("foo"))) int bad5(void); // expected-error {{'error' and 'warning' attributes are not compatible}} + +/* + * Note: we differ from GCC here; rather than support redeclarations that add + * or remove this fn attr, we diagnose such differences. + */ + +void foo(void); // expected-note {{previous declaration is here}} +__attribute__((error("oh no foo"))) void foo(void); // expected-error {{'error' attribute does not appear on the first declaration}} 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 @@ -63,6 +63,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) Index: clang/test/Frontend/backend-attribute-error-warning.c =================================================================== --- /dev/null +++ clang/test/Frontend/backend-attribute-error-warning.c @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -verify=expected,enabled -emit-codegen-only %s +// RUN: %clang_cc1 -verify=expected,enabled -emit-codegen-only %s -x c++ +// RUN: %clang_cc1 -verify -emit-codegen-only -Wno-attribute-warning %s +// RUN: %clang_cc1 -verify -emit-codegen-only -Wno-attribute-warning %s -x c++ + +__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); + +__attribute__((error("one"), error("two"))) // expected-warning {{attribute 'error' is already applied with different arguments}} +void // expected-note@-1 {{previous attribute is here}} +duplicate_errors(void); + +__attribute__((warning("one"), warning("two"))) // expected-warning {{attribute 'warning' is already applied with different arguments}} +void // expected-note@-1 {{previous attribute is here}} +duplicate_warnings(void); + +void baz(void) { + foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}} + if (x()) + bar(); // expected-error {{call to 'bar' declared with 'error' attribute: oh no bar}} + + quux(); // enabled-warning {{call to 'quux' declared with 'warning' attribute: oh no quux}} + __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455' declared with 'error' attribute: demangle me}} + duplicate_errors(); // expected-error {{call to 'duplicate_errors' declared with 'error' attribute: two}} + duplicate_warnings(); // enabled-warning {{call to 'duplicate_warnings' declared with 'warning' attribute: two}} +} + +#ifdef __cplusplus +template <typename T> +__attribute__((error("demangle me, too"))) +T +nocall(T t); + +struct Widget { + __attribute__((warning("don't call me!"))) + operator int() { return 42; } +}; + +void baz_cpp(void) { + foo(); // expected-error {{call to 'foo' declared with 'error' attribute: oh no foo}} + if (x()) + bar(); // expected-error {{call to 'bar' declared with 'error' attribute: oh no bar}} + quux(); // enabled-warning {{call to 'quux' declared with 'warning' attribute: oh no quux}} + + // Test that we demangle correctly in the diagnostic for C++. + __compiletime_assert_455(); // expected-error {{call to '__compiletime_assert_455' declared with 'error' attribute: demangle me}} + nocall<int>(42); // expected-error {{call to 'nocall<int>' declared with 'error' attribute: demangle me, too}} + + Widget W; + int w = W; // enabled-warning {{'operator int' declared with 'warning' attribute: don't call me!}} +} +#endif Index: clang/test/Frontend/backend-attribute-error-warning-optimize.c =================================================================== --- /dev/null +++ clang/test/Frontend/backend-attribute-error-warning-optimize.c @@ -0,0 +1,22 @@ +// 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 'error' attribute: oh no foo}} + if (x()) + bar(); +} + +// FIXME: indirect call detection not yet supported. +void (*quux)(void); + +void indirect(void) { + quux = foo; + quux(); +} 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]] = {{{.*}}"dontcall" +// 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]] = {{{.*}}"dontcall" +// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}} Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -947,6 +947,14 @@ D->addAttr(::new (S.Context) EnableIfAttr(S.Context, AL, Cond, Msg)); } +static void handleErrorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + StringRef NewUserDiagnostic; + if (!S.checkStringLiteralArgumentAttr(AL, 0, NewUserDiagnostic)) + return; + if (ErrorAttr *EA = S.mergeErrorAttr(D, AL, NewUserDiagnostic)) + D->addAttr(EA); +} + namespace { /// Determines if a given Expr references any of the given function's /// ParmVarDecls, or the function's implicit `this` parameter (if applicable). @@ -3458,6 +3466,29 @@ D->addAttr(::new (S.Context) InitPriorityAttr(S.Context, AL, prioritynum)); } +ErrorAttr *Sema::mergeErrorAttr(Decl *D, const AttributeCommonInfo &CI, + StringRef NewUserDiagnostic) { + if (const auto *EA = D->getAttr<ErrorAttr>()) { + std::string NewAttr = CI.getNormalizedFullName(); + assert((NewAttr == "error" || NewAttr == "warning") && + "unexpected normalized full name"); + bool Match = (EA->isError() && NewAttr == "error") || + (EA->isWarning() && NewAttr == "warning"); + if (!Match) { + Diag(EA->getLocation(), diag::err_attributes_are_not_compatible) + << CI << EA; + Diag(CI.getLoc(), diag::note_conflicting_attribute); + return nullptr; + } + if (EA->getUserDiagnostic() != NewUserDiagnostic) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << EA; + Diag(EA->getLoc(), diag::note_previous_attribute); + } + D->dropAttr<ErrorAttr>(); + } + return ::new (Context) ErrorAttr(Context, CI, NewUserDiagnostic); +} + FormatAttr *Sema::mergeFormatAttr(Decl *D, const AttributeCommonInfo &CI, IdentifierInfo *Format, int FormatIdx, int FirstArg) { @@ -7979,6 +8010,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; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -2628,6 +2628,8 @@ NewAttr = S.mergeDLLImportAttr(D, *ImportA); else if (const auto *ExportA = dyn_cast<DLLExportAttr>(Attr)) NewAttr = S.mergeDLLExportAttr(D, *ExportA); + else if (const auto *EA = dyn_cast<ErrorAttr>(Attr)) + NewAttr = S.mergeErrorAttr(D, *EA, EA->getUserDiagnostic()); else if (const auto *FA = dyn_cast<FormatAttr>(Attr)) NewAttr = S.mergeFormatAttr(D, *FA, FA->getType(), FA->getFormatIdx(), FA->getFirstArg()); @@ -3363,6 +3365,14 @@ New->dropAttr<InternalLinkageAttr>(); } + if (auto *EA = New->getAttr<ErrorAttr>()) { + if (!Old->hasAttr<ErrorAttr>()) { + Diag(EA->getLocation(), diag::err_attribute_missing_on_first_decl) << EA; + Diag(Old->getLocation(), diag::note_previous_declaration); + New->dropAttr<ErrorAttr>(); + } + } + if (CheckRedeclarationModuleOwnership(New, Old)) return true; Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -2136,6 +2136,9 @@ else if (const auto *SA = FD->getAttr<SectionAttr>()) F->setSection(SA->getName()); + if (FD->hasAttr<ErrorAttr>()) + F->addFnAttr("dontcall"); + // If we plan on emitting this inline builtin, we can't treat it as a builtin. if (FD->isInlineBuiltinDeclaration()) { const FunctionDecl *FDBody; 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 DontCallDiagHandler(const DiagnosticInfoDontCall &D); }; void BackendConsumer::anchor() {} @@ -758,6 +759,33 @@ EmitOptimizationMessage(D, diag::warn_fe_backend_optimization_failure); } +void BackendConsumer::DontCallDiagHandler(const DiagnosticInfoDontCall &D) { + if (const Decl *DE = Gen->GetDeclForMangledName(D.getFunctionName())) + if (const auto *FD = dyn_cast<FunctionDecl>(DE)) { + assert(FD->hasAttr<ErrorAttr>() && + "expected error or warning function attribute"); + + if (const auto *EA = FD->getAttr<ErrorAttr>()) { + assert((EA->isError() || EA->isWarning()) && + "ErrorAttr neither error or warning"); + + SourceLocation LocCookie = + SourceLocation::getFromRawEncoding(D.getLocCookie()); + + // FIXME: we can't yet diagnose indirect calls. When/if we can, we + // should instead assert that LocCookie.isValid(). + if (!LocCookie.isValid()) + return; + + Diags.Report(LocCookie, EA->isError() + ? diag::err_fe_backend_error_attr + : diag::warn_fe_backend_warning_attr) + << FD << EA->getUserDiagnostic(); + } + } + // TODO: assert if DE or FD were nullptr? +} + /// This function is invoked when the backend needs /// to report something to the user. void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) { @@ -829,6 +857,9 @@ case llvm::DK_Unsupported: UnsupportedDiagHandler(cast<DiagnosticInfoUnsupported>(DI)); return; + case llvm::DK_DontCall: + DontCallDiagHandler(cast<DiagnosticInfoDontCall>(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 @@ -5317,6 +5317,15 @@ TargetDecl->hasAttr<MSAllocatorAttr>()) getDebugInfo()->addHeapAllocSiteMetadata(CI, RetTy->getPointeeType(), Loc); + // Add metadata if calling an __attribute__((error(""))) or warning fn. + if (TargetDecl && TargetDecl->hasAttr<ErrorAttr>()) { + 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/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3338,6 +3338,8 @@ const AttributeCommonInfo &CI, bool BestCase, MSInheritanceModel Model); + ErrorAttr *mergeErrorAttr(Decl *D, const AttributeCommonInfo &CI, + StringRef NewUserDiagnostic); FormatAttr *mergeFormatAttr(Decl *D, const AttributeCommonInfo &CI, IdentifierInfo *Format, int FormatIdx, int FirstArg); Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -1222,6 +1222,7 @@ def BackendOptimizationRemarkMissed : DiagGroup<"pass-missed">; def BackendOptimizationRemarkAnalysis : DiagGroup<"pass-analysis">; def BackendOptimizationFailure : DiagGroup<"pass-failed">; +def BackendWarningAttributes : DiagGroup<"attribute-warning">; // 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 @@ -73,6 +73,12 @@ def err_fe_backend_unsupported : Error<"%0">, BackendInfo; def warn_fe_backend_unsupported : Warning<"%0">, BackendInfo; +def err_fe_backend_error_attr : + Error<"call to %0 declared with 'error' attribute: %1">, BackendInfo; +def warn_fe_backend_warning_attr : + Warning<"call to %0 declared with 'warning' attribute: %1">, BackendInfo, + InGroup<BackendWarningAttributes>; + 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 @@ -6071,3 +6071,29 @@ - ``enforce_tcb_leaf(Name)`` indicates that this function is a part of the TCB named ``Name`` }]; } + +def ErrorAttrDocs : Documentation { + let Category = DocCatFunction; + let Heading = "error, warning"; + let Content = [{ +The ``error`` and ``warning`` function attributes 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. + +.. code-block:: c++ + + __attribute__((warning("oh no"))) void dontcall(); + void foo() { + if (someCompileTimeAssertionThatsTrue) + dontcall(); // Warning + + dontcall(); // Warning + + if (someCompileTimeAssertionThatsFalse) + dontcall(); // No Warning + sizeof(dontcall()); // No Warning + } + }]; +} Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -3838,3 +3838,12 @@ let Documentation = [EnforceTCBLeafDocs]; bit InheritEvenIfAlreadyPresent = 1; } + +def Error : InheritableAttr { + let Spellings = [GCC<"error">, GCC<"warning">]; + let Accessors = [Accessor<"isError", [GCC<"error">]>, + Accessor<"isWarning", [GCC<"warning">]>]; + let Args = [StringArgument<"UserDiagnostic">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [ErrorAttrDocs]; +} Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -103,6 +103,8 @@ - Wide multi-characters literals such as ``L'ab'`` that would previously be interpreted as ``L'b'`` are now ill-formed in all language modes. The motivation for this change is outlined in `P2362 <wg21.link/P2362>`_. +- 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