Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Clang] add support for error+warning fn attrs
Add support for the GNU C style __attribute__((error(""))) and __attribute__((warning(""))). These attributes are meant to be put on declarations 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). This is also similar to diagnose_if function attribute, but can diagnose after optimizations have been run. 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. Users instead may be able use inline asm .err directives. 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, "dontcall" (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 during 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. Link: https://bugs.llvm.org/show_bug.cgi?id=16428 Link: ClangBuiltLinux/linux#1173 Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D106030
- Loading branch information
1 parent
cc4bfd7
commit 846e562
Showing
26 changed files
with
418 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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]+}}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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]+}}} |
22 changes: 22 additions & 0 deletions
22
clang/test/Frontend/backend-attribute-error-warning-optimize.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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}} |
Oops, something went wrong.